datasheet

12-HAL库串口通信总结

2019-08-16来源: eefocus关键字:HAL库  串口通信  传输方式

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

flag=1;

}

 

 

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

[1] [2]

关键字:HAL库  串口通信  传输方式

编辑:什么鱼 引用地址:http://news.eeworld.com.cn/mcu/ic471325.html
本网站转载的所有的文章、图片、音频视频文件等资料的版权归版权所有人所有,本站采用的非本站原创文章及图片等内容无法一一联系确认版权者。如果本网所选内容的文章作者及编辑认为其作品不宜公开自由传播,或不应无偿使用,请及时通过电子邮件或电话通知我们,以迅速采取适当措施,避免给双方造成不必要的经济损失。

上一篇:11-STM32F746之存储总结
下一篇:13-HAL库DMA系统总结

关注eeworld公众号 快捷获取更多信息
关注eeworld公众号
快捷获取更多信息
关注eeworld服务号 享受更多官方福利
关注eeworld服务号
享受更多官方福利

推荐阅读

16.HAL库之SPI和QSPI

沿进行数据变化。DDR模式:在该模式下,指令阶段在SCLK下降沿发送数据,而在地址,交替字节,数据阶段在SCLK上升沿和下降沿均发送数据。双闪存模式:使用两个外部SPI四线,可将flash扩大一倍。QSPI配置:时钟使能,管脚定义,QSPI配置,flash初始化,QSPI读/写函数定义。void Qspi_Config(){ __HAL_RCC_QSPI_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); __HAL_RCC_GPIOF_CLK_ENABLE(); Flash_GPIO.Pin=GPIO_PIN_2; Flash_GPIO.Mode=GPIO_MODE_AF_PP
发表于 2019-08-16
16.HAL库之SPI和QSPI

HAL库之485+DMA通信(STM32F746)

(uint32_t bound){  __HAL_RCC_GPIOD_CLK_ENABLE();  __HAL_RCC_USART2_CLK_ENABLE();   GPIO_InitTypeDef GPIO_Init2;   GPIO_Init2.Pin = GPIO_PIN_4;              //485_RTS  GPIO_Init2.Mode =GPIO_MODE_OUTPUT_PP;  GPIO_Init2.Pull = GPIO_NOPULL;  GPIO_Init2.Speed
发表于 2019-08-16
HAL库之485+DMA通信(STM32F746)

15-HAL库之定时器学习

RepetitionCounter;   //重复计数,高级控制定时器所用   uint32_t AutoReloadPreload;   //自动预装载值}TIM_Base_InitTypeDef;应用例子如下:#include "basic_tim.h"TIM_HandleTypeDef Basic_Tim6;void Basic_Tim_Config(void){ HAL_TIM_Base_MspInit(&Basic_Tim6); __HAL_RCC_TIM6_CLK_ENABLE(); Basic_Tim6.Instance=TIM6
发表于 2019-08-16
15-HAL库之定时器学习

14-HAL库之I2C通信

发送停止传输信号P。读数据时,主机向从机发应答信号,当主机希望停止接收数据时,发送非应答信号。起始信号和停止信号见上图。数据有效性见上图。2.基于HAL库的I2C配置步骤:时钟使能—GPIO管脚配置—I2C配置初始化:void MyI2C_Init(void){ __HAL_RCC_GPIOB_CLK_ENABLE(); GPIO_InitTypeDef GPIO_InitStruct; GPIO_InitStruct.Pin = GPIO_PIN_6;  GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;  GPIO_InitStruct.Pull
发表于 2019-08-16
14-HAL库之I2C通信

13-HAL库DMA系统总结

。2.DMA相关函数解析关于DMA,ST官方提供了HAL库(封装函数)和LL库(直接操作寄存器)。和GPIO定义类似,需要先使能相关时钟,定义句柄结构体DMA_HandleTypeDef mydma2,随后进行DMA的各项配置,最后进行初始化。typedef struct __DMA_HandleTypeDef{    DMA_Stream_TypeDef          *Instance;                       
发表于 2019-08-16
13-HAL库DMA系统总结

7.STM32F407ZG串口通信配置流程

步骤:1.时钟使能:GPIO时钟使能,串口时钟使能。    RCC_AHB1PeriphResetCmd(RCC_AHB1Periph_GPIOA, ENABLE);    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);2.端口配置:GPIO引脚配置,复用模式。    GPIO_InitTypeDef GPIO_Struct;     GPIO_Struct.GPIO_Pin=GPIO_Pin_9;    GPIO_Struct.GPIO_Mode
发表于 2019-08-16

小广播

何立民专栏

单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号 电信业务审批[2006]字第258号函 京公海网安备110108001534 Copyright © 2005-2019 EEWORLD.com.cn, Inc. All rights reserved