at组件介绍 关于at组件的介绍可移步到rtthread的文档中心 AT命令章节,这里不再赘述
移植到freeRTOS项目中只用到的at client的功能,所以这里也只移植了client部分。移植分为两块,一块是将at client中使用的线程创建、信号量创建收发等rtthread系统API替换成freeRTOS的,这部分比较简单;另一块是串口数据收发部分,rtthread是基于自带的UART设备驱动框架来做的,并带有数据缓存功能,移植过来需要将其剥离,然后实现串口数据的发送、接收及接收数据的缓存功能。移植后 - 串口数据发送是采用的轮询发送模式
- 串口数据接收使用的是DMA方式
- 接收数据缓存使用的是顺序队列
在移植前先来了解下at client的设计思路,有哪些数据结构以及提供了哪些API,注意这些数据结构跟API是移植调整过后的,跟原有的相差不大
数据结构at_response 结构体用于响应数据的接收,at_urc_table 结构体是urc数据注册表,at_client 结构体是at client控制句柄
- struct at_response
- {
- /* response buffer */
- char *buf;
- /* the maximum response buffer size, it set by `at_create_resp()` function */
- uint16_t buf_size;
- /* the length of current response buffer */
- uint16_t buf_len;
- /* the number of setting response lines, it set by `at_create_resp()` function
- * == 0: the response data will auto return when received 'OK' or 'ERROR'
- * != 0: the response data will return when received setting lines number data */
- uint16_t line_num;
- /* the count of received response lines */
- uint16_t line_counts;
- /* the maximum response time */
- uint32_t timeout;
- };
- typedef struct at_response *at_response_t;
- struct at_client;
- /* URC(Unsolicited Result Code) object, such as: 'RING', 'READY' request by AT server */
- struct at_urc
- {
- const char *cmd_prefix;
- const char *cmd_suffix;
- void (*func)(struct at_client *client, const char *data, uint16_t size);
- };
- typedef struct at_urc *at_urc_t;
- struct at_urc_table
- {
- size_t urc_size;
- const struct at_urc *urc;
- };
- typedef struct at_urc *at_urc_table_t;
- struct at_client
- {
- at_status_t status;
- char end_sign;
- /* the current received one line data buffer */
- char *recv_line_buf;
- /* The length of the currently received one line data */
- uint16_t recv_line_len;
- /* The maximum supported receive one line data length */
- uint16_t recv_line_size;
- xSemaphoreHandle rx_notice;
- xSemaphoreHandle lock;
- at_response_t resp;
- xSemaphoreHandle resp_notice;
- at_resp_status_t resp_status;
- struct at_urc_table *urc_table;
- uint16_t urc_table_size;
- /* uart receive queue */
- struct array_queue *recv_q;
- /* The maximum supported receive data length */
- uint16_t recv_queue_size;
- /* uart receive uart */
- UART_INDEX_E uart_index;
- /* handle task */
- TaskHandle_t parser;
- };
- typedef struct at_client *at_client_t;
复制代码
API
- /* get AT client object */
- at_client_t at_client_get_first(void);
- /* AT client initialize and start*/
- at_client_t at_client_init(UART_INDEX_E uart_index, uint16_t recv_line_size, uint16_t recv_queue_size);
- /* AT client send or receive data */
- int at_client_obj_send(at_client_t client, char *buf, int size);
- int at_client_obj_recv(at_client_t client, char *buf, int size, uint32_t timeout);
- /* AT client send commands to AT server and waiter response */
- int at_obj_exec_cmd(at_client_t client, at_response_t resp, const char *cmd_expr, ...);
- /* set AT client a line end sign */
- void at_obj_set_end_sign(at_client_t client, char ch);
- /* Set URC(Unsolicited Result Code) table */
- int at_obj_set_urc_table(at_client_t client, const struct at_urc * table, int size);
- /* AT response object create and delete */
- at_response_t at_create_resp(uint16_t buf_size, uint16_t line_num, uint32_t timeout);
- void at_delete_resp(at_response_t resp);
- at_response_t at_resp_set_info(at_response_t resp, int buf_size, int line_num, uint32_t timeout);
- /* AT response line buffer get and parse response buffer arguments */
- //获取指定行号的响应数据
- const char *at_resp_get_line(at_response_t resp, int resp_line);
- //根据关键字获取响应数据
- const char *at_resp_get_line_by_kw(at_response_t resp, const char *keyword);
- //根据resp_expr格式使用标准sscanf 解析语法,解析指定行的响应数据
- int at_resp_parse_line_args(at_response_t resp, int resp_line, const char *resp_expr, ...);
- //根据resp_expr格式使用标准sscanf 解析语法,解析指定关键字的响应数据
- int at_resp_parse_line_args_by_kw(at_response_t resp, const char *keyword, const char *resp_expr, ...);
复制代码
at client 流程用户线程中调用 at_client_init API时会自动创建 parser 线程,parser线程内部实现了 响应数据的接收、响应数据的解析、URC 数据处理等整个AT 命令数据交互流程。
发送数据时的数据流向为:用户线程调用 at_exec_cmd 发送数据 ---> 串口 ---> at server
接收数据时的数据流向为:at server ---> 串口接收,然后发送 rx_notice 信号量 ---> parser线程解析,然后发送 resp_notice 信号量
串口数据接收处理
在创建at client处理线程时,需要注册好 串口数据接收回调函数,这样当串口接收到数据时,就会调用该回调函数,在该回调函数中,会将接收到的数据存入队列中,然后发送信号量通知at client处理线程进行处理
串口驱动:
- typedef enum
- {
- UART1_INDEX,
- UART2_INDEX,
- UART3_INDEX,
- UART_INDEX_ALL
-
- }UART_INDEX_E;
- /* STM32 uart dirver class */
- struct stm32_uart
- {
- const char *name;
- USART_TypeDef *Instance;
- IRQn_Type irq_type;
- int (*rx_ind)(UART_INDEX_E uart_index, char *recv_data, int recv_len);
- };
- extern struct stm32_uart stm32_uart_handle[UART_INDEX_ALL];
- extern void set_uart_rx_indicate(UART_INDEX_E uart_index, int (*set_rx_ind)(UART_INDEX_E uart_index, char *recv_data, int recv_len));
- extern void uart_send_data_by_index(UART_INDEX_E index, unsigned char *data, unsigned short len);
复制代码
串口接收中断:
- void USART3_IRQHandler( void )
- {
- uint8_t tmp;
- uint16_t rxLen = 0;
-
- if(LL_USART_IsActiveFlag_IDLE(USART3))
- {
- rxLen = sizeof(uart3_dam_rxbuf) - LL_DMA_GetDataLength(DMA1, LL_DMA_STREAM_1);
- if(stm32_uart_handle[UART3_INDEX].rx_ind)
- {
- //调用了注册的数据接收回调函数
- stm32_uart_handle[UART3_INDEX].rx_ind(UART3_INDEX, uart3_dam_rxbuf, rxLen);
- }
-
- LL_USART_ClearFlag_IDLE(USART3);
- LL_DMA_DisableStream(DMA1, LL_DMA_STREAM_1);
- LL_DMA_SetDataLength(DMA1, LL_DMA_STREAM_1, sizeof(uart3_dam_rxbuf));
- //将 EN 位置“1”以启动新传输之前,DMA_LISR 或 DMA_HISR 寄存器中与数据流相对应的事件标志必须清零。
- LL_DMA_ClearFlag_TE1(DMA1);
- LL_DMA_ClearFlag_FE1(DMA1);
- LL_DMA_ClearFlag_TC1(DMA1);
- LL_DMA_ClearFlag_HT1(DMA1);
- LL_DMA_EnableStream (DMA1, LL_DMA_STREAM_1);
- }
- ...
- }
复制代码
at client处理线程注册的接收回调函数
- static int at_client_rx_indicate(UART_INDEX_E uart_index, char *recv_data, int recv_len)
- {
- int idx = 0, i = 0, res;
- BaseType_t xHigherPriorityTaskWoken;
-
- for (idx = 0; idx < AT_CLIENT_NUM_MAX; idx++)
- {
- if (at_client_table[idx].uart_index == uart_index && at_client_table[idx].status == AT_STATUS_INITIALIZED)
- {
- for(i=0; i
- {
- if((res = array_queue_enqueue(at_client_table[idx].recv_q, &recv_data[i])) != 0)
- break;
- }
- xSemaphoreGiveFromISR(at_client_table[idx].rx_notice, &xHigherPriorityTaskWoken);
- }
- }
- return recv_len;
- }
复制代码
数据缓存 --- 顺序队列
队列跟栈类似都是“操作受限”的线性表,只不过队列是先进先出结构,队列也有两个基本的操作:入队 enqueue(),放一个数据到队列尾部;出队 dequeue(),从队列头部取一个元素。根据实现方式不同,也可以分为两种:使用数组来实现的顺序队列,和使用链表来实现的链式队列。
队列需要两个指针:一个是 head 指针,指向队头;一个是 tail 指针,指向队尾;
用size来表示队列的总大小,num来记录队列的当前元素个数,则可以这样判断:队列满时:num = size,队列空时:num = 0
- #define ARRAY_QUEUE_MALLOC(size) pvPortMalloc(size)
- #define ARRAY_QUEUE_CALLOC(n,size) pvPortMalloc(n*size)
- #define ARRAY_QUEUE_FREE(p) vPortFree(p)
- #define ARRAY_QUEUE_SIZE(pqueue) (pqueue->size)
- #define ARRAY_QUEUE_NUM(pqueue) (pqueue->num)
- #define ARRAY_QUEUE_IS_EMPTY(pqueue) (pqueue->num == 0)
- #define ARRAY_QUEUE_IS_FULL(pqueue) (pqueue->num == pqueue->size)
- struct array_queue
- {
- int size; /* queue total size */
- int num; /* queue used size rang:1-(size-1) */
- int head; /* points to the the next dequeue data */
- int tail; /* points to the the next enqueue data */
- int tpsz; /* data type size */
- void *p; /* queue space */
- };
- extern struct array_queue* array_queue_creat(int size, int tpsz);
- extern int array_queue_init (struct array_queue *queue, int size, int tpsz);
- extern int array_queue_empty (struct array_queue *queue);
- extern int array_queue_clear (struct array_queue *queue);
- extern int array_queue_destory(struct array_queue *queue);
- extern int array_queue_enqueue(struct array_queue *queue, void *in_data);
- extern int array_queue_dequeue(struct array_queue *queue, void *out_data);
复制代码
原文转自:rt-thread社区
|