BES机盒通讯模块实现
现在市面上的耳机大部分机盒通讯是通过单线串口实现双耳和充电盒进行数据交流的,
通讯逻辑初始化
- SDK在communication_init()中会创建一个处理机盒通讯的线程
if (command_mempool == NULL){
command_mempool = osPoolCreate(osPool(command_mempool));
}
if (communication_mailbox == NULL){
communication_mailbox = osMailCreate(osMailQ(communication_mailbox), NULL);
} //创建邮箱
if (communication_tid == NULL){
communication_tid = osThreadCreate(osThread(communication_thread), NULL);
} //创建处理线程
if (rx_command_block_p == NULL){
communication_command_block_alloc(&rx_command_block_p);
memset(rx_command_block_p->cmd_buf, 0, COMMAND_LEN_MAX);
rx_command_block_p->cmd_len = 0;
}
当有相关的邮箱时会执行对应的通讯功能
机盒通讯邮箱的功能有
“COMMUNICATION_MSG_TX_REQ”, //需要发送数据的消息
“COMMUNICATION_MSG_TX_DONE”, //发送完成的消息
“COMMUNICATION_MSG_RX_REQ”, //需要接收数据的消息
“COMMUNICATION_MSG_RX_DONE”, //接收数据完成的消息
“COMMUNICATION_MSG_INIT”, //需要初始化的消息
static void communication_thread(void const *argument)
{
while(1){
COMMUNICATION_MAIL *mail_p = NULL;
if (!communication_mailbox_get(&mail_p)){
communication_process(mail_p);
communication_mailbox_free(mail_p);
}
}
}
同时会加载初始化的邮箱给到线程处理, “COMMUNICATION_MSG_INIT”,
msg.message = COMMUNICATION_MSG_INIT;
communication_mailbox_put(&msg);
communication_command_block_alloc(&cmd_blk);
cmd_blk->cmd_buf[0] = 0xff;
cmd_blk->cmd_len = 1;
communication_send_command(cmd_blk);
初始化还需要注册接收完成回调函数和发送完成回调函数, 就是有接收到通讯数据或者完成数据发送时会调用这个函数(在COMMUNICATION_MSG_RX_DONE和COMMUNICATION_MSG_TX_DONE消息处理中会调用)
int communication_receive_register_callback(communication_receive_func_typedef p)
{
if(p == NULL)
return -1;
communication_receive_cb = p;
TRACE(1,"[%s] register receive callback success\n", __func__);
return 0;
}
int communication_send_complete_register_callback(communication_send_complete_typedef p)
{
if(p==NULL)
return -1;
communication_send_complete_cb = p;
TRACE(1, "[%s] register send complete callback success.\n", __func__);
return 0;
}
还有一个是通讯逻辑初始化执行的就是处理COMMUNICATION_MSG_INIT消息
COMMUNICATION_MSG_INIT消息中就是做通讯UI的初始化
SDK中串口使用的是DMA收发数据的, 当DMA收发完成后会调用这uart_rx_dma_handler, uart_tx_dma_handler两个函数, 进而加载COMMUNICATION_MSG_TX_DONE,和 COMMUNICATION_MSG_RX_DONE消息
默认DMA接收完成后会调用COMMUNICATION_MSG_RX_DONE消息
case COMMUNICATION_MSG_INIT:
uart_init();
communication_io_mode_init(); //一般把uart先设置成RX模式
uart_rx_dma_start();
break;
通讯逻辑RX
串口的RX模式由UI设置, 可以通过调用uart_rx_edge_detect_handler,将串口设置成RX模式
SDK中串口使用的是DMA收到一包数据时会调用uart_rx_dma_handler函数, 会创建一个COMMUNICATION_MSG_RX_DONE消息, 进而调用自己的RX处理函数
static void uart_rx_edge_detect_handler(enum HAL_GPIO_PIN_T pin)
{
COMMUNICATION_MAIL msg = {0};
TRACE(1,"[%s] enter...", __func__);
//disable led2 pin external interrupt mode...
communication_io_mode_switch(COMMUNICATION_MODE_DISABLE_IRQ);
//post uart rx request...
msg.message = COMMUNICATION_MSG_RX_REQ;
communication_mailbox_put(&msg);
}
case COMMUNICATION_MSG_RX_REQ:
communication_io_mode_switch(COMMUNICATION_MODE_RX);
uart_rx_dma_start(); //等待接收完成
break;
case COMMUNICATION_MSG_RX_DONE:
if(communication_receive_cb != NULL)
(*communication_receive_cb)(rx_command_block_p->cmd_buf, mail_p->parms2); //RX数据处理
memset(rx_command_block_p->cmd_buf, 0, mail_p->parms2);
break;
通讯逻辑TX
串口的TX模式由UI设置, 可以通过调用communication_send_buf函数, 将发送的BUF数据装进malloc的cmd_blk->cmd_buf中, 生成COMMUNICATION_MSG_TX_REQ消息. , 通过处理COMMUNICATION_MSG_TX_REQ消息将机盒数据发给充电盒, DMA发送完成后会生成COMMUNICATION_MSG_TX_DONE消息.
int communication_send_buf(uint8_t * buf, uint8_t len)
{
COMMAND_BLOCK *cmd_blk;
communication_command_block_alloc(&cmd_blk);
memcpy(cmd_blk->cmd_buf, buf, len);
cmd_blk->cmd_len = len;
communication_send_command(cmd_blk);
return 0;
}
void communication_send_command(COMMAND_BLOCK *cmd_blk)
{
COMMUNICATION_MAIL mail;
memset(&mail, 0, sizeof(mail));
ASSERT(cmd_blk->cmd_len <=COMMAND_LEN_MAX, "%s len error", __func__);
mail.message = COMMUNICATION_MSG_TX_REQ;
mail.parms2 = (uint32_t)cmd_blk;
communication_mailbox_put(&mail);
}
case COMMUNICATION_MSG_TX_REQ:
command_block_p = (COMMAND_BLOCK *)mail_p->parms2;
uart_rx_dma_stop();
communication_io_mode_switch(COMMUNICATION_MODE_TX);
hal_uart_dma_send(comm_uart, command_block_p->cmd_buf, command_block_p->cmd_len, NULL, NULL); //会将数据放进DMA,
break;
case COMMUNICATION_MSG_TX_DONE:
command_block_p = (COMMAND_BLOCK *)mail_p->parms2;
communication_command_block_free(command_block_p); //释放发送缓冲内存
if(communication_send_complete_cb != NULL)
(*communication_send_complete_cb)(); //处理发送完成的UI
communication_io_mode_switch(COMMUNICATION_MODE_RX);
break;