概要
最近做了个基于STM32F429的cdc和uart相互透传的小工程,原以为是个十分简单的事情,但在开发中遇到了些小问题,感觉有必要写一篇记录一下。
整体架构流程
- usb-cdc收到不定长数据包后由uart-dma发出
- uart-dma收到不定长数据包后由usb-cdc发出
- usb-cdc能设置uart的参数
技术细节
-
cubemx设置
-
时钟配置
-
串口设置
-
这里我将DMA接收设置为循环模式,设置为普通模式也是可以的;DMA发送设置为普通模式
-
因为后面要用DMA+IDLE(空闲中断)的方式,在参考其他博客时看到将接收引脚设为上拉能有效防止误触发空闲中断。这里无从考证,暂且记录下来学着做
-
USB设置
-
这里直接用cubemx的usb协议栈将usb设置为cdc
-
中断优先级设置
-
设置合理的中断优先级,保证系统时钟能正常运行,之前就遇到过HAL_DELAY卡死的现象,找了一圈后发现是有东西抢了中断响应,将系统时钟中断优先级抬高即可
-
代码解析
-
main.c里使能IDLE空闲中断和DMA接收
uint8_t uart_dma_rbuf[2048] = {0};
int main(void)
{
...
__HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);//使能串UART2 IDLE中断
HAL_UART_Receive_DMA(&huart1,(uint8_t *)uart_dma_rbuf, sizeof(uart_dma_rbuf));//开启USART1的DMA接收
...
while(1)
{
...
}
}
- stm32f4xx_it.c里添加DMA接收
void USART1_IRQHandler(void)
{
/* USER CODE BEGIN USART1_IRQn 0 */
uint16_t tmp_flag = 0;
uint16_t temp = 0;
tmp_flag = __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE);
if(tmp_flag != RESET)//IDLE标志被置位
{
__HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位
HAL_UART_DMAStop(&huart1);//关闭串口的DMA
temp = sizeof(uart_dma_rbuf) - __HAL_DMA_GET_COUNTER(&hdma_usart1_rx);//接收的字节 = 总字节 - 剩余的字节
CDC_Transmit_FS(uart_dma_rbuf,temp);
//HAL_UART_Transmit_DMA(&huart1,(uint8_t *)uart_dma_rbuf,temp);//测试
HAL_UART_Receive_DMA(&huart1,(uint8_t *)uart_dma_rbuf, sizeof(uart_dma_rbuf));//因为上面关闭了串口的DMA,所以要重新开启DMA接收
}
/* USER CODE END USART1_IRQn 0 */
HAL_UART_IRQHandler(&huart1);
/* USER CODE BEGIN USART1_IRQn 1 */
/* USER CODE END USART1_IRQn 1 */
}
- usb-cdc接收
- 因为FS(USB全速)每包接收64字节,当数据超过64字节时会分包接收,所以想用接收空闲的方式来接收不定长的一包数据,利用systick中断来进行倒计时,
uint8_t usb_rec_flag = 0;//cdc接收标志
uint8_t usb_rdata[2048] = {0};//cdc接收缓存区
uint32_t usb_rec_num = 0;//一包数据的长度
uint8_t usb_rec_idle_time = 0;//接收空闲计时
static int8_t CDC_Receive_FS(uint8_t* Buf, uint32_t *Len)
{
/* USER CODE BEGIN 6 */
//CDC_Transmit_FS(Buf, *Len);
memcpy(&usb_rdata[usb_rec_num],Buf,*Len);//拷贝接收数据到接收数据区
usb_rec_num+=*Len;//累加接收数据计数
usb_rec_idle_time = 3;//设定空闲定时
usb_rec_flag = 1;//将接收状态置1
USBD_CDC_SetRxBuffer(&hUsbDeviceFS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceFS);
return (USBD_OK);
/* USER CODE END 6 */
}
- cdc接收空闲计时,并添加到systick中断里运行
extern uint8_t usb_rec_flag;
extern uint8_t usb_rdata[2048];
extern uint32_t usb_rec_num;
extern uint8_t usb_rec_idle_time;
void usb_rec_idle_tick(void)
{
if(usb_rec_idle_time != 0)
{
usb_rec_idle_time--; //空闲计时
}
if((usb_rec_idle_time == 0)&&(usb_rec_flag == 1)) //判断为USB接收空闲并且处在接收状态时发送数据
{
HAL_UART_Transmit_DMA(&huart1, (uint8_t *)usb_rdata, usb_rec_num);//将接收到的数据发出去
//CDC_Transmit_FS(usb_rdata,usb_rec_num);//测试
usb_rec_num = 0;//USB接收计数清零
usb_rec_flag = 0;//USB接收标记清零
}
}
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
usb_rec_idle_tick();
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
- 用usb设置uart参数,在usbd-cdc_if.c里的CDC_Control_FS中添加
static int8_t CDC_Control_FS(uint8_t cmd, uint8_t* pbuf, uint16_t length)
{
/* USER CODE BEGIN 5 */
switch(cmd)
{
case CDC_SEND_ENCAPSULATED_COMMAND:
break;
case CDC_GET_ENCAPSULATED_RESPONSE:
break;
case CDC_SET_COMM_FEATURE:
break;
case CDC_GET_COMM_FEATURE:
break;
case CDC_CLEAR_COMM_FEATURE:
break;
/*******************************************************************************/
/* Line Coding Structure */
/*-----------------------------------------------------------------------------*/
/* Offset | Field | Size | Value | Description */
/* 0 | dwDTERate | 4 | Number |Data terminal rate, in bits per second*/
/* 4 | bCharFormat | 1 | Number | Stop bits */
/* 0 - 1 Stop bit */
/* 1 - 1.5 Stop bits */
/* 2 - 2 Stop bits */
/* 5 | bParityType | 1 | Number | Parity */
/* 0 - None */
/* 1 - Odd */
/* 2 - Even */
/* 3 - Mark */
/* 4 - Space */
/* 6 | bDataBits | 1 | Number Data bits (5, 6, 7, 8 or 16). */
/*******************************************************************************/
case CDC_SET_LINE_CODING:
huart1.Init.BaudRate = (uint32_t)(pbuf[0] | (pbuf[1] << 8) | (pbuf[2] << 16) | (pbuf[3] << 24));
HAL_UART_Init(&huart1);
break;
case CDC_GET_LINE_CODING:
break;
case CDC_SET_CONTROL_LINE_STATE:
break;
case CDC_SEND_BREAK:
break;
default:
break;
}
return (USBD_OK);
/* USER CODE END 5 */
}
小结
- 开两个上位机,两边相互收发多行字符串没有问题,设计的功能正常
- 这里只是提供一种思路,并不太适用快速的串口通信,测试发现发送频率在100ms以下时会丢包严重
- 工程代码:USB-UART不定长度数据透传