历史上的今天
今天是:2025年08月16日(星期六)
2019年08月16日 | 12-HAL库串口通信总结
2019-08-16 来源:eefocus
1.定义了三种传输方式:阻塞传输,中断传输、DMA传输
HAL_UART_Transmit; HAL_UART_Receive
HAL_UART_Transmit_IT; HAL_UART_Receive_IT
HAL_UART_Transmit_DMA; HAL_UART_Receive_DMA
此外还定义了两个中断回调函数,供中断和DMA使用,分别在数据传输一半和完成时使用
voidHAL_UART_TxCpltCallback(UART_HandleTypeDef *huart);
void HAL_UART_TxHalfCpltCallback(UART_HandleTypeDef*huart);
voidHAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
voidHAL_UART_RxHalfCpltCallback(UART_HandleTypeDef *huart);
2.阻塞传输
阻塞传输是调用这个函数并在等待时间内一直等待操作完成。
uint8_t aTxbuffer[]="enter 10 characters:n";
uint8_t aRxBuffer;
uint8_t Usart1_RxBuff[10];
uint8_t Usart1_Rx_Cnt = 0;
int main(void)
{
HAL_Init();
Sysclk_config();
USART1_UART_Init(19200);
printf("input your string:n");
HAL_UART_Transmit(&huart1 ,(uint8_t*)aTxbuffer,sizeof(aTxbuffer),0xFFF);
HAL_UART_Receive(&huart1,(uint8_t*)Usart1_RxBuff,10,10);
HAL_UART_Transmit(&huart1 ,(uint8_t*)Usart1_RxBuff,10,10);
}
可以添加循环语句,循环输入输出。
3.中断传输
配置串口,开启中断,在中断处理函数中进行输入语句的输出。
通过查看源代码,可以看到HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)这个函数只是用来开启中断用的,并不能真正接收数据。开启中断后,在中断处理函数HAL_UART_IRQHandler(&huart1)中,会先调用UART_Receive_IT(huart)函数进行数据输入的接收,此为静态全局函数,代码如下:
static HAL_StatusTypeDef UART_Receive_IT(UART_HandleTypeDef *huart)
{
uint16_t* tmp;
uint16_t uhMask = huart->Mask;
/* Check that a Rx process is ongoing */
if(huart->RxState == HAL_UART_STATE_BUSY_RX)
{
if ((huart->Init.WordLength == UART_WORDLENGTH_9B) && (huart->Init.Parity == UART_PARITY_NONE))
{
tmp = (uint16_t*) huart->pRxBuffPtr ;
*tmp = (uint16_t)(huart->Instance->RDR & uhMask);
huart->pRxBuffPtr +=2;
}
else
{
*huart->pRxBuffPtr++ = (uint8_t)(huart->Instance->RDR & (uint8_t)uhMask);
}
if(--huart->RxXferCount == 0)
{
/* Disable the UART Parity Error Interrupt and RXNE interrupt*/
CLEAR_BIT(huart->Instance->CR1, (USART_CR1_RXNEIE | USART_CR1_PEIE));
/* Disable the UART Error Interrupt: (Frame error, noise error, overrun error) */
CLEAR_BIT(huart->Instance->CR3, USART_CR3_EIE);
/* Rx process is completed, restore huart->RxState to Ready */
huart->RxState = HAL_UART_STATE_READY;
HAL_UART_RxCpltCallback(huart);
return HAL_OK;
}
return HAL_OK;
}
else
{
/* Clear RXNE interrupt flag */
__HAL_UART_SEND_REQ(huart, UART_RXDATA_FLUSH_REQUEST);
return HAL_BUSY;
}
}
可以看到该函数的作用是将接收到的数据存入结构体huart内的pRxBuffPtr指针中,当待传输数据长度RxXferCount为0时,便调用回调函数HAL_UART_RxCpltCallback(huart);因此可以考虑更改此函数的源代码,当待传输数据的字符为空格或回车时,判断为输入结束,将RxXferCount置为0,然后调用回调函数进行数据处理。
a.单字节循环接收数据
HAL_UART_Receive_IT通过设置接收缓冲区和需要接收的数据个数。当数据接收达到设定个数后引发一次中断调用回调函数HAL_UART_RxCpltCallback。由于只引发一次中断,如果需要连续接收,则需要在HAL_UART_RxCpltCallback再调用HAL_UART_Receive_IT。这种定长的接收可能并不是想要的,往往传输的数据都是不定长的,我想这需要将HAL_UART_Receive_IT长度设置为1,然后自己根据接收的数据判断。此外由于回调函数没有指明是哪个串口引发的中断,因此有必要在回调函数中做判断,如if(huart==&huart1){ }。
int main(void)
{
HAL_Init();
Sysclk_config();
USART1_UART_Init(19200);
//HAL_UART_Transmit(&huart1,aTxBuffer,sizeof(aTxBuffer),0xFFF);
HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);//开启接收中断
while(1)
{
if(flag==1)
{
HAL_UART_Transmit(&huart1,RxBuff,count,0xFF);
printf("n");
for(uint8_t i=0;i RxBuff[i]=0; }//清空数组 flag=0; count=0; HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);//重新开启接收中断 } } } void USART1_IRQHandler(void) { HAL_UART_IRQHandler(&huart1); } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { if(count if(aRxBuffer!=0x0d)//输入非回车 { flag=0; RxBuff[count]=aRxBuffer; count++; HAL_UART_Transmit(&huart1,&aRxBuffer,count,0x20); printf("n"); HAL_UART_Receive_IT(&huart1,&aRxBuffer,1);//重新开启接收中断 } else flag=1; } else flag=1; } //接收中断回调函数 b.长字节数据循环输入 设置输入缓冲数组(长度可设置100),利用单次中断,将接收到的数据元素轮流存入数组中,数据元素为0或回车时判断数据输入完成,进行数据输入完成标志位置位。 void Buffer_reset(void); int fputc(int ch, FILE *f); uint8_t aTxBuffer[]="this is a test message!n"; uint16_t flag=0; uint8_t RxBuff[LENTH]; //接收缓冲数组 uint16_t count = 0; //接收缓冲计数 uint8_t aRxBuffer[LENTH]={0}; //USART接收Buffer int main(void) { HAL_Init(); Sysclk_config(); Buffer_reset(); USART1_UART_Init(19200); HAL_UART_Receive_IT(&huart1,aRxBuffer,LENTH);//开启接收中断 while(1) { if(flag==1) { HAL_UART_Transmit(&huart1,RxBuff,count,0xFF); printf("n"); Buffer_reset(); flag=0; count=0; HAL_UART_Receive_IT(&huart1,aRxBuffer,LENTH);//重新开启接收中断 } } } void USART1_IRQHandler(void) { HAL_UART_IRQHandler(&huart1); do { RxBuff[count]=aRxBuffer[count]; printf("aRxBuffer[%d]=%dn",count,aRxBuffer[count]); count++; } while((aRxBuffer[count]!=0)&&(count } void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) { } //接收中断回调函数 void Buffer_reset(void){ for(uint8_t i=0;i RxBuff[i]=0; aRxBuffer[i]=0; }//清空数组 } 此代码暂时有问题,调试中。 4.DMA传输 DMA可以解放CPU,同时可以利用DMA+空闲中断实现任意字节的串口输入输出。 空闲中断:接收到一条完整的数据,就会产生空闲中断,同时空闲标志位置位。 串口接收中断:每接收到一个字符,就会产生一个串口接收中断。 原理:利用DMA配置,将串口读入的数据存储在DMA缓冲区的接收数组中。当检测到一帧数据(即数据输入完成)时,产生空闲中断,此时可以将所接收的数据进行处理或输出。 步骤: a.串口配置(时钟使能,引脚配置,串口配置,中断配置,使能空闲中断,串口全局中断,开启DMA接收数据) b.DMA配置(时钟使能,usart_tx和usart_rx通道配置,中断配置,关联usart和DMA通道) c.重写串口中断函数(检测到空闲中断时,清除空闲中断标志位,停止DMA传输,获取输入数据的长度,置位输入完成标志位) d.主函数处理(检测到输入完成标志位时,进行数据处理或输入,然后清空数组,清除数据长度和输入完成标志位) DMA配置: void usart_dma_init(void){ __HAL_RCC_DMA2_CLK_ENABLE(); huart1_dma_rx.Instance=DMA2_Stream2; huart1_dma_rx.Init.Channel=DMA_CHANNEL_4; huart1_dma_rx.Init.Direction=DMA_PERIPH_TO_MEMORY; huart1_dma_rx.Init.PeriphInc=DMA_PINC_DISABLE; huart1_dma_rx.Init.MemInc=DMA_MINC_ENABLE; huart1_dma_rx.Init.MemDataAlignment= DMA_MDATAALIGN_BYTE; huart1_dma_rx.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE; huart1_dma_rx.Init.Mode=DMA_NORMAL; huart1_dma_rx.Init.Priority=DMA_PRIORITY_LOW; huart1_dma_rx.Init.FIFOMode=DMA_FIFOMODE_DISABLE; HAL_DMA_Init(&huart1_dma_rx); //RX_DMA_config huart1_dma_tx.Instance=DMA2_Stream7; huart1_dma_tx.Init.Channel=DMA_CHANNEL_4; huart1_dma_tx.Init.Direction=DMA_MEMORY_TO_PERIPH; huart1_dma_tx.Init.PeriphInc=DMA_PINC_DISABLE; huart1_dma_tx.Init.MemInc=DMA_MINC_ENABLE; huart1_dma_tx.Init.MemDataAlignment= DMA_MDATAALIGN_BYTE; huart1_dma_tx.Init.PeriphDataAlignment=DMA_PDATAALIGN_BYTE; huart1_dma_tx.Init.Mode=DMA_NORMAL; huart1_dma_tx.Init.Priority=DMA_PRIORITY_HIGH; huart1_dma_tx.Init.FIFOMode=DMA_FIFOMODE_DISABLE; HAL_DMA_Init(&huart1_dma_rx); //TX_DMA_config __HAL_LINKDMA(&huart1, hdmarx, huart1_dma_rx); __HAL_LINKDMA(&huart1, hdmatx, huart1_dma_tx); //关联USART1和DMA HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 1, 1); HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn); HAL_NVIC_SetPriority(DMA2_Stream7_IRQn, 1, 1); HAL_NVIC_EnableIRQ(DMA2_Stream7_IRQn); //配置DMA通道的中断 } 中断处理函数: void USART1_IRQHandler(void) { uint32_t tmp_flag = 0; uint32_t temp; tmp_flag= __HAL_UART_GET_FLAG(&huart1,UART_FLAG_IDLE); if(tmp_flag==1)//当产生空闲中断时(及接收到一帧数据) { __HAL_UART_CLEAR_IDLEFLAG(&huart1);//清除空闲中断标志位 HAL_UART_DMAStop(&huart1); //停止串口的DMA传输 temp = __HAL_DMA_GET_COUNTER(&huart1_dma_rx);// 获取DMA中未传输的数据个数 rx_len = BUFFER_SIZE - temp; //总计数减去未传输的数据个数,即得到已经接收的数据个数 flag=1; } HAL_UART_IRQHandler(&huart1); } 5.利用定时器实现串口的不定长字节输入 当接收到第一个字符时,打开定时器。经过延时后,进入定时器中断回调函数,在该回调函数中进行数据的处理和输出。 int main(void) { HAL_Init(); Sysclk_config(); USART1_UART_Init(19200); Basic_Tim_Config(); printf("input strings:n"); num_reset(); HAL_UART_Receive(&huart1,RxBuff,LENTH,0xFFF); if(RxBuff[0]) { HAL_TIM_Base_Start_IT(&Basic_Tim6);//有数据输入的时候就开启定时器 } while(1) { if(flag) { printf("input strings again:n"); flag=0; count=0; num_reset(); HAL_UART_Receive(&huart1,RxBuff,LENTH,0xFFF); //开启串口输入 if(RxBuff[0]) { HAL_TIM_Base_Start_IT(&Basic_Tim6);//重新开启定时器 } } } } void TIM6_DAC_IRQHandler(TIM_HandleTypeDef *htim) { HAL_TIM_IRQHandler(&Basic_Tim6); } void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { HAL_TIM_Base_Stop(&Basic_Tim6); huart1.RxState = HAL_UART_STATE_READY; huart1.Lock=HAL_UNLOCKED; printf("your input:"); do { aRxBuff[count]=RxBuff[count]; count++; } while(RxBuff[count]); if((HAL_UART_Transmit(&huart1,aRxBuff,count+1,0xFFF))==HAL_OK)//串口输出 { flag=1; } } void num_reset(void) { for(uint8_t i=0;i RxBuff[i]=0; aRxBuff[i]=0; } }
下一篇:13-HAL库DMA系统总结
史海拾趣
|
本帖最后由 jameswangsynnex 于 2015-3-3 19:58 编辑 数字电视机顶盒(STB)开发学习平台 此平台包括一台高品质的成熟的dvb-s接收机和完全开放的源代码、电原理图和PCB图,广大dvb爱好者和单片机爱好者可以用他来学习和了解DVB的软件和硬件结构及 ...… 查看全部问答> |
|
我们现有关于PIC单片机、ARM应用开发的2本图书选题,诚找有写书意向的作者合作。要求作者实战经验和写作资源丰富,有一定的业余时间、能够保证写作进度。有意者请直接发信,写明自己最擅长的领域。 联系方式:haohong99@163.com、478057587(Q ...… 查看全部问答> |
|
我在命令行方式下,用romimage.exe ce.bib 生成nk,执行到 compacting bin file 时报指令访问了0x0内存. 而在PB下makeimg却正常。哪位大侠知道什么原因? … 查看全部问答> |
|
如题,当应用程序在运行的时候,通过usb和电脑同步,发现应用软件某些线程停在sleep。当断开usb连接后,线程有时能继续进行,有时不能(这个相当懊恼),写了个简单软件,里面就开一个线程,做如上测试,照样发现问题(排除程序复杂 ...… 查看全部问答> |
|
我用的STM32103系列,外部是25M,定时设置后,出不来0.5us,最快也是2us.请各位DX给看看,代码如下 TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // TIM_OCInitTypeDef TIM_OCInitStructure ...… 查看全部问答> |
|
同志们,最近做一个项目,用的是MSP430F149,用AD进行数据采集,我用的是查询功能,发现如下问题: 程序运行起来后,一直是正常的,忽然有天发现AD采集不对了,后来在线调试程序,运行若干天后再次出现问题, 问题原因是ADC12IFG始终为0了。系统 ...… 查看全部问答> |
|
matlab仿真程序如下:它的输出怎么调都是在1左右,求问大神是什么原因? clear all; close all; ts=0.001; %sys=tf(50,[0.125,7, 0]); %dsys=c2d(sys,ts,\'z\'); %[num,den]=tfdata(dsys,\'v\'); num=[0 -0.00324 0.003325]; den=[1 -1.9 ...… 查看全部问答> |




