原创 一个基于HAL库的串口控制框架

2024-12-4 15:23 83 0 分类: MCU/ 嵌入式 文集: STM32 软件

将串口接收配置为 DMA方式,DMA 使用 DMA_CIRCULAR 模式工作。串口接收的数据实际上构成循环队列。

定义一个结构Comm_typedef 结构体中包含指向接收到的第一字节位置chrchr,最后一字节位置 lastchr 接收缓冲区长度 rlen 指向接收缓冲区指针 *pRxBuf 指向使用的串口句柄指针*pUart, 指向使用的DMA串口接收句柄指针 *pRx 指向DMA串口发送指针*pTx 检查接收到的字节数的函数 CheckRsv 和串口处理函数 Parse

#ifndef __COMM_H

#define __COMM_H

typedef struct

{

uint16_t curchr;

uint16_t lastchr;

uint16_t rlen;

uint8_t *pRxBuf;

UART_HandleTypeDef *pUart;

DMA_HandleTypeDef *pRx;

DMA_HandleTypeDef *pTx;

uint8_t (* Parse)(struct stComm *p);

uint8_t (* CheckRsv)(struct stComm *p);

} Comm_typedef;

uint8_t UartCheckRsv(Comm_typedef *p);

uint8_t SerialParse(Comm_typedef *p);

void Serial_Control_Init(Comm_typedef *pcom);

#endif

// 完成串口的配置后,可以得到 uart 及 dma 操作句柄

extern UART_HandleTypeDef huart2;

extern DMA_HandleTypeDef hdma_usart2_tx;

extern DMA_HandleTypeDef hdma_usart2_rx;

// 定接收缓冲区 长度及缓冲区

#define RXBUFLEN 32

uint8_t UARTRxBuf[RXBUFLEN];

// 检查是否接收到数据的函数,使用DMA数据计数器与DMA缓冲区长度的差得到lastchr的位置,如果lastchr 不等于 chrchr则说明收到数据。

uint8_t UartCheckRsv(Comm_typedef *p) {

p->lastchr = p->rlen - p->pRx->Instance->CNDTR; // NDTR

if(p->curchr != p->lastchr)

return 1;

else

return 0;

}

uint8_t SerialParse(Comm_typedef *p);

// 串口控制初始化函数,将一个初始化一个 Comm_typedef 类型

void Serial_Control_Init(Comm_typedef *pcom) {

pcom->pUart = &huart2;

pcom->rlen = RXBUFLEN;

pcom->lastchr = 0;

pcom->curchr = 0;

pcom->pRxBuf = UARTRxBuf;

pcom->pRx = &hdma_usart2_rx;

pcom->pTx = &hdma_usart2_tx;

pcom->CheckRsv = UartCheckRsv;

pcom->Parse = SerialParse;

}

实际使用时,先定义好Comm_typedef 类型

Comm_typedef CntlComm;

然后初始化

Serial_Control_Init(&CntlComm);

启动串口DMA方式接收。

HAL_UART_Receive_DMA(CntlComm.pUart,CntlComm.pRxBuf,CntlComm.rlen);

// 程序中定时执行CntlComm.CheckRsv(&CntlComm)检查是否接收到数据,如果收到则运行串口解析程序。 这个定时时间大于100ms为宜。不要过快。

if(CntlComm.CheckRsv(&CntlComm)) {

CntlComm.Parse(&CntlComm);

}

// 关于解析程序,需要依据自行定义的串口通信协议编写。这里有一个简单的协议。数据格式如下:

包头

数据内容

包尾

SET

XXX

\r\n

GET

XXX

\r\n

// 定义一个解析程序如下:在解析程序中查找合法的字段,先找包头,然后找包尾。找到合法的包后,作出交互处理。同时修改curchr 跳过已经处理的数据或无效数据。

#define MAXCMDLEN 30

uint8_t SerialParse(Comm_typedef *p){

static uint8_t tempstr[30];

int8_t sync_idx = -1,tail_idx = -1, j;

int16_t i;

static uint8_t cmd[MAXCMDLEN];

i = p->curchr;

while(i != p->lastchr){ //Scan to last chr to find head word

if((((p->pRxBuf == 'G')&&(p->pRxBuf[(i+1) % p->rlen] == 'E')&&(p->pRxBuf[(i+2) % p->rlen] == 'T'))

||((p->pRxBuf == 'S')&&(p->pRxBuf[(i+1) % p->rlen] == 'E')&&(p->pRxBuf[(i+2) % p->rlen] == 'T'))){

sync_idx = i; /* first sync byte is found */

break;

}

else{

i = (i >= (p->rlen - 1))? 0 : i + 1;

}

}

if(sync_idx != -1){ // head found

j = 0;

while(i !=p->lastchr){

cmd[j++] = p->pRxBuf;

if(j >= (p->rlen - 1)) break;

if((p->pRxBuf == '\r')&&(p->pRxBuf[(i+1) % p->rlen] == '\n')){

tail_idx = i;

break;

}

else{

i = (i >= (p->rlen - 1))? 0 : i + 1;

}

}

cmd[j++] = '\n';cmd[j]=0;

if(tail_idx != -1){ // tail found

if(strstr((char *)cmd,"SETTIME") != NULL){

uint8_t hour = ((cmd[7]-'0')*10) + (cmd[8] - '0');

uint8_t minute = ((cmd[9]-'0')*10) + (cmd[10] - '0');

uint8_t second = ((cmd[11]-'0')*10) + (cmd[12] - '0');

DS1302_SetTime(hour,minute,second);

sprintf((char *)tempstr,"Write time\r\n");

HAL_UART_Transmit_DMA(p->pUart,tempstr,strlen((char *)tempstr));

}

else if(strstr((char *)cmd,"GETTIME") != NULL){

uint8_t year,month,day,hour,minute,second;

DS1302_GetDate(&year, &month, &day);

DS1302_GetTime(&hour, &minute, &second);

sprintf((char *)tempstr,"%d.%d.%d. %2d:%02d:%02d\r\n",year,month,day,hour,minute,second);

HAL_UART_Transmit(p->pUart,tempstr,strlen((char *)tempstr),1000);

}

p->curchr = tail_idx + 2;

} // Finish one cmd process

else

{

// tail not found

p->curchr = p->lastchr;

return 0;

}

}

else

{ // head not found

p->curchr = p->lastchr;

return 0;

}

return 1;

}

如果有多个串口,可以为每个串口控制定义一个 Comm_typedef 类型。分别进行初始化。这个可以使用近于一致的操作。并实现各自的独立性,互不影响。

作者: southcreek, 来源:面包板社区

链接: https://mbb.eet-china.com/blog/uid-me-408807.html

版权声明:本文为博主原创,未经本人允许,禁止转载!

文章评论0条评论)

登录后参与讨论
我要评论
0
0
关闭 站长推荐上一条 /2 下一条