做影视网站赚钱,wordpress页面不能访问,个人网站首页设计,网站建设竞价托管服务[MSPM0开发]之七 MSPM0G3507 UART串口收发、printf重定向#xff0c;循环缓冲解析自定义协议等 一、 MSPM0G3507 UART概述二、 MSPM0G3507 UART结构及收发工作过程三、 MSPM0G3507 UART例程概述四、MSPM0G3507 配置五、 MSPM0G3507 UART 串口发送及printf重定向5.1 MSPM0G3507… [MSPM0开发]之七 MSPM0G3507 UART串口收发、printf重定向循环缓冲解析自定义协议等 一、 MSPM0G3507 UART概述二、 MSPM0G3507 UART结构及收发工作过程三、 MSPM0G3507 UART例程概述四、MSPM0G3507 配置五、 MSPM0G3507 UART 串口发送及printf重定向5.1 MSPM0G3507 UART 串口发送5.2 MSPM0G3507 UART 串口中断接收测试5.3 MSPM0G3507 printf重定向 六 MSPM0G3507 UART中断接收及循环缓冲实现自定义协议数据解析七、 遗留问题----fputc重定义八 续上了 一、 MSPM0G3507 UART概述
UART 控制器包含以下特性 • 完全可编程串行接口 – 5、 6、 7 或 8 个数据位 – 偶校验、奇校验、固定校验或无奇偶校验位生成与检测 – 可产生 1 或 2 个停止位 – 最低有效位或最高有效位数据最先传送和接收 – 断线检测 – 输入信号上的干扰滤波器 – 可编程波特率生成 过采样率为 16、 8 或 3 倍 • 独立的发送和接收 4 深度 FIFO 可减少 CPU 中断服务负载 • 支持 DMA 数据传输 • 提供标准的基于 FIFO 深度的中断以及发送结束中断 • 在包括停止和待机模式在内的所有低功耗模式下均有效 • 在低功耗模式下运行时 支持通过异步快速时钟请求在检测到起始位时唤醒 SYSOSC 在 FCL 模式下使用 SYSOSC 时 支持高达 19200B 的速率 精度 1% • 支持环回模式运行 • 支持硬件流控制 • 支持 9 位多点配置 • 支持的协议 – 本地互连网络 (LIN) 支持 – DALI –IrDA – ISO7816 Smart Card – RS485 – 曼彻斯特编码 – 空闲线多处理器 MSPM0G350x 包含四个 UART 接口一个支持 LIN、IrDA、DALI、Smart Card、Manchester三个支持待机模式下的低功耗运行
二、 MSPM0G3507 UART结构及收发工作过程 发送数据 当需要进行发送时 先将数据写入发送 FIFO。 如果启用 UART 则会根据 UARTx.LCRH 寄存器中指示的参数开始发送数据帧。 UART 模块会持续发送数据 直到发送 FIFO 中没有可发数据为止。 数据一经写入发送 FIFO 即 如果 FIFO 不为空 UARTx.STAT 寄存器中的 BUSY 位即会生效 并在数据发送期间一直保持有效。 仅当发送 FIFO 为空 且最后一个字符 包括停止位 已发送到移位寄存器时 BUSY 位才为负。即使无法再启用UART UART 也可以指示它正忙。生成 BREAK 信号期间 也会设置 BUSY。
接收数据 当接收器空闲 RX 信号持续为 1 且数据输入变为低电平 已接收到开始位 时 接收计数器开始运行 并根据UARTx.CTL0 寄存器中 HSE 位的过采样设置 在不同的周期对数据进行采样。 如果 RX 信号在基于过采样设置的特定数量的周期后仍为低电平 则开始位有效并被识别。 检测到有效的开始位后 将根据数据字符的编程长度对连续的数据位进行采样。之后将捕获并校验奇偶校验位 如果使能了奇偶校验 。 数据长度和奇偶校验都在UART.LCRH 寄存器中定义。 如果 RXD 信号为高电平 则确认有效停止位 否则发生成帧错误。 若成功接收到一帧数据 则数据和与之相关的错误标志都将保存到接收 FIFO 中。
UART状态标志  中DL_GPIO_initPeripheralOutputFunction(GPIO_UART_0_IOMUX_TX, GPIO_UART_0_IOMUX_TX_FUNC);DL_GPIO_initPeripheralInputFunction(GPIO_UART_0_IOMUX_RX, GPIO_UART_0_IOMUX_RX_FUNC);// UART硬件初始化
static const DL_UART_Main_ClockConfig gUART_0ClockConfig {.clockSel DL_UART_MAIN_CLOCK_BUSCLK,.divideRatio DL_UART_MAIN_CLOCK_DIVIDE_RATIO_1
};static const DL_UART_Main_Config gUART_0Config {.mode DL_UART_MAIN_MODE_NORMAL,.direction DL_UART_MAIN_DIRECTION_TX_RX,.flowControl DL_UART_MAIN_FLOW_CONTROL_NONE,.parity DL_UART_MAIN_PARITY_NONE,.wordLength DL_UART_MAIN_WORD_LENGTH_8_BITS,.stopBits DL_UART_MAIN_STOP_BITS_ONE
};/*** brief 初始化UART_0模块* * 该函数负责配置和初始化UART_0模块包括设置时钟配置、初始化配置、* 过采样率、波特率除数以及中断和FIFO的相关配置。通过这些配置可以* 使UART_0模块以特定的波特率进行通信并具备基本的中断和数据处理能力。*/
SYSCONFIG_WEAK void SYSCFG_DL_UART_0_init(void)
{// 设置UART_0的时钟配置DL_UART_Main_setClockConfig(UART_0_INST, (DL_UART_Main_ClockConfig *) gUART_0ClockConfig);// 初始化UART_0配置DL_UART_Main_init(UART_0_INST, (DL_UART_Main_Config *) gUART_0Config);// 设置UART_0的过采样率为16倍DL_UART_Main_setOversampling(UART_0_INST, DL_UART_OVERSAMPLING_RATE_16X);// 设置UART_0的波特率除数以达到9600的波特率DL_UART_Main_setBaudRateDivisor(UART_0_INST, UART_0_IBRD_32_MHZ_9600_BAUD, UART_0_FBRD_32_MHZ_9600_BAUD);/* 中断配置 */// 使能UART_0的接收中断DL_UART_Main_enableInterrupt(UART_0_INST, DL_UART_MAIN_INTERRUPT_RX);/* FIFO配置 */// 使能UART_0的FIFO功能DL_UART_Main_enableFIFOs(UART_0_INST);// 设置UART_0接收FIFO的触发阈值为半满DL_UART_Main_setRXFIFOThreshold(UART_0_INST, DL_UART_RX_FIFO_LEVEL_1_2_FULL);// 设置UART_0发送FIFO的触发阈值为四分之一空DL_UART_Main_setTXFIFOThreshold(UART_0_INST, DL_UART_TX_FIFO_LEVEL_1_4_EMPTY);// 使能UART_0模块DL_UART_Main_enable(UART_0_INST);
} main.c初始化调用及串口发送字符串
int main(void) {SYSCFG_DL_init();NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);NVIC_EnableIRQ(UART_0_INST_INT_IRQN);UART_printf(helloWorld\r\n);while (1) {delay_cycles(32000000);UART_printf(helloWorld\r\n);}
}其中 UART_printf函数定义如下
void UART_printf(const char *format, ...) {uint32_t length;va_list args;uint32_t i;char TxBuffer[128] {0};va_start(args, format);length vsnprintf((char *)TxBuffer, sizeof(TxBuffer), (char *)format, args);va_end(args);for (i 0; i length; i) {DL_UART_Main_transmitDataBlocking(UART_0_INST, TxBuffer[i]);}
}提供另一个仅支持发送字符串函数
void sendString(const char *str) {while (*str ! \0) {DL_UART_Main_transmitDataBlocking(UART_0_INST, (uint8_t)*str);str;}
}5.2 MSPM0G3507 UART 串口中断接收测试
上面的初始化代码中包含串口的发送和接收以及开启了串口中断。 在5.1基础上增加串口接收中断服务函数即可 下面的中断服务函数中实现 当串口接收到数据后翻转LED并将接收到的通过串口发送
void UART_0_INST_IRQHandler(void) {switch (DL_UART_Main_getPendingInterrupt(UART_0_INST)) {/*! UART interrupt index for UART receive */case DL_UART_MAIN_IIDX_RX: DL_GPIO_togglePins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);gEchoData DL_UART_Main_receiveData(UART_0_INST);DL_UART_Main_transmitData(UART_0_INST, gEchoData);break;default:break;}
}5.3 MSPM0G3507 printf重定向
在5.2基础上引用头文件
#include stdarg.h
#include stdio.h
#include string.h重定向实现3个发送函数
int fputc(int _c, FILE *_fp) {DL_UART_Main_transmitDataBlocking(UART_0_INST, _c);return _c;
}int fputs(const char *restrict s, FILE *restrict stream) {uint16_t i, len;len strlen(s);for (i 0; i len; i) {DL_UART_Main_transmitDataBlocking(UART_0_INST, s[i]);}return len;
}int puts(const char *_ptr) {int cnt fputs(_ptr, stdout);cnt fputs(\n, stdout);return cnt;
}运行结果
六 MSPM0G3507 UART中断接收及循环缓冲实现自定义协议数据解析
要求定义一个全局数组做串口接收缓存接收中断将接收到的压入缓存在main.c的while()循环中读取缓存数据并进行帧数据解析支持自定义帧头内容和长度一帧数据的长度读取一帧数据长度。 使用通义灵码生成。 cmd_queue.h
/*!* \file cmd_queue.h* \brief 串口指令队列支持多队列、多帧头、可配置帧长* \version 1.3* \date 2024.10* \copyright wo4fisher*/#ifndef _CMD_QUEUE
#define _CMD_QUEUE
#include ti_msp_dl_config.h// 队列配置参数
#define QUEUE_MAX_SIZE 100typedef uint8_t qdata;
typedef uint16_t qsize;// 接收状态结构体暂时未使用
typedef struct {uint8_t pos; // 当前接收位置const qdata *pHead; // 帧头指针uint8_t head_len; // 帧头长度uint8_t frame_len; // 总帧长含帧头
} CmdState;// 队列句柄结构体定义
typedef struct {qsize _head; // 写指针qsize _tail; // 读指针qdata _data[QUEUE_MAX_SIZE]; // 数据缓冲区
} QueueHandle;/*!* \brief 初始化一个新的队列* \param[out] q 指向队列句柄的指针*/
void queue_init(QueueHandle *q);/*!* \brief 清空指定队列* \param[in] q 队列句柄*/
void queue_reset(QueueHandle *q);/*!* \brief 将字节加入队列* \param[in] q 队列句柄* \param[in] _data 要入队的数据*/
void queue_push(QueueHandle *q, qdata _data);/*!* \brief 从队列中取出一个字节* \param[in] q 队列句柄* \param[out] _data 数据输出指针* \return 成功返回 true队列为空返回 false*/
uint8_t queue_pop(QueueHandle *q, qdata *_data);/*!* \brief 获取队列中当前有效数据个数* \param[in] q 队列句柄* \return 当前未读取的数据数量*/
qsize queue_size(QueueHandle *q);/*!* \brief 从队列中提取一条完整指令帧支持多帧头、帧长可配* \param[in] q 队列句柄* \param[out] buffer 接收缓冲区* \param[in] buf_len 缓冲区大小* \param[in] frame_header 帧头数组如 {0x3F, 0x06}* \param[in] header_len 帧头长度* \param[in] total_frame_len 完整帧长度含帧头* \return 成功接收的字节数0 表示无完整帧*/
qsize queue_find_cmd_ext(QueueHandle *q, qdata *buffer, qsize buf_len,const qdata *frame_header, uint8_t header_len,uint8_t total_frame_len);#endif // _CMD_QUEUE
cmd_queue.c
/*!* \file cmd_queue.c* \brief 串口指令队列实现支持多队列、多帧头、可配置帧长* \version 1.3* \date 2024.10* \copyright wo4fisher*/#include cmd_queue.h// 平台相关关中断保护
#define ENTER_CRITICAL()
#define LEAVE_CRITICAL()// 初始化队列
void queue_init(QueueHandle *q) { q-_head q-_tail 0; }// 清空队列
void queue_reset(QueueHandle *q) { q-_head q-_tail 0; }// 入队操作
void queue_push(QueueHandle *q, qdata _data) {qsize next_head (q-_head 1) % QUEUE_MAX_SIZE;if (next_head ! q-_tail) {q-_data[q-_head] _data;q-_head next_head;}
}// 出队操作
uint8_t queue_pop(QueueHandle *q, qdata *_data) {uint8_t success 0;if (q-_tail ! q-_head) {*_data q-_data[q-_tail];q-_tail (q-_tail 1) % QUEUE_MAX_SIZE;success 1;}return 1;
}// 获取队列大小
qsize queue_size(QueueHandle *q) {qsize size;size (q-_head QUEUE_MAX_SIZE - q-_tail) % QUEUE_MAX_SIZE;return size;
}// 提取完整指令帧支持多帧头、帧长可配
qsize queue_find_cmd_ext(QueueHandle *q, qdata *buffer, qsize buf_len,const qdata *frame_header, uint8_t header_len,uint8_t total_frame_len) {qdata _data;uint8_t rxSum 0;uint8_t received_pos 0;if (buffer NULL || buf_len total_frame_len || frame_header NULL ||header_len 0 || total_frame_len header_len) {return 0;}// 成功连续读取一帧数据// 错误直接退出直到下一次调用此函数再判断while (queue_size(q) 0) {// 没有数据退出if (!queue_pop(q, _data))break;if (received_pos header_len) { // 检测帧头if (_data ! frame_header[received_pos]) {// 检测帧头失败received_pos归零从头来received_pos 0;continue;} else {buffer[received_pos] _data;}} else { // 帧数据缓存buffer[received_pos] _data;}if (received_pos total_frame_len) {return total_frame_len;}}return 0;
}
串口中断
void UART_0_INST_IRQHandler(void) {switch (DL_UART_Main_getPendingInterrupt(UART_0_INST)) {case DL_UART_MAIN_IIDX_RX:DL_GPIO_togglePins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);gEchoData DL_UART_Main_receiveData(UART_0_INST);queue_push(q1, gEchoData);break;default:break;}
}main.c完整代码
#include bsp_tick.h
#include cmd_queue.h
#include ti_msp_dl_config.h
#include stdarg.h
#include stdio.h
#include string.hvolatile uint8_t gEchoData 0;void UART_printf(const char *format, ...) {uint32_t length;va_list args;uint32_t i;char TxBuffer[128] {0};va_start(args, format);length vsnprintf((char *)TxBuffer, sizeof(TxBuffer), (char *)format, args);va_end(args);for (i 0; i length; i) {DL_UART_Main_transmitDataBlocking(UART_0_INST, TxBuffer[i]);}
}QueueHandle q1;
const qdata my_header[] {0x3F, 0x06};
qdata buffer[10];int main(void) {SYSCFG_DL_init();systick_init(32000);NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);NVIC_EnableIRQ(UART_0_INST_INT_IRQN);// DL_SYSCTL_enableSleepOnExit();delay_ms(100);UART_printf(helloWorld\r\n);queue_init(q1);while (1) {qsize len queue_find_cmd_ext(q1, buffer, sizeof(buffer), my_header, 2, 8);if (len 0) {UART_printf(Received command: cmd[2]%d,cmd[3]%d\r\n, buffer[2],buffer[3]);}delay_ms(30);}
}void UART_0_INST_IRQHandler(void) {switch (DL_UART_Main_getPendingInterrupt(UART_0_INST)) {case DL_UART_MAIN_IIDX_RX:DL_GPIO_togglePins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);gEchoData DL_UART_Main_receiveData(UART_0_INST);queue_push(q1, gEchoData);break;default:break;}
}七、 遗留问题----fputc重定义 在五、 MSPM0G3507 UART 串口发送及printf重定向一节中实现了printf的重定向。 但是在添加了六 MSPM0G3507 UART中断接收及循环缓冲实现自定义协议数据解析一节的代码后程序编译出现个问题#10056: symbol “fputc” redefined: first defined in “./uart_echo_interrupts_standby.o”; redefined in “C:\ti\ccs2010\ccs\tools\compiler\ti-cgt-armllvm_4.0.2.LTS\lib\armv6m-ti-none-eabi/c/libc.afputc.c.obj” 大意是fputc多重定义了。 另外定义在libc.a文件中。 此库文件在别的工程中也是存在的 原本以为是因为添加循环缓冲实现自定义协议数据解析的代码才意外引入了libc.a库文件结果不是。 把循环缓冲实现自定义协议数据解析相关代码去掉后再此编译该错误就消失了可以正常使用自定义的fputc实现printf重定向。并且项目属性中是包含libc.a文件的。 理解不了!!! 八 续上了
就在写完七、 遗留问题----fputc重定义的内容为了找编译错误的图恢复程序重现错误的时候结果。。。结果它好了 现在它又能用了 汗… 不过时有丢帧的情况发生。 下图为使能FIFO接收中断增加判断接收FIFO不为空连续读取接收数据。 或者取消FIFO的使用结果都是一样不同概率会丢帧丢帧的原因是发送8个字节仅接受7个字节未接收完全。
最后附上main.c最后的内容
#include bsp_tick.h
#include cmd_queue.h
#include ti_msp_dl_config.h
#include stdarg.h
#include stdio.h
#include string.hvolatile uint8_t gEchoData 0;void sendString(const char *str) {while (*str ! \0) {DL_UART_Main_transmitDataBlocking(UART_0_INST, (uint8_t)*str);str;}
}void UART_printf(const char *format, ...) {uint32_t length;va_list args;uint32_t i;char TxBuffer[128] {0};va_start(args, format);length vsnprintf((char *)TxBuffer, sizeof(TxBuffer), (char *)format, args);va_end(args);for (i 0; i length; i) {DL_UART_Main_transmitDataBlocking(UART_0_INST, TxBuffer[i]);}
}QueueHandle q1;
const qdata my_header[] {0x3F, 0x06};
qdata buffer[10];int main(void) {SYSCFG_DL_init();systick_init(32000);NVIC_ClearPendingIRQ(UART_0_INST_INT_IRQN);NVIC_EnableIRQ(UART_0_INST_INT_IRQN);// DL_SYSCTL_enableSleepOnExit();delay_ms(100);UART_printf(helloWorld\r\n);queue_init(q1);while (1) {qsize len queue_find_cmd_ext(q1, buffer, sizeof(buffer), my_header, 2, 8);if (len 0) {printf(Received command: cmd[2]%d,cmd[3]%d\r\n, buffer[2], buffer[3]);}delay_ms(30);}
}void UART_0_INST_IRQHandler(void) {switch (DL_UART_Main_getPendingInterrupt(UART_0_INST)) {case DL_UART_MAIN_IIDX_RX:DL_GPIO_togglePins(GPIO_LEDS_PORT, GPIO_LEDS_USER_LED_1_PIN);while (!DL_UART_Main_isRXFIFOEmpty(UART_0_INST)) {gEchoData DL_UART_Main_receiveData(UART_0_INST);queue_push(q1, gEchoData);}break;default:break;}
}int fputc(int _c, FILE *_fp) {DL_UART_Main_transmitDataBlocking(UART_0_INST, _c);return _c;
}int fputs(const char *restrict s, FILE *restrict stream) {uint16_t i, len;len strlen(s);for (i 0; i len; i) {DL_UART_Main_transmitDataBlocking(UART_0_INST, s[i]);}return len;
}int puts(const char *_ptr) {int cnt fputs(_ptr, stdout);cnt fputs(\n, stdout);return cnt;
}