当前位置: 首页 > news >正文

[MSPM0开发]之七 MSPM0G3507 UART串口收发、printf重定向,循环缓冲解析自定义协议等

[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状态标志:
![状态标志](https://i-blog.csdnimg.cn/direct/15e5553a5c6d470c9dc3e3f6c944b560.png组态标志
天猛星电路原理图:
图中使用了UART0,映射在了PA10、PA11两个引脚上。并且通过调试下载处的排针和USB typeC接口引出。
原理图

三、 MSPM0G3507 UART例程概述

例程概述

四、MSPM0G3507 配置

配置基于uart_echo_interrupts_standby例程修改
时钟配置波特率等参数
高级参数配置

五、 MSPM0G3507 UART 串口发送及printf重定向

5.1 MSPM0G3507 UART 串口发送

uart初始化:

//GPIO重映射部分 void SYSCFG_DL_GPIO_init(void) 中: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.h>volatile 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.a<fputc.c.obj>”
大意是fputc多重定义了。 另外定义在libc.a文件中。
此库文件在别的工程中也是存在的

证据1

原本以为是因为添加循环缓冲实现自定义协议数据解析的代码才意外引入了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.h>volatile 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;
}
http://www.xdnf.cn/news/1024507.html

相关文章:

  • 前端八股文-react篇
  • Ubuntu 与 Windows 实现文件夹共享
  • 前缀和:leetcode974--和可被K整除的子数组
  • 序列化问题和网络字节序
  • 商城系统微服务化改造:三大难点与实战解决方案
  • P5 QT项目----会学网络调试助手服务端(5.1)
  • 一文读懂:晶振不同等级的差异及对应最佳应用场景
  • 关于 WASM: WASM + JS 混合逆向流程
  • ffmpeg rtmp推流源码分析
  • Java的学习心得
  • 大型螺旋桨三维扫描尺寸检测逆向建模-中科米堆
  • 为什么传统 Bug 追踪系统正在被抛弃?
  • 一个完整的LSTM风光发电预测与并网优化方案,包含数据处理、模型构建、训练优化、预测应用及系统集成实现细节
  • frida对qt5(32位)实现简单HOOK
  • java中的类与对象
  • 文件系统1(Linux中)
  • 纪念2024.10-2025.6飞牛os的6次系统崩溃
  • 大矩阵可以分解为低秩矩阵的乘积
  • 什么是音频?
  • Git 分支管理规范
  • 【Python训练营打卡】day52 @浙大疏锦行
  • 《并查集》题集
  • AndroidManifest里面的lable标签
  • Flutter:加减乘除,科学计数法转换
  • 《第二章-内功筑基》 C++修炼生涯笔记(基础篇)数据类型与运算符
  • 前端给一行文字不设置宽度 ,不拆分 ,又能让某几个字在视觉下方居中显示
  • LeetCode 2529.正整数和负整数的最大计数
  • Appium + Java 测试全流程
  • Spring boot 的 maven 打包过程
  • Fiori 初学记录----怎么调用后端系统odata 服务实现简单的CURD