串口数据在中断中根据通信空闲判断结束帧,接收到整包数据后把整包数据通过发送到消息队列,处理数据的任务在任务函数中从消息队列获取数据并处理
1、框架
//中断服务程序
void USART3_IRQHandler(void)
{
uint8_t err = 0;
uart3_rx_msg_t *uart_rx_msg;
OSIntEnter();
if((__HAL_UART_GET_FLAG(&UART3_Handler,UART_FLAG_RXNE)!=RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
//PRINTF("3RXNE\r\n");
__HAL_UART_CLEAR_FLAG(&UART3_Handler,UART_FLAG_RXNE);
}
if((__HAL_UART_GET_FLAG(&UART3_Handler,UART_FLAG_IDLE)!=RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
//PRINTF("3IDLE\r\n");//根据空闲判断整包接收完成
UART_ConfigMode_Set_RecvData(&uart3_config);
if(UART_Is_ConfigMode(&uart3_config) == 0)
{
uart3_rx_len = USART3_DMA_RecvData(uart3_rx_buffer);
//uart3_rx_buffer[uart3_rx_len] = '\0';
//PRINTF("%s",uart3_rx_buffer);
uart_rx_msg = (uart3_rx_msg_t*)OSMemGet(mem_uart3,&err);
if(err != OS_ERR_NONE || uart_rx_msg == NULL)//
{
if(err == OS_ERR_MEM_NO_FREE_BLKS)
{
//无剩余内存块的时候,恢复任务去处理
OSTaskResume(UART3_TASK_PRIO);
}
PRINTF("\r\n WARNING: OSMemGet for uart3_rx_msg failed,return err = %d !\r\n",err);
__HAL_UART_CLEAR_IDLEFLAG(&UART3_Handler);
OSIntExit();
return;
}
memset(uart_rx_msg->buffer,0,sizeof(uart_rx_msg->buffer));
uart_rx_msg->len = uart3_rx_len;
memcpy(uart_rx_msg->buffer, uart3_rx_buffer, uart3_rx_len);
err = OSQPost(uart3_Q,uart_rx_msg); //将串口收到数据用消息队列发出
if(err != OS_ERR_NONE)
{
OSMemPut(mem_uart3,uart_rx_msg);
PRINTF("\r\n WARNING: OSQPost uart3_rx_msg failed,return err =%d !\r\n",err);
}
//OSTimeDlyResume(UART3_TASK_PRIO);
OSTaskResume(UART3_TASK_PRIO);
}
__HAL_UART_CLEAR_IDLEFLAG(&UART3_Handler);
}
if((__HAL_UART_GET_FLAG(&UART3_Handler,UART_FLAG_TC)!=RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
//PRINTF("3TC\r\n");
__HAL_UART_CLEAR_FLAG(&UART3_Handler,UART_FLAG_TC);
}
HAL_UART_IRQHandler(&UART3_Handler);
OSIntExit();
}
//任务函数
void UART3_Task_RFC1(void *pdata)
{
uint8_t err = 0;
uart3_rx_msg_t *uart_rx_msg;
PRINTF("UART3_Task_RFC1 Start.\r\n");
while(1)
{
//PRINTF("UART3_Task_RFC1\r\n");
uart_rx_msg = OSQAccept(uart3_Q, &err);//从消息队列获取数据
while(err == OS_ERR_NONE)
{
PRINTF("UART3_Task_RFC1 recv len:%d\r\n", uart_rx_msg->len);
PRINTF("UART3_Task_RFC1 recv data:%s\r\n", uart_rx_msg->buffer);
printf_hex("UART3_Task_RFC1 recv data:", uart_rx_msg->buffer, uart_rx_msg->len);
OSMemPut(mem_uart3, uart_rx_msg);
uart_rx_msg = OSQAccept(uart3_Q, &err);
}
//OSTimeDly(10);
//delay_ms(10);
//PRINTF("UART3_Task_RFC1 Suspend.\r\n");
OSTaskSuspend(UART3_TASK_PRIO);
}
}
2、在以上框架下对数据操作分情况讨论
1、串口数据都是主送上送,任务只负责处理以及应答
只有一个任务操作读写串口数据时,不存在资源抢占,使用框架结构处理即可,但
2、串口数据存在主动上送,也存在主动召读,召读有超时时间
主动上送数据由专门处理任务,发起召读命令的任务与专门处理任务能是一个任务,否则数据处理会因为召读等待得不到及时处理
Interrupt 负责把数据发送到消息队列
召读命令请求数据时,数据直接从串口获取,添加标志不通过Interrupt 把数据发送到消息队列
//USART2中断服务程序
void USART2_IRQHandler(void)
{
uint8_t err = 0;
uart2_rx_msg_t *uart_rx_msg;
OSIntEnter();
if((__HAL_UART_GET_FLAG(&UART2_Handler,UART_FLAG_RXNE)!=RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
//PRINTF("2RXNE\r\n");
__HAL_UART_CLEAR_FLAG(&UART2_Handler,UART_FLAG_RXNE);
}
if((__HAL_UART_GET_FLAG(&UART2_Handler,UART_FLAG_IDLE)!=RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
//PRINTF("2IDLE\r\n");
UART_ConfigMode_Set_RecvData(&uart2_config);//判断是否是召读命令应答,如果是,则不发到消息队列,有召读处自己读取
if(UART_Is_ConfigMode(&uart2_config) == 0)
{
uart2_rx_len = USART2_DMA_RecvData(uart2_rx_buffer);
//uart2_rx_buffer[uart2_rx_len] = '\0';
//PRINTF("%s",uart2_rx_buffer);
uart_rx_msg = (uart2_rx_msg_t*)OSMemGet(mem_uart2,&err);
if(err != OS_ERR_NONE || uart_rx_msg == NULL)//
{
if(err == OS_ERR_MEM_NO_FREE_BLKS)
{
//无剩余内存块的时候,恢复任务去处理
OSTaskResume(UART2_TASK_PRIO);
}
PRINTF("\r\n WARNING: OSMemGet for uart2_rx_msg failed,return err = %d !\r\n",err);
__HAL_UART_CLEAR_IDLEFLAG(&UART2_Handler);
OSIntExit();
return;
}
memset(uart_rx_msg->buffer,0,sizeof(uart_rx_msg->buffer));
uart_rx_msg->len = uart2_rx_len;
memcpy(uart_rx_msg->buffer, uart2_rx_buffer, uart2_rx_len);
err = OSQPost(uart2_Q,uart_rx_msg); //将串口收到数据用消息队列发出
if(err != OS_ERR_NONE)
{
OSMemPut(mem_uart2,uart_rx_msg);
PRINTF("\r\n WARNING: OSQPost uart2_rx_msg failed,return err =%d !\r\n",err);
}
//OSTimeDlyResume(UART2_TASK_PRIO);
OSTaskResume(UART2_TASK_PRIO);
}
__HAL_UART_CLEAR_IDLEFLAG(&UART2_Handler);
}
if((__HAL_UART_GET_FLAG(&UART2_Handler,UART_FLAG_TC)!=RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
//PRINTF("2TC\r\n");
__HAL_UART_CLEAR_FLAG(&UART2_Handler,UART_FLAG_TC);
}
HAL_UART_IRQHandler(&UART2_Handler);
OSIntExit();
}
uint8_t GPRS_Setpara(const char *cmd,const char *para, uint32_t timeout_ms)
{
uint8_t s[128];
uint8_t ret = 0;
memset(s,0,sizeof(s));
(void)sprintf((char *)s,"%s%s",cmd,para);
UART_Enter_ConfigMode(&uart2_config);//标记召读开始
GPRS_Send_ATCMD((char *)s);
if(GPRS_Receive(gprs_rxbuf, timeout_ms))//召读处直接获取数据,并处理
{
if(GPRS_Find_bina(gprs_rxbuf, (uint8_t *)"OK")!=NULL)
{
// PRINTF("ATCMD Return:%s\r\n", gprs_rxbuf);
ret = 1;
}
if(GPRS_Find_bina(gprs_rxbuf, (uint8_t *)"+QIOPEN: 0,566")!=NULL)
{
ret = 0;
}
}
UART_Exit_ConfigMode(&uart2_config);
return ret;
}
缺点:如果在召读标记的过程中,串口收到主动上送的非召读数据,导致非召读数据得不到正常处理,且导致等待召读应答较久,该方式适合无主动上报的纯召读-应答模式,可不使用消息队列
3、串口数据存在主动上送也存在召读,且高效处理
不管是召读数据还是主动上报数据全部由Interrupt 负责把数据发送到消息队列;任务函数中根据数据类型理解处理:标记是否收到数据,并且根据是否需要返回数据进行数据返回,以便召读处可立即获取到数据。
该方式实现依赖于一个动态链表,链表存放召读处期待的数据类型,以及节点地址,该链表会在任务函数处理中用来查看,是否有对应的召读,根据标志情况返回数据。
node_list_t *node_list_head = NULL;
node_list_t *node_list_current = NULL;
node_list_t *find_node_list(uint8_t addr, uint8_t type)
{
node_list_t *node;
if(node_list_head == NULL){
// PRINTF("node_list_head is NULL\r\n");
return NULL;
}
node = node_list_head;
do{
if(node->addr == addr && node->type == type){
// PRINTF("node addr:%02x type:%02x node_list finded\r\n", addr, type);
return node;
}
node = node->next;
}while(node != NULL);
PRINTF("node addr:%02x type:%02x no node_list, return NULL\r\n", addr, type);
return node;
}
node_list_t *create_node_list(uint8_t addr, uint8_t type)
{
uint8_t err = 0;
node_list_t *node = NULL;
node = find_node_list(addr, type);
if(node != NULL){
// PRINTF("node addr:%02x type:%02x node_list has exist\r\n", addr, type);
return node;
}
//if((node = (node_list_t *)malloc(sizeof(node_list_t))) != NULL)
if((node = (node_list_t*)OSMemGet(mem_nodelist,&err)) != NULL)
{
// PRINTF("node addr:%02x type:%02x OSMemGet node_list_t succeed\r\n", addr, type);
}
else{
// PRINTF("node addr:%02x type:%02x OSMemGet node_list_t failed,return err:%d\r\n", addr, type, err);
return NULL;
}
node->addr = addr;
node->type = 0;
node->tx = 0;
node->rx = 0;
node->len = 0;
node->buf = NULL;
node->next = NULL;
if(node_list_head == NULL){
node_list_head = node;
node_list_current = node;
}
else{
node_list_current->next = node;
node_list_current = node;
}
return node;
}
int delete_node_list(uint8_t addr, uint8_t type)
{
uint8_t err = 0;
node_list_t *node = NULL;
node_list_t *last = NULL;
node = find_node_list(addr, type);
if(node == NULL){
// PRINTF("node addr:%02x type:%02x node_list don't exist\r\n", addr, type);
return 0;
}
if(node == node_list_head){
node_list_head = node->next;
if(node_list_head == NULL)
node_list_current = NULL;
// PRINTF("node addr:%02x type:%02x node_list delete\r\n", addr, type);
//free(node);
err = OSMemPut(mem_nodelist, node);
if(err != OS_ERR_NONE) PRINTF("OSMemPut node_list Failed.\r\n");
}
else{
last = node_list_head;
do{
if(last->next == node){
// PRINTF("node addr:%02x type:%02x last node_list finded\r\n",addr, type);
break;
}
last = last->next;
}while(last != NULL);
if(last != NULL)
{
last->next = node->next;
if(node == node_list_current) node_list_current = last;
// PRINTF("node addr:%02x type:%02x node_list delete\r\n", addr, type);
//free(node);
err = OSMemPut(mem_nodelist, node);
if(err != OS_ERR_NONE) PRINTF("OSMemPut node_list Failed.\r\n");
}
else
{
// PRINTF("node addr:%02x type:%02x node_list delete failed\r\n", addr, type);
return 0;
}
}
return 1;
}
int delete_node_list_usenode(node_list_t *node)
{
uint8_t err = 0;
node_list_t *last = NULL;
if(node == node_list_head){
node_list_head = node->next;
if(node_list_head == NULL)
node_list_current = NULL;
PRINTF("node addr:%02x type:%02x node_list delete succeed\r\n",node->addr, node->type);
//free(node);
err = OSMemPut(mem_nodelist, node);
if(err != OS_ERR_NONE) PRINTF("OSMemPut node_list Failed.\r\n");
}
else{
last = node_list_head;
do{
if(last->next == node){
// PRINTF("node addr:%02x type:%02x last node_list finded\r\n",node->addr, node->type);
break;
}
last = last->next;
}while(last != NULL);
if(last != NULL)
{
last->next = node->next;
if(node == node_list_current) node_list_current = last;
// PRINTF("node addr:%02x type:%02x node_list delete succeed\r\n",node->addr, node->type);
//free(node);
err = OSMemPut(mem_nodelist, node);
if(err != OS_ERR_NONE) PRINTF("OSMemPut node_list Failed.\r\n");
}
else
{
// PRINTF("node addr:%02x type:%02x node_list delete failed\r\n",node->addr, node->type);
return 0;
}
}
return 1;
}
void node_init(node_list_t *node, uint8_t addr, uint8_t type, uint8_t tx, uint8_t * buf)
{
if(node == NULL) return;
node->addr = addr;
node->type = type;
node->tx = tx;
//node->rx = 0;
//node->len = 0;
node->buf = buf;
//node->next = NULL;
}
//UART4中断服务程序
void UART4_IRQHandler(void)
{
uint8_t err = 0;
uart4_rx_msg_t *uart_rx_msg;
OSIntEnter();
if((__HAL_UART_GET_FLAG(&UART4_Handler,UART_FLAG_RXNE)!=RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
//PRINTF("4RXNE\r\n");
UART4_RecvData(uart4_rx_buffer);
__HAL_UART_CLEAR_FLAG(&UART4_Handler,UART_FLAG_RXNE);
}
if((__HAL_UART_GET_FLAG(&UART4_Handler,UART_FLAG_IDLE)!=RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
//PRINTF("4IDLE\r\n");
//非DMA方式
UART4_RX_STA|=0x4000;
UART_ConfigMode_Set_RecvData(&uart4_config);
if(UART_Is_ConfigMode(&uart4_config) == 0)
{
if((UART4_RX_STA&0xc000)==0xc000)
{
//uart4_rx_buffer[uart4_rx_len] = '\0';
//PRINTF("%s",uart4_rx_buffer);
//PRINTF("%s",uart1_rx_buffer);
if(ucosii_start_ok == 0)
{
UART4_RX_STA = 0;
__HAL_UART_CLEAR_IDLEFLAG(&UART4_Handler);
OSIntExit();
return;
}
uart_rx_msg = (uart4_rx_msg_t*)OSMemGet(mem_uart4,&err);
if(err != OS_ERR_NONE || uart_rx_msg == NULL)//
{
if(err == OS_ERR_MEM_NO_FREE_BLKS)
{
//无剩余内存块的时候,恢复任务去处理
OSTaskResume(UART4_TASK_PRIO);
}
PRINTF("\r\n WARNING: OSMemGet for uart4_rx_msg failed,return err = %d !\r\n",err);
UART4_RX_STA = 0;
__HAL_UART_CLEAR_IDLEFLAG(&UART4_Handler);
OSIntExit();
return;
}
memset(uart_rx_msg->buffer,0,sizeof(uart_rx_msg->buffer));
uart_rx_msg->len = uart4_rx_len;
memcpy(uart_rx_msg->buffer, uart4_rx_buffer, uart4_rx_len);
err = OSQPost(uart4_Q,uart_rx_msg); //将串口收到数据用消息队列发出
if(err != OS_ERR_NONE)
{
OSMemPut(mem_uart4,uart_rx_msg);
PRINTF("\r\n WARNING: OSQPost uart4_rx_msg failed,return err =%d !\r\n",err);
}
UART4_RX_STA = 0;
//OSTimeDlyResume(UART4_TASK_PRIO);
OSTaskResume(UART4_TASK_PRIO);
}
}
__HAL_UART_CLEAR_IDLEFLAG(&UART4_Handler);
}
if((__HAL_UART_GET_FLAG(&UART4_Handler,UART_FLAG_TC)!=RESET)) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
//PRINTF("4TC\r\n");
__HAL_UART_CLEAR_FLAG(&UART4_Handler,UART_FLAG_TC);
}
HAL_UART_IRQHandler(&UART4_Handler);
OSIntExit();
}
void UART8_Task_DTU(void *pdata)
{
uint8_t err = 0;
uart8_rx_msg_t *uart_rx_msg;
char str[30];
uint32_t time_now = 0;
uint32_t time_ms = 0;
uint8_t m2taddr = 0x01;
// uint8_t m2t_Init_ok = 0;
// uint8_t heart_timeout = 0;//0超时,1未超时
PRINTF("UART8_Task_DTU Start.\r\n");
time_now = gettime();
while(1)
{
//PRINTF("UART8_Task_DTU\r\n");
uart_rx_msg = OSQAccept(uart4_Q, &err);
while(err == OS_ERR_NONE)
{
PRINTF("UART8_Task_DTU recv len:%d\r\n", uart_rx_msg->len);
// PRINTF("UART8_Task_DTU recv data:%s\r\n", uart_rx_msg->buffer);
printf_hex("UART8_Task_DTU recv data", uart_rx_msg->buffer, uart_rx_msg->len);
M2T_Process(uart_rx_msg->buffer, uart_rx_msg->len);//任务函数中根据数据类型理解处理
OSMemPut(mem_uart4, uart_rx_msg);
uart_rx_msg = OSQAccept(uart4_Q, &err);
}
time_ms += 10;
if(time_ms >= 1000)
{
time_ms = 0;
time_now = gettime();
}
//OSTimeDly(10);
delay_ms(10);
//PRINTF("UART8_Task_DTU Suspend.\r\n");
//OSTaskSuspend(UART6_TASK_PRIO);
}
}
//协议处理
uint8_t M2T_Process(uint8_t *data, uint16_t datalen)
{
uint8_t ret = 0;
m2tpara_t m2tpara;
node_list_t *node = NULL;
m2tpara.data = m2t_txbuf;
m2tpara.len = M2T_Get_UserData(data, datalen, &m2tpara);
node = find_node_list(m2tpara.m2taddr, m2tpara.ti);
if(node == NULL)
{
PRINTF("M2T_Process discard accident frame\r\n");
return ret;
}
switch(m2tpara.ti)
{
case TI_LINK_START://链路启动
if(node->buf == NULL)
{//数据不需要返回则立即处理
if(node->tx == 1) node->rx = 1;
PRINTF("M2T_Process recv TI_LINK_START\r\n");
}
else
{//数据返回
if(node->tx == 1)
{
memcpy(node->buf, data, datalen);
node->len = datalen;
node->rx = 1;
}
//M2T_Queue_Post(data, datalen);
}
break;
case TI_CALL_ALL://总召唤
if(node->buf == NULL)
{//数据不需要返回则立即处理
if(node->tx == 1) node->rx = 1;
PRINTF("M2T_Process recv TI_CALL_ALL\r\n");
}
else
{//数据返回
if(node->tx == 1)
{
memcpy(node->buf, data, datalen);
node->len = datalen;
node->rx = 1;
}
//M2T_Queue_Post(data, datalen);
}
break;
case TI_ALARM://突发召换告警
if(node->buf == NULL)
{//数据不需要返回则立即处理
if(node->tx == 1) node->rx = 1;
PRINTF("M2T_Process recv M2T_Heart_Ack\r\n");
}
else
{//数据返回
if(node->tx == 1)
{
memcpy(node->buf, data, datalen);
node->len = datalen;
node->rx = 1;
}
//M2T_Queue_Post(data, datalen);
}
break;
default :
PRINTF("M2T_Process recv ERROR\r\n");
break;
}
return ret;
}
//时钟同步
uint8_t M2T_Clock_Syn(uint8_t m2taddr)
{
uint16_t len;
m2tpara_t m2tpara;
uint8_t ret = 0;
uint8_t timelen = 0;
node_list_t *node = NULL;
//时间临时数据存到m2t_rxbuf
timelen = M2T_Get_Time(m2t_rxbuf);
if(timelen == 0)
{ PRINTF("M2T Get Time failed.\r\n");
return 0; //时钟同步失败
}
if(M2T_MutexPend() == 0) return ret;
m2tpara.m2taddr = m2taddr;
m2tpara.ti = TI_CLOCK_SYN;
m2tpara.cot = COT_ACTIVE;
m2tpara.ct = 1;
m2tpara.data = m2t_txbuf;
len = M2T_Create_Frame(&m2tpara, m2t_rxbuf, timelen);
//UART_Enter_ConfigMode(&uart6_config);
node = create_node_list(m2tpara.m2taddr, m2tpara.ti);
if(node == NULL)
{
M2T_MutexPost();
return 0;
}
node_init(node, m2tpara.m2taddr, m2tpara.ti, 1, m2t_rxbuf);
M2T_Send(m2t_txbuf,len);
//len = M2T_Receive(m2t_rxbuf, M2T_RECV_TIMEOUT);
if(M2T_Receive_Check(node, M2T_RECV_TIMEOUT, node->tx, node->tx))
{
len = node->len;
}
else
{
len = 0;
}
if(len)
{
memset(m2t_txbuf, 0, sizeof(m2t_txbuf));
m2tpara.len = M2T_Get_UserData(m2t_rxbuf, len, &m2tpara);
if((m2tpara.ti == TI_CLOCK_SYN) && (m2tpara.cot == COT_ACTIVE_ACK) && (m2tpara.ct == 1) && (m2tpara.len == 7))
{
ret = 1;//时钟同步成功
PRINTF("M2T_Clock_Syn succeed.\r\n");
}
}
if(ret ==0 ) PRINTF("M2T_Clock_Syn failed.\r\n");
delete_node_list_usenode(node);
M2T_MutexPost();
//UART_Exit_ConfigMode(&uart6_config);
return ret;
}
//检查node是否收到数据,timeout_ms是最大超时时间,返回0,超时,1接收成功
uint8_t M2T_Receive_Check(node_list_t *node, int32_t timeout_ms, uint8_t tx, uint8_t rx)
{
uint16_t singletime_ms;
uint8_t ret = 0;
singletime_ms = 100;
while(timeout_ms)
{
delay_ms(singletime_ms);
if(node->tx == tx && node->rx == rx)
{
ret = 1;
break;
}
timeout_ms -= singletime_ms;
}
return ret;
}