完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
经过一段时间的学习,总结IDE环境的一些使用方法,纯属个人想法,多有不足,共享以讨论,望指正。
IDE的串口实现很简单,通过在界面中勾选串口,以及相应的DMA、中断即可完成串口的驱动层配置,使用HAL_UART_Transmit_DMA函数和HAL_UART_Receive_DMA函数即可完成对串口数据的收发。 但是程序的实现有些问题。在发送过程中,程序不能在任意情况下发送数据,需要等待上一次数据发送完成才能进行下一次发送(废话)。在接收过程中,HAL_UART_Receive_DMA函数必须指定接收数据的长度,当接收端不知道接收数据情况时,当收到的数据未能达到接收数据长度时,会造成始终等待而无法处理数据,做不到实时数据处理。 为解决以上问题,在借助freertos系统的情况下,接收数据实时处理,发送数据可在程序的任何地方完成。 发送程序处理思路: 程序部分: void SendData(unsigned char *data, unsigned int length) { osStatus status; unsigned char *p, *pSend_data; pTXBuffer pTx; unsigned int l; pSend_data = data; while(length) { //_UART_BUFFER_ 串口DMA buffer大小,数据长度超过buffer大小则自动分包 l = (length > _UART_BUFFER_)?_UART_BUFFER_:length; p = get_memory(sizeof(TXBuffer) + l);//申请内存 if(p) { pTx = (pTXBuffer)p; pTx->MsgLength = l; pTx->pBuf = p + sizeof(TXBuffer); memcpy(pTx->pBuf, pSend_data, l);//保存需要发送的数据 status = osMessagePut(UARTSendQueueHandle, (uint32_t)p, 0);//数据指针加入队列 if(status != osOK) { free_memory(p);//加入队列失败,释放内存 } else ChipUartSend();//调用发送函数 } else { } } } unsigned char uart3_send_buffer[_UART_BUFFER_] = {0};//DMA缓冲区 unsigned short uart3_length = 0;//记录发送数据长度,同时充当发送完成标志位 //该函数即会在中断中调用也会在任务中调用 void ChipUartSend(void) { osEvent evt; pTXBuffer pTx; if(uart_length == 0) { evt = osMessageGet(UARTSendQueueHandle, 0);//从队列中获取消息 if(evt.status == osEventMessage)//消息有效 { pTx = (pTXBuffer)evt.value.p; //注意发送函数中的数据指针会直接作为dma地址,因此需要将数据拷贝出来,以防函数结束造成内存问题 memcpy(uart3_send_buffer, pTx->pBuf, pTx->MsgLength); uart_length = pTx->MsgLength; HAL_UART_Transmit_DMA(&huart, uart3_send_buffer, uart3_length); free_memory(evt.value.p);//无法发送是否正确均释放内存 } } } //该函数会在中断中调用 void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart) { if(huart == &huart1) { uart_length = 0;//清除标志位 ChipUartSend(); } } 接收程序比较麻烦,即需要解决收到数据即触发中断又用到dma以降低CPU消耗,又需要解决接收到数据长度大于HAL_UART_Receive_DMA函数给出的长度造成的数据丢失。 stm32芯片提供了很好的中断解决DMA下的数据实时接收问题,即UART_IT_IDLE中断,该中断在一包数据接收完成后触发,但是找遍了整个可函数均为发现对该中断的处理,只好自己实现。为不对库函数进行修改(免得每次生成代码时导致程序覆盖),通过串口中断回调函数处理。处理过程中需要停止DMA中断,以便置位库函数状态并重新调用HAL_UART_Receive_DMA函数开始新数据的接收,但是库函数提供的HAL_StatusTypeDef HAL_UART_DMAStop(UART_HandleTypeDef *huart)同时停止了发送DMA,会对发送造成影响,因此重写了对DMA的接收停止函数。 当接收数据长度大于buffer长度时,需要调用接收回调函数将数据接收并重新开启新的接收过程。 当所有数据接收完成时,在任务中处理就可以了。 HAL_StatusTypeDef HAL_UART_RX_DMAStop(UART_HandleTypeDef *huart) { uint32_t dmarequest = 0x00U; /* Stop UART DMA Rx request if ongoing */ dmarequest = HAL_IS_BIT_SET(huart->Instance->CR3, USART_CR3_DMAR); if ((huart->RxState == HAL_UART_STATE_BUSY_RX) && dmarequest) { CLEAR_BIT(huart->Instance->CR3, USART_CR3_DMAR); /* Abort the UART DMA Rx stream */ if (huart->hdmarx != NULL) { HAL_DMA_Abort(huart->hdmarx); } /* Disable RXNE, PE and ERR (Frame error, noise error, overrun error) interrupts */ CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE)); CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE); /* At end of Rx process, restore huart->RxState to Ready */ huart->RxState = HAL_UART_STATE_READY; } return HAL_OK; } unsigned char g_rev_uart1_buffer[_UART_BUFFER_] = {0};//数据接收dma buffer //中断回调函数 void L_User_UART_Irq(void) { osStatus status; unsigned int rx_len = 0; unsigned int temp; if(__HAL_UART_GET_FLAG(&huart1, UART_FLAG_IDLE) == SET) //获取IDLE标志位 { __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除标志位 temp = huart1.Instance->SR; //清除状态寄存器SR,读取SR寄存器可以实现清除SR寄存器的功能 temp = huart1.Instance->DR; //读取数据寄存器中的数据 HAL_UART_RX_DMAStop(&huart1);//停止DMA rx_len = sizeof(g_rev_uart1_buffer) - hdma_usart1_rx.Instance->NDTR; //总计数减去未传输的数据个数,得到已经接收的数据个数 if(rx_len) { unsigned char *p; pRXBuffer pRx; p = get_memory(sizeof(RXBuffer) + rx_len);//获取内存 if(p) { pRx = (pRXBuffer)p; pRx->MsgLength = rx_len; pRx->pBuf = p + sizeof(RXBuffer); memcpy(pRx->pBuf, g_rev_uart1_buffer, rx_len);//拷贝收到的数据 status = osMessagePut(ReceiveGSMQueueHandle, (uint32_t)p, 0);//将数据发送到队列中 if(status != osOK) { free_memory(p); } } } HAL_UART_Receive_DMA(&huart1, g_rev_uart1_buffer, sizeof(g_rev_uart1_buffer));//重新开启数据接收 } //接收回调,处理过程与中断处理相同 void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { unsigned char *p; pRXBuffer pRx; unsigned int rx_len = 0; osStatus status; if(huart == &huart1) { HAL_UART_RX_DMAStop(&huart1); //停止DMA rx_len = sizeof(g_rev_uart1_buffer) - hdma_usart1_rx.Instance->NDTR;//可不计算,长度即为buffer长度 p = get_memory(sizeof(RXBuffer) + rx_len); if(p) { pRx = (pRXBuffer)p; pRx->MsgLength = rx_len; pRx->pBuf = p + sizeof(RXBuffer); memcpy(pRx->pBuf, g_rev_uart1_buffer, rx_len); status = osMessagePut(ReceiveGSMQueueHandle, (uint32_t)p, 0); if(status != osOK) { free_memory(p); } } HAL_UART_Receive_DMA(&huart1, g_rev_uart1_buffer, sizeof(g_rev_uart1_buffer)); } } 程序开始需要调用以下两句话,确保中断和DMA均开启,虽然我看可函数默认开启了所有中断。 HAL_UART_Receive_DMA(&huart3, g_rev_uart3_buffer, sizeof(g_rev_uart3_buffer)); __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);//使能idle中断 测试采用循环发送进行,上位机发送一个大于DMA接收buffer长度的数据,并定时发送(1ms),下位机收到数据后原封不动返回上位机,检查上位机数据收发数量是否一致。经过长时间测试,不会丢失任何数据,稳定可靠。 |
|
|
|
只有小组成员才能发言,加入小组>>
调试STM32H750的FMC总线读写PSRAM遇到的问题求解?
1117 浏览 1 评论
X-NUCLEO-IHM08M1板文档中输出电流为15Arms,15Arms是怎么得出来的呢?
1143 浏览 1 评论
575 浏览 2 评论
STM32F030F4 HSI时钟温度测试过不去是怎么回事?
430 浏览 2 评论
ST25R3916能否对ISO15693的标签芯片进行分区域写密码?
1032 浏览 2 评论
1622浏览 9评论
STM32仿真器是选择ST-LINK还是选择J-LINK?各有什么优势啊?
285浏览 4评论
STM32F0_TIM2输出pwm2后OLED变暗或者系统重启是怎么回事?
295浏览 3评论
280浏览 3评论
stm32cubemx生成mdk-arm v4项目文件无法打开是什么原因导致的?
259浏览 3评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-8-21 16:07 , Processed in 0.899502 second(s), Total 77, Slave 61 queries .
Powered by 电子发烧友网
© 2015 www.ws-dc.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号