历史上的今天
返回首页

历史上的今天

今天是: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 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_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;

}

}


推荐阅读

史海拾趣

Altonics公司的发展小趣事

Altonics公司创始人在XXXX年创建了这家公司,初期面临着资金短缺、市场竞争激烈和技术人才缺乏的困境。然而,创始人凭借对电子技术的深刻理解和敏锐的市场洞察力,决定专注于工业自动化控制领域的研发与生产。经过不懈的努力,公司成功开发出一款高性能的自动化控制设备,受到了市场的广泛认可,为公司的后续发展奠定了坚实的基础。

ATM [Advanced Technical Materials]公司的发展小趣事

然而,随着ATM的普及,安全问题也日益凸显。黑客攻击、恶意软件植入等事件时有发生,给ATM的安全运营带来了挑战。为了应对这些挑战,ATM行业加强了技术研发和安全防护,采用更加先进的加密技术和安全机制,确保用户的资金安全。

以上五个故事虽然不直接涉及ATM公司的具体发展,但展示了ATM技术在电子行业中的发展历程和重要事件。这些故事反映了ATM从诞生到广泛应用,再到技术创新和安全挑战的整个过程,也展示了电子行业在推动ATM发展中的重要作用。

AITSEMI公司的发展小趣事

AITSEMI公司成立于XXXX年,创立之初便以研发高性能模拟与混合信号IC为核心目标。面对当时市场上对高性能、高性价比集成电路的迫切需求,AITSEMI团队凭借深厚的技术积累和敏锐的市场洞察力,成功开发出了一系列具有竞争力的产品,为公司的初步发展奠定了坚实的基础。

台湾三礼(3L)公司的发展小趣事

近年来,随着环保意识的提升和绿色能源的普及,三礼公司也积极投身于绿色生产领域。2019年,公司在广西南宁开始建设新厂,预定投资3亿元人民币用于厂房及自动化设备的建设。新厂总建筑面积达70,000平方米,将成为公司目前所有生产基地中规模最大的一个。同时,公司还计划全面架设太阳能发电系统,目标是在未来三年内承担公司总需求电量的30%。这一举措不仅有助于降低公司的生产成本,还体现了公司对环保事业的积极贡献。

这五个故事只是三礼公司发展历程中的一部分缩影,但它们却生动地展现了公司在电子行业中的成长轨迹和不懈努力。从北美市场的拓展到中国内地生产能力的扩大,再到技术突破和新产品开发,三礼公司始终保持着敏锐的市场洞察力和强大的创新能力。同时,公司还积极投身于绿色生产领域,为推动电子行业的可持续发展做出了积极贡献。

Baneasa SA公司的发展小趣事

随着产品的逐渐成熟,Baneasa SA开始积极寻求市场拓展的机会。公司通过与各大电子设备制造商建立合作伙伴关系,成功将其产品应用于手机、电脑、家电等多个领域。这些合作伙伴关系的建立不仅为Baneasa SA带来了稳定的订单量,还进一步提升了公司的品牌知名度和市场份额。

GSME Electronics公司的发展小趣事

随着生产设备的引进和技术团队的壮大,GSME Electronics开始专注于半导体器件的研发与生产。公司产品线逐渐丰富,涵盖了片式各种系列表面贴装的小信号三极管、中功率驱动三极管、肖特基、开关二极管及小功率可控硅等多种半导体分立器件。这些产品广泛应用于移动通信、计算机、消费类电子信息产品、家用电器、工业自动化控制设备等领域,满足了市场的多样化需求,推动了公司业务的快速增长。

问答坊 | AI 解惑

数字电视机顶盒(STB)开发学习平台

本帖最后由 jameswangsynnex 于 2015-3-3 19:58 编辑 数字电视机顶盒(STB)开发学习平台 此平台包括一台高品质的成熟的dvb-s接收机和完全开放的源代码、电原理图和PCB图,广大dvb爱好者和单片机爱好者可以用他来学习和了解DVB的软件和硬件结构及 ...…

查看全部问答>

------------------------这个话筒该怎么接?---------------

怎样让话筒的声音输入到录音机中呢?实现一个麦克风的功能。 …

查看全部问答>

兼职:寻找有经验人编写ARM 、PIC单片机开发类图书

我们现有关于PIC单片机、ARM应用开发的2本图书选题,诚找有写书意向的作者合作。要求作者实战经验和写作资源丰富,有一定的业余时间、能够保证写作进度。有意者请直接发信,写明自己最擅长的领域。 联系方式:haohong99@163.com、478057587(Q ...…

查看全部问答>

基于Windows CE的手机信号测试系统的设计和实现.pdf

基于Windows CE的手机信号测试系统的设计和实现.pdf…

查看全部问答>

romimage.exe执行ce.bib时报访存错误?

我在命令行方式下,用romimage.exe ce.bib 生成nk,执行到 compacting bin file 时报指令访问了0x0内存. 而在PB下makeimg却正常。哪位大侠知道什么原因? …

查看全部问答>

activesync同步导致应用软件线程卡住

    如题,当应用程序在运行的时候,通过usb和电脑同步,发现应用软件某些线程停在sleep。当断开usb连接后,线程有时能继续进行,有时不能(这个相当懊恼),写了个简单软件,里面就开一个线程,做如上测试,照样发现问题(排除程序复杂 ...…

查看全部问答>

VHDL编程问题

编辑了一段程序,却无法下载到FPGA开发板中(xc2s50)晶振40m,错误是:Signal qmh cannot be synthesized, bad synchronous description.请高人指点主要是数字钟用数码管显示,用4*4矩阵按键中的两个键去控制数字钟的调时功能,当按下按键key—cod ...…

查看全部问答>

急求定时器定时0.5us解决方案

我用的STM32103系列,外部是25M,定时设置后,出不来0.5us,最快也是2us.请各位DX给看看,代码如下   TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure; //  TIM_OCInitTypeDef  TIM_OCInitStructure ...…

查看全部问答>

关于ADC12IFG为0的问题

同志们,最近做一个项目,用的是MSP430F149,用AD进行数据采集,我用的是查询功能,发现如下问题: 程序运行起来后,一直是正常的,忽然有天发现AD采集不对了,后来在线调试程序,运行若干天后再次出现问题, 问题原因是ADC12IFG始终为0了。系统 ...…

查看全部问答>

有没有做过数字PID的高手?

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 ...…

查看全部问答>