移植到freeRTOS
项目中只支持在系统中使用的客户端的功能,创建只是将客户端的两个块的线程也转移给客户端,创建一个等流量的客户端,创建免费的 API 替换为 API 的服务,这部分比较简单的另一个功能是驱动器自带发送数据的部分,基于线程的UART的,并携带设备,移植过来将其执行操作,然后执行监控数据,接收及接收及发送数据接收数据的缓存功能。
卫星数据发送是采用轮询发送模式
发送数据接收使用的是DMA方式
接收数据高速使用是起点
移植前先来了解下客户端的思路,有哪些数据结构以及提供了哪些API,注意这些数据结构跟API是移植后的,跟原来设计的不同
数据结构
at_response 结构体用于响应数据的接收结构体是控制的数据,at_client 结构体是在客户端句柄
结构 at_response
{
/* 响应缓冲区 */
字符 *buf;
/* 最大响应缓冲区大小,由 `at_create_resp()` 函数设置 */
uint16_t buf_size;
/* 当前响应缓冲区的长度 */
uint16_t buf_len;
/* 设置响应行数,由 `at_create_resp()` 函数设置
* == 0:收到'OK'或'ERROR'时,响应数据将自动返回
* != 0:收到设置行数数据后返回响应数据 */
uint16_t line_num;
/* 收到的响应行数 */
uint16_t line_counts;
/* 最大响应时间 */
uint32_t 超时;
};
typedef struct at_response *at_response_t;
结构 at_client;
/* URC(Unsolicited Result Code) 对象,如:'RING', 'READY' AT 服务器请求 */
结构 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;
结构 at_urc_table
{
size_t urc_size;
常量结构 at_urc *urc;
};
typedef struct at_urc *at_urc_table_t;
结构 at_client
{
at_status_t 状态;
char end_sign;
/* 当前接收到的一行数据缓冲区 */
字符 *recv_line_buf;
/* 当前接收到的一行数据的长度 */
uint16_t recv_line_len;
/* 最大支持接收一行数据长度 */
uint16_t recv_line_size;
xSemaphoreHandle rx_notice;
xSemaphoreHandle 锁;
at_response_t 响应;
xSemaphoreHandle resp_notice;
at_resp_status_t 响应状态;
结构 at_urc_table *urc_table;
uint16_t urc_table_size;
/* uart 接收队列 */
结构数组队列*recv_q;
/* 支持的最大接收数据长度 */
uint16_t recv_queue_size;
/* uart 接收 uart */
UART_INDEX_E UART_index;
/* 处理任务 */
TaskHandle_t 解析器;
};
typedef struct at_client *at_client_t;
API
/* 获取 AT 客户端对象 */
at_client_t at_client_get_first(void);
/* AT客户端初始化并启动*/
at_client_t at_client_init(UART_INDEX_E uart_index, uint16_t recv_line_size, uint16_t recv_queue_size);
/* AT 客户端发送或接收数据 */
int at_client_obj_send(at_client_t 客户端,char *buf,int 大小);
int at_client_obj_recv(at_client_t 客户端,char *buf,int 大小,uint32_t 超时);
/* AT 客户端发送命令到 AT 服务器和等待响应 */
int at_obj_exec_cmd(at_client_t 客户端,at_response_t 响应,const char *cmd_expr,...);
/* 为 AT 客户端设置行结束符 */
无效 at_obj_set_end_sign(at_client_t 客户端,字符 ch);
/* 设置 URC(Unsolicited Result Code) 表 */
int at_obj_set_urc_table(at_client_t client, const struct at_urc * table, int size);
/* AT 响应对象的创建和删除 */
at_response_t at_create_resp(uint16_t buf_size, uint16_t line_num, uint32_t timeout);
无效 at_delete_resp(at_response_t 响应);
at_response_t at_resp_set_info(at_response_t 响应,int buf_size,int line_num,uint32_t 超时);
/* AT 响应行缓冲区获取和解析响应缓冲区参数 */
//获取指定行号的响应数据
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_init 会自动创建解析器线程,解析内部线程时实现的响应数据接收、请求数据解析、URC数据处理等整个命令数据处理API。
发送数据时的数据流向为:用户线程发送数据 at_exec_cmd 数据 ---> 串口 ---> 在服务器
接收数据时的流为:在服务器 ---> 向数据量接收,然后发送 rx_notice 信号量 ---> parser 线程解析,然后发送 resp_notice 信号量
在客户端处理数据接收处理
时,需要在接收设备时,这样对接收到数据时,应调用该功能,在该功能中接收到的数据存入处理,接收到的数据存入客户中,然后发送信号在客户处理线程进行处理量驱动通知
:
类型定义枚举
{
UART1_INDEX,
UART2_INDEX,
UART3_INDEX,
UART_INDEX_ALL
}UART_INDEX_E;
/* STM32 uart 驱动类 */
结构 stm32_uart
{
常量字符*名称;
USART_TypeDef *实例;
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)) != 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);
|