滄州網(wǎng)站建設(shè)制作設(shè)計(jì)優(yōu)化百度瀏覽器電腦版
[MSPM0開發(fā)]之七 MSPM0G3507 UART串口收發(fā)、printf重定向,循環(huán)緩沖解析自定義協(xié)議等
- 一、 MSPM0G3507 UART概述
- 二、 MSPM0G3507 UART結(jié)構(gòu)及收發(fā)工作過程
- 三、 MSPM0G3507 UART例程概述
- 四、MSPM0G3507 配置
- 五、 MSPM0G3507 UART 串口發(fā)送及printf重定向
- 5.1 MSPM0G3507 UART 串口發(fā)送
- 5.2 MSPM0G3507 UART 串口中斷接收測試
- 5.3 MSPM0G3507 printf重定向
- 六 MSPM0G3507 UART中斷接收及循環(huán)緩沖實(shí)現(xiàn)自定義協(xié)議數(shù)據(jù)解析
- 七、 遺留問題----fputc重定義
- 八 續(xù)上了
一、 MSPM0G3507 UART概述
UART 控制器包含以下特性:
? 完全可編程串行接口
– 5、 6、 7 或== 8 個(gè)數(shù)據(jù)位==
– 偶校驗(yàn)、奇校驗(yàn)、固定校驗(yàn)或無奇偶校驗(yàn)位生成與檢測
– 可產(chǎn)生 1 或 2 個(gè)停止位
– 最低有效位或最高有效位數(shù)據(jù)最先傳送和接收
– 斷線檢測
– 輸入信號上的干擾濾波器
– 可編程波特率生成, 過采樣率為 16、 8 或 3 倍
? 獨(dú)立的發(fā)送和接收 4 深度 FIFO 可減少 CPU 中斷服務(wù)負(fù)載
? 支持 DMA 數(shù)據(jù)傳輸
? 提供標(biāo)準(zhǔn)的基于 FIFO 深度的中斷以及發(fā)送結(jié)束中斷
? 在包括停止和待機(jī)模式在內(nèi)的所有低功耗模式下均有效
? 在低功耗模式下運(yùn)行時(shí), 支持通過異步快速時(shí)鐘請求在檢測到起始位時(shí)喚醒 SYSOSC( 在 FCL 模式下使用
SYSOSC 時(shí), 支持高達(dá) 19200B 的速率)( 精度 1%)
? 支持環(huán)回模式運(yùn)行
? 支持硬件流控制
? 支持 9 位多點(diǎn)配置
? 支持的協(xié)議:
– 本地互連網(wǎng)絡(luò) (LIN) 支持
– DALI
–IrDA
– ISO7816 Smart Card
– RS485
– 曼徹斯特編碼
– 空閑線多處理器
MSPM0G350x 包含四個(gè) UART 接口;一個(gè)支持 LIN、IrDA、DALI、Smart Card、Manchester,三個(gè)支持待機(jī)模式下的低功耗運(yùn)行
二、 MSPM0G3507 UART結(jié)構(gòu)及收發(fā)工作過程
發(fā)送數(shù)據(jù):
當(dāng)需要進(jìn)行發(fā)送時(shí), 先將數(shù)據(jù)寫入發(fā)送 FIFO。
如果啟用 UART, 則會根據(jù) UARTx.LCRH 寄存器中指示的參數(shù)開始發(fā)送數(shù)據(jù)幀。
UART 模塊會持續(xù)發(fā)送數(shù)據(jù), 直到發(fā)送 FIFO 中沒有可發(fā)數(shù)據(jù)為止。
數(shù)據(jù)一經(jīng)寫入發(fā)送 FIFO( 即, 如果 FIFO 不為空), UARTx.STAT 寄存器中的 BUSY 位即會生效, 并在數(shù)據(jù)發(fā)送期間一直保持有效。
僅當(dāng)發(fā)送 FIFO 為空, 且最后一個(gè)字符( 包括停止位) 已發(fā)送到移位寄存器時(shí), BUSY 位才為負(fù)。即使無法再啟用UART, UART 也可以指示它正忙。生成 BREAK 信號期間, 也會設(shè)置 BUSY。
接收數(shù)據(jù):
當(dāng)接收器空閑( RX 信號持續(xù)為 1) 且數(shù)據(jù)輸入變?yōu)?mark>低電平( 已接收到開始位) 時(shí), 接收計(jì)數(shù)器開始運(yùn)行, 并根據(jù)UARTx.CTL0 寄存器中 HSE 位的過采樣設(shè)置, 在不同的周期對數(shù)據(jù)進(jìn)行采樣。
如果 RX 信號在基于過采樣設(shè)置的特定數(shù)量的周期后仍為低電平, 則開始位有效并被識別。
檢測到有效的開始位后, 將根據(jù)數(shù)據(jù)字符的編程長度對連續(xù)的數(shù)據(jù)位進(jìn)行采樣。之后將捕獲并校驗(yàn)奇偶校驗(yàn)位( 如果使能了奇偶校驗(yàn)) 。
數(shù)據(jù)長度和奇偶校驗(yàn)都在UART.LCRH 寄存器中定義。
如果 RXD 信號為高電平, 則確認(rèn)有效停止位, 否則發(fā)生成幀錯(cuò)誤。
若成功接收到一幀數(shù)據(jù), 則數(shù)據(jù)和與之相關(guān)的錯(cuò)誤標(biāo)志都將保存到接收 FIFO 中。
UART狀態(tài)標(biāo)志:
引腳上。并且通過調(diào)試下載處的排針和USB typeC接口引出。
三、 MSPM0G3507 UART例程概述
四、MSPM0G3507 配置
配置基于uart_echo_interrupts_standby例程修改
五、 MSPM0G3507 UART 串口發(fā)送及printf重定向
5.1 MSPM0G3507 UART 串口發(fā)送
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模塊* * 該函數(shù)負(fù)責(zé)配置和初始化UART_0模塊,包括設(shè)置時(shí)鐘配置、初始化配置、* 過采樣率、波特率除數(shù)以及中斷和FIFO的相關(guān)配置。通過這些配置,可以* 使UART_0模塊以特定的波特率進(jìn)行通信,并具備基本的中斷和數(shù)據(jù)處理能力。*/
SYSCONFIG_WEAK void SYSCFG_DL_UART_0_init(void)
{// 設(shè)置UART_0的時(shí)鐘配置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);// 設(shè)置UART_0的過采樣率為16倍DL_UART_Main_setOversampling(UART_0_INST, DL_UART_OVERSAMPLING_RATE_16X);// 設(shè)置UART_0的波特率除數(shù),以達(dá)到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);// 設(shè)置UART_0接收FIFO的觸發(fā)閾值為半滿DL_UART_Main_setRXFIFOThreshold(UART_0_INST, DL_UART_RX_FIFO_LEVEL_1_2_FULL);// 設(shè)置UART_0發(fā)送FIFO的觸發(fā)閾值為四分之一空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初始化調(diào)用及串口發(fā)送字符串:
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函數(shù)定義如下:
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]);}
}
提供另一個(gè)僅支持發(fā)送字符串函數(shù):
void sendString(const char *str) {while (*str != '\0') {DL_UART_Main_transmitDataBlocking(UART_0_INST, (uint8_t)*str);str++;}
}
5.2 MSPM0G3507 UART 串口中斷接收測試
上面的初始化代碼中,包含串口的發(fā)送和接收,以及開啟了串口中斷。
在5.1基礎(chǔ)上增加串口接收中斷服務(wù)函數(shù)即可
下面的中斷服務(wù)函數(shù)中實(shí)現(xiàn) 當(dāng)串口接收到數(shù)據(jù)后,翻轉(zhuǎn)LED并將接收到的通過串口發(fā)送
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基礎(chǔ)上,引用頭文件
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
重定向?qū)崿F(xiàn)3個(gè)發(fā)送函數(shù)
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;
}
運(yùn)行結(jié)果:
六 MSPM0G3507 UART中斷接收及循環(huán)緩沖實(shí)現(xiàn)自定義協(xié)議數(shù)據(jù)解析
要求:定義一個(gè)全局?jǐn)?shù)組做串口接收緩存,接收中斷將接收到的壓入緩存,在main.c的while()循環(huán)中,讀取緩存數(shù)據(jù)并進(jìn)行幀數(shù)據(jù)解析,支持自定義幀頭內(nèi)容和長度,一幀數(shù)據(jù)的長度,讀取一幀數(shù)據(jù)長度。
使用通義靈碼生成。
cmd_queue.h
/*!* \file cmd_queue.h* \brief 串口指令隊(duì)列(支持多隊(duì)列、多幀頭、可配置幀長)* \version 1.3* \date 2024.10* \copyright wo4fisher*/#ifndef _CMD_QUEUE
#define _CMD_QUEUE
#include "ti_msp_dl_config.h"// 隊(duì)列配置參數(shù)
#define QUEUE_MAX_SIZE 100typedef uint8_t qdata;
typedef uint16_t qsize;// 接收狀態(tài)結(jié)構(gòu)體,暫時(shí)未使用
typedef struct {uint8_t pos; // 當(dāng)前接收位置const qdata *pHead; // 幀頭指針uint8_t head_len; // 幀頭長度uint8_t frame_len; // 總幀長(含幀頭)
} CmdState;// 隊(duì)列句柄結(jié)構(gòu)體定義
typedef struct {qsize _head; // 寫指針qsize _tail; // 讀指針qdata _data[QUEUE_MAX_SIZE]; // 數(shù)據(jù)緩沖區(qū)
} QueueHandle;/*!* \brief 初始化一個(gè)新的隊(duì)列* \param[out] q 指向隊(duì)列句柄的指針*/
void queue_init(QueueHandle *q);/*!* \brief 清空指定隊(duì)列* \param[in] q 隊(duì)列句柄*/
void queue_reset(QueueHandle *q);/*!* \brief 將字節(jié)加入隊(duì)列* \param[in] q 隊(duì)列句柄* \param[in] _data 要入隊(duì)的數(shù)據(jù)*/
void queue_push(QueueHandle *q, qdata _data);/*!* \brief 從隊(duì)列中取出一個(gè)字節(jié)* \param[in] q 隊(duì)列句柄* \param[out] _data 數(shù)據(jù)輸出指針* \return 成功返回 true,隊(duì)列為空返回 false*/
uint8_t queue_pop(QueueHandle *q, qdata *_data);/*!* \brief 獲取隊(duì)列中當(dāng)前有效數(shù)據(jù)個(gè)數(shù)* \param[in] q 隊(duì)列句柄* \return 當(dāng)前未讀取的數(shù)據(jù)數(shù)量*/
qsize queue_size(QueueHandle *q);/*!* \brief 從隊(duì)列中提取一條完整指令幀(支持多幀頭、幀長可配)* \param[in] q 隊(duì)列句柄* \param[out] buffer 接收緩沖區(qū)* \param[in] buf_len 緩沖區(qū)大小* \param[in] frame_header 幀頭數(shù)組(如 {0x3F, 0x06})* \param[in] header_len 幀頭長度* \param[in] total_frame_len 完整幀長度(含幀頭)* \return 成功接收的字節(jié)數(shù),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 串口指令隊(duì)列實(shí)現(xiàn)(支持多隊(duì)列、多幀頭、可配置幀長)* \version 1.3* \date 2024.10* \copyright wo4fisher*/#include "cmd_queue.h"// 平臺相關(guān):關(guān)中斷保護(hù)
#define ENTER_CRITICAL()
#define LEAVE_CRITICAL()// 初始化隊(duì)列
void queue_init(QueueHandle *q) { q->_head = q->_tail = 0; }// 清空隊(duì)列
void queue_reset(QueueHandle *q) { q->_head = q->_tail = 0; }// 入隊(duì)操作
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;}
}// 出隊(duì)操作
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;
}// 獲取隊(duì)列大小
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;}// 成功:連續(xù)讀取一幀數(shù)據(jù)// 錯(cuò)誤:直接退出,直到下一次調(diào)用此函數(shù)再判斷while (queue_size(q) > 0) {// 沒有數(shù)據(jù),退出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 { // 幀數(shù)據(jù)緩存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 串口發(fā)送及printf重定向一節(jié)中實(shí)現(xiàn)了printf的重定向。
但是在添加了六 MSPM0G3507 UART中斷接收及循環(huán)緩沖實(shí)現(xiàn)自定義協(xié)議數(shù)據(jù)解析一節(jié)的代碼后,程序編譯出現(xiàn)個(gè)問題:#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文件中。
此庫文件在別的工程中也是存在的
原本以為是因?yàn)樘砑?mark>循環(huán)緩沖實(shí)現(xiàn)自定義協(xié)議數(shù)據(jù)解析的代碼才意外引入了libc.a庫文件,結(jié)果不是。
把循環(huán)緩沖實(shí)現(xiàn)自定義協(xié)議數(shù)據(jù)解析相關(guān)代碼去掉后,再此編譯,該錯(cuò)誤就消失了,可以正常使用自定義的fputc實(shí)現(xiàn)printf重定向。并且項(xiàng)目屬性中是包含libc.a文件的。
理解不了!!!
八 續(xù)上了
就在寫完七、 遺留問題----fputc重定義的內(nèi)容,為了找編譯錯(cuò)誤的圖,恢復(fù)程序,重現(xiàn)錯(cuò)誤的時(shí)候,結(jié)果。。。結(jié)果,它好了!!!!
現(xiàn)在它又能用了!!!! 汗…
不過時(shí)有丟幀的情況發(fā)生。
下圖為使能FIFO,接收中斷增加判斷接收FIFO不為空,連續(xù)讀取接收數(shù)據(jù)。
或者取消FIFO的使用,結(jié)果都是一樣,不同概率會丟幀,丟幀的原因是發(fā)送8個(gè)字節(jié),僅接受7個(gè)字節(jié),未接收完全。
最后附上main.c最后的內(nèi)容:
#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;
}