将串口接收配置为 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条评论)
登录后参与讨论