【STM32】-串口开发经验分享-基于RTOS+空闲中断

这篇具有很好参考价值的文章主要介绍了【STM32】-串口开发经验分享-基于RTOS+空闲中断。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1. 概述    

2.串口介绍

2.1 原理框图

2.2 RS-232C

2.3 RS-422

2.4 RS-485

2.5 UART

3. STM32 USART介绍

4. CubeMx生成Uart初始化代码

4.1 NewProject选择单片机型号

4.2 设置rcc时钟

 4.3 设置Usart

4.4 初始化代码

4.5 注意

5 工程源码解析

5.1 程序架构

5.2 源码

fml_ring_buffer.c

fml_usart.c

app_usart_task.c

stm32f4xx_it.c

main.c

6. 效果

7. 源码分享

1. 概述    

        在原子阿波罗探索者开发板(芯片STM32F429XX)上实现RS232串口通信功能,工程框架为UCOSIII+串口空闲中断实现数据接收发送。

2.串口介绍

        串行接口(Serial Interface)简称串口,也称串行通信接口或串行通讯接口是指数据一位一位地顺序传送,其特点是通信线路简单,只要一对传输线就可以实现双向通信。一条信息的各位数据被逐位按顺序传送的通讯方式称为串行通讯。串行通讯的特点是:数据位的传送,按位顺序进行,根据信息的传送方向,串行通讯可以进一步分为单工、半双工和全双工三种。串行接口按电气标准及协议来分,包括TTL、RS-232、RS-422RS-422RS-422、RS485等,这4种串口只在电气信号上有差别,在帧格式,传输逻辑和软件操作上基本都是一样的。在实际项目应用中会有些差别。经常有人把RS-232、RS-422、RS-485 误称为通讯协议,这是不对的,它们仅是关于UART通讯的一个机械和电气接口标准。

2.1 原理框图

        以STM32单片机串口通信RS-232C为例,原理图如下:

【STM32】-串口开发经验分享-基于RTOS+空闲中断

        CPU——STM32ARM内核,通过内部数据总线、地址总线、控制总线和UART连接。

        UART——STM32的外设通用异步收发传输器,作为ARM内核与STM32外部串行数据交互的桥梁。

        RS-232电平转换——RS-232 线路驱动器/接收器,如MAX232。将STM32UART输出电平变换为RS-232C电平。

2.2 RS-232C

        RS-232采取不平衡传输方式,即所谓单端通讯。由于其发送电平与接收电平的差仅为2V3V左右,所以其共模抑制能力差,再加上双绞线上的分布电容,其传送距离最大为约15米,最高速率为20kb/s

2.3 RS-422

        典型的RS-422是四线接口。实际上还有一根信号地线,共5根线。其DB9连接器引脚定义。由于接收器采用高输入阻抗和发送驱动器比RS232更强的驱动能力,故允许在相同传输线上连接多个接收节点,最多可接10个节点。即一个主设备(Master),其余为从设备(Slave),从设备之间不能通信,所以RS-422支持点对多的双向通信。接收器输入阻抗为4k,故发端最大负载能力是10×4k+100Ω(终接电阻)。RS-422的最大传输距离为1219米,最大传输速率为10Mb/s。其平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能达到最大传输距离。只有在很短的距离下才能获得最高速率传输。一般100米长的双绞线上所能获得的最大传输速率仅为1Mb/s。

2.4 RS-485

        RS-485是从RS-422基础上发展而来的,所以RS-485许多电气规定与RS-422相仿。如都采用平衡传输方式、都需要在传输线上接终端电阻等。RS-485可以采用二线与四线方式,二线制可实现真正的多点双向通信,而采用四线连接时,与RS-422一样只能实现点对多的通信,即只能有一个主(Master)设备,其余为从设备,但它比RS-422有改进,无论四线还是二线连接方式总线上可多接到32个设备。RS-485与RS-422的不同还在于其共模输出电压是不同的,RS-485是-7V至+12V之间,而RS-422在-7V至+7V之间,RS-485接收器最小输入阻抗为12kΩ、RS-422是4kΩ;由于RS-485满足所有RS-422的规范,所以RS-485的驱动器可以在RS-422网络中应用。

        RS-485与RS-422一样,其最大传输距离约为1219米,最大传输速率为10Mb/s。平衡双绞线的长度与传输速率成反比,在100kb/s速率以下,才可能使用规定最长的电缆长度。只有在很短的距离下才能获得最高速率传输。一般100米长双绞线最大传输速率仅为1Mb/s

2.5 UART

         通用异步收发传输器(Universal Asynchronous Receiver/Transmitter,通常称为UART)是一种异步收发传输器,是硬件范畴,将数据通过串行通讯进行传输。它在发送端执行并行到串行数据转换,在接收端执行串行到并行数据转换。它是通用的,因为传输速度、数据速度等参数是可配置的。

3. STM32 USART介绍

        UART和USART都是单片机上的通用串口,UART是通用异步收/发器,USART是通用同步/异步收/发器。USART在UART基础上增加了同步功能,当我们在异步通信的时候,USART与UART没有什么区别。

STM32可通过对 USART_CR1 寄存器中的 M 位进行编程来选择 8 位或 9 位的字长。帧详细信息如下图:

【STM32】-串口开发经验分享-基于RTOS+空闲中断

4. CubeMx生成Uart初始化代码

4.1 NewProject选择单片机型号

【STM32】-串口开发经验分享-基于RTOS+空闲中断

4.2 设置rcc时钟

        选择RCC时钟为外部晶振,如图所示。

【STM32】-串口开发经验分享-基于RTOS+空闲中断

         设置HCLK为180M,选择Clock Configuration选项卡,按下图中标注位置配置时钟。

【STM32】-串口开发经验分享-基于RTOS+空闲中断

 4.3 设置Usart

        本文是在探索者阿波罗板上实现串口调试,USART3发送引脚为PB10 ,接收引脚为PB11。选择USART3为异步模式,如下图:

【STM32】-串口开发经验分享-基于RTOS+空闲中断

        设置USART3,如下图:

【STM32】-串口开发经验分享-基于RTOS+空闲中断

4.4 初始化代码

USART初始化相关函数如下表所示。

序号

函数名称

函数功能说明

1

MX_GPIO_Init()

USART相关引脚时钟使能

2

MX_USART3_UART_Init()

USART3配置,波特率、帧位数、校验等

3

HAL_UART_MspInit()

UART底层硬件IO初始化,中断优先级设置

4

HAL_UART_MspDeInit()

UART底层硬件IO复位

具体代码如下:

MX_GPIO_Init(),初始化GPIO时钟。

void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();

}

MX_USART3_UART_Init(),配置串口波特率、数据格式等。

void MX_USART3_UART_Init(void)
{

  huart3.Instance = USART3;
  huart3.Init.BaudRate = 115200;
  huart3.Init.WordLength = UART_WORDLENGTH_8B;
  huart3.Init.StopBits = UART_STOPBITS_1;
  huart3.Init.Parity = UART_PARITY_NONE;
  huart3.Init.Mode = UART_MODE_TX_RX;
  huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart3.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart3) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

HAL_UART_MspInit(),串口时钟、GPIO、中断初始化。

void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct;
  if(uartHandle->Instance==USART3)
  {
  /* USER CODE BEGIN USART3_MspInit 0 */

  /* USER CODE END USART3_MspInit 0 */
    /* USART3 clock enable */
    __HAL_RCC_USART3_CLK_ENABLE();
  
    /**USART3 GPIO Configuration    
    PB10     ------> USART3_TX
    PB11     ------> USART3_RX 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Pull = GPIO_PULLUP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* USART3 interrupt Init */
    HAL_NVIC_SetPriority(USART3_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART3_IRQn);
  /* USER CODE BEGIN USART3_MspInit 1 */

  /* USER CODE END USART3_MspInit 1 */
  }
}

HAL_UART_MspDeInit(),串口复位函数。

void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{

  if(uartHandle->Instance==USART3)
  {
  /* USER CODE BEGIN USART3_MspDeInit 0 */

  /* USER CODE END USART3_MspDeInit 0 */
    /* Peripheral clock disable */
    __HAL_RCC_USART3_CLK_DISABLE();
  
    /**USART3 GPIO Configuration    
    PB10     ------> USART3_TX
    PB11     ------> USART3_RX 
    */
    HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);

    /* USART3 interrupt Deinit */
    HAL_NVIC_DisableIRQ(USART3_IRQn);
  /* USER CODE BEGIN USART3_MspDeInit 1 */

  /* USER CODE END USART3_MspDeInit 1 */
  }
} 

4.5 注意

        CubeMx生成的初始化代码中,,发送数据没有问题,接收数据需要字节编写串口接收回调函数,并打开串口接收中断。

5 工程源码解析

        本文介绍的串口收发程序采用UCOSIII+空闲中断代码实现串口收发数据,串口初始化程序根据CubeMx生成的代码改变,经测试稳定可靠。

5.1 程序架构

        软件进行分层设计,分为应用层、模块层和硬件层等,和Uart相关文件及所在层如下表:

层名称 文件名 备注
应用层(APP)

main.c

stm32f4xx_it.c

app_usart_task.c

模块层(FML)

fml_usart.c

fml_ring_buffer.c

硬件层(HAL、HDL) HAL库

5.2 源码

fml_ring_buffer.c

        该模块为循环缓冲区模块,主要实现数据的缓冲。源码见工程文件,这里不再赘述。

fml_usart.c

        该模块主要完成串口的初始化、配置等。声明中断回调函数,完成数据接收及初步处理工作。

序号 函数名称 函数功能说明
1 MX_USART3_UART_Init() USART3配置,波特率、帧位数、校验等
2 HAL_UART_MspInit() UART底层硬件IO初始化,中断优先级设置
3 HAL_UART_MspDeInit() UART底层硬件IO复位

        具体代码如下:

void MX_USART3_UART_Init(void)
{
    huart3.Instance = USART3;
    huart3.Init.BaudRate = 115200;
    huart3.Init.WordLength = UART_WORDLENGTH_8B;
    huart3.Init.StopBits = UART_STOPBITS_1;
    huart3.Init.Parity = UART_PARITY_NONE;
    huart3.Init.Mode = UART_MODE_TX_RX;
    huart3.Init.HwFlowCtl = UART_HWCONTROL_NONE;
    huart3.Init.OverSampling = UART_OVERSAMPLING_16;
    if (HAL_UART_Init(&huart3) != HAL_OK)
    {
    //_Error_Handler(__FILE__, __LINE__);
    }
      
    __HAL_UART_ENABLE_IT(&huart3, UART_IT_IDLE);        //使能空闲中断
    __HAL_UART_ENABLE_IT(&huart3, UART_IT_RXNE);        //使能接收中断
      
    //创建缓冲区
    FML_ring_buffer_create(&ring_buffer_usart3, buffer_usart3, RING_BUFFER_USART3_SIZE);
}
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{
    GPIO_InitTypeDef GPIO_InitStruct;
    if(uartHandle->Instance==USART3)
    {
        __HAL_RCC_GPIOB_CLK_ENABLE();
        __HAL_RCC_USART3_CLK_ENABLE();
      
        /**USART3 GPIO Configuration    
        PB10     ------> USART3_TX
        PB11     ------> USART3_RX 
        */
        GPIO_InitStruct.Pin = GPIO_PIN_10|GPIO_PIN_11;
        GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
        GPIO_InitStruct.Pull = GPIO_PULLUP;
        GPIO_InitStruct.Speed = GPIO_SPEED_MEDIUM;
        GPIO_InitStruct.Alternate = GPIO_AF7_USART3;
        HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

        /* USART3 interrupt Init */
        HAL_NVIC_SetPriority(USART3_IRQn, 3, 3);
        HAL_NVIC_EnableIRQ(USART3_IRQn);
    }
}
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
    if(uartHandle->Instance==USART3)
    {
        /* Peripheral clock disable */
        __HAL_RCC_USART3_CLK_DISABLE();

        /**USART3 GPIO Configuration    
        PB10     ------> USART3_TX
        PB11     ------> USART3_RX 
        */
        HAL_GPIO_DeInit(GPIOB, GPIO_PIN_10|GPIO_PIN_11);

        /* USART3 interrupt Deinit */
        HAL_NVIC_DisableIRQ(USART3_IRQn);
    }
}

app_usart_task.c

        该应用为串口数据处理业务模块,主要是将接收到数据按照协议解析,并完成相应业务处理。主要函数如下:

void APP_usart_task(void *p_arg) 
{
#define USART_RX_LEN 200
    OS_ERR err;
    CPU_TS ts;
    static uint8_t USART_RxBuffer[USART_RX_LEN];    //缓冲区
    uint16_t len;
    uint8_t RxByteBuffer;           
    uint8_t RxByteCount = 0;        //一般指令按字节查询计数
	while(1)
	{      
        //请求信号量,等待串口总线接收数据
        OSTaskSemPend(0,OS_OPT_PEND_BLOCKING,&ts,&err);
        while(1)
        {
            len = FML_ring_buffer_get_buf_used(&ring_buffer_usart3);      //获取接收到的数据长度
            if(len ==0) break;
            FML_ring_buffer_read(&RxByteBuffer,1,&ring_buffer_usart3);    //读1个字节
            
            /*********************@@--检测HEAD1,HEAD2,字节0 1 ******************/
            if(RxByteCount<= 1)     //接收HEAD
            {
                if(RxByteBuffer==APP_USART_HEAD1 && RxByteCount==0)           //收到HEAD1   
                {    
                    USART_RxBuffer[0] = RxByteBuffer;
                    RxByteCount++;
                }
                else if(RxByteBuffer==APP_USART_HEAD2 && RxByteCount==1)           //收到HEAD2
                {
                    USART_RxBuffer[1] = RxByteBuffer;
                    RxByteCount++;
                }
                else        //未收到“连续HEAD1/HEAD2”指令,重新开始
                {   RxByteCount=0;  }  
            }
            
            /**********************@@--接收数据长度,字节2 **********************/
            else if(RxByteCount==2)                 //接收数据长度+剩余数据
            {
                USART_RxBuffer[2]=RxByteBuffer ;    //接收数据长度
                RxByteCount++;
                if(USART_RxBuffer[2]>(USART_RX_LEN-3) | USART_RxBuffer[2]<3)  //指令长度错误
                {  
                    RxByteCount=0; 
                    
                }
                else                                //接收剩余数据
                {
                    len = FML_ring_buffer_get_buf_used(&ring_buffer_usart3);    //获取缓冲区剩余数据长度
                    if(len >= USART_RxBuffer[2]-3)
                    {
                        FML_ring_buffer_read(&USART_RxBuffer[3],USART_RxBuffer[2]-3,&ring_buffer_usart3);    //读该条指令剩余数据
                        APP_UsartCommonCmdDataProcess(USART_RxBuffer, USART_RxBuffer[2]);          /**@@调用一般指令处理程序*/
                        RxByteCount=0;
                    }
                    else    //数据长度错误 
                    {
                        RxByteCount=0;

                    }
                    len = FML_ring_buffer_get_buf_used(&ring_buffer_usart3);    //获取接收到的数据长度
                    if(len == 0) break;                                         //处理完成
                    else continue;                                              //继续处理
                }
            }
            
        }//while(1)
	}//while(1)
}//app_usart_task
//串口接收完成回调函数
void FML_USART3_RxCpltCallback(void)
{
    OS_ERR err;
    OSTaskSemPost(&AppUsartTaskTCB,OS_OPT_POST_FIFO,&err);    //发送信号量
}

stm32f4xx_it.c

        该模块主要声明中断入口,并通过回调函数调用模块层的函数,由模块层完成数据接收及初步处理。和串口相关代码如下:

//串口USART3中断
void USART3_IRQHandler(void)
{
    OSIntEnter();  
    FML_USART3_IRQHandler();       
    OSIntExit(); 
}

main.c

        完成功能模块层的初始化,应用层业务模块初始化,创建任务流程。源代码详见工程文件。主要函数如下:

//创建串口任务
	OSTaskCreate((OS_TCB*     )&AppUsartTaskTCB,		
				 (CPU_CHAR*   )"APP Usart task", 		
                 (OS_TASK_PTR )APP_usart_task, 			
                 (void*       )0,					
                 (OS_PRIO	  )USART_TASK_PRIO,     
                 (CPU_STK*    )&USART_TASK_STK[0],	
                 (CPU_STK_SIZE)USART_STK_SIZE/10,	
                 (CPU_STK_SIZE)USART_STK_SIZE,		
                 (OS_MSG_QTY  )5,               //任务消息数5条					
                 (OS_TICK	  )0,  					
                 (void*       )0,					
                 (OS_OPT      )OS_OPT_TASK_STK_CHK|OS_OPT_TASK_STK_CLR,
                 (OS_ERR*     )&err);

6. 效果

        经工程设置为收到串口数据自动应答模式,经实测,通过串口调试助手间隔1mS向单片机发送数据,无丢帧现象。

【STM32】-串口开发经验分享-基于RTOS+空闲中断

7. 源码分享

        下载地址:STM32基于RTOS和空闲中断实现的串口通信程序文章来源地址https://www.toymoban.com/news/detail-414682.html

到了这里,关于【STM32】-串口开发经验分享-基于RTOS+空闲中断的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • STM32 UART串口通信IDLE空闲中断的使用步骤

    参考了各路大神的资料,蒙蔽了半天,终于学会了,记录一下,以后忘了可以回来复习参考。 一、首先在stm32cube中配置打开对应uart串口的中断 二、工程main函数调用 __HAL_UART_ENABLE_IT(huart1,UART_IT_IDLE);//hal库宏定义,使能串口空闲中断     HAL_UART_Receive_DMA(huart1,data,sizeof(data));//使

    2024年02月12日
    浏览(39)
  • STM32实战(三):利用空闲中断从串口接收任意长数据

    “ 想起密码了,终于想起密码了(´・ω・`) ” 这次我们利用STM32F103的UART内部的空闲中断来实现对串口任意长数据的接收,通过简洁的手段解决了接收端在事前无法得知数据长度的问题。本次教程我们需要一块STM32核心板与一个USB转TTL工具。 STM32的异步串口接收寄存器可以存放

    2024年02月13日
    浏览(43)
  • 深入理解 STM32 串口空闲中断的工作原理与实现方法

    STM32 微控制器的串口空闲中断是一种重要的通信机制,用于处理数据接收方面的任务。 本文深入解析了 STM32 串口空闲中断的工作原理,包括触发条件和中断服务函数的实现方法,并给出了相应的代码示例。 STM32 微控制器的串口通信是嵌入式系统中常见的通信方式之一。为了

    2024年02月01日
    浏览(45)
  • 【STM32 HAL库实战】串口DMA + 空闲中断 实现不定长数据接收

    STM32CubeMX最新版: 打开STM32CubeMX软件,点击ACCESS TO MCU SELECTOR,在Commercial Part Number 中输入MCU型号,例如我在这里输入了STM32L431RCT6。选中正确型号然后双击进入下一步的配置界面。 1.1 SYS配置如图 1.2 RCC配置如图 开启了外部晶振,若无则都选择Disable 1.3 USART1配置 NVIC Settings 注意

    2024年02月03日
    浏览(75)
  • Stm32407串口1空闲中断+DMA收发(基于标准库实现)

    stm32串口的配置很简单,这里就不赘述了,使用 USART_SendData() 阻塞模式发送数据,或是接收中断配置 “接收缓冲区非空” USART_IT_RXNE ,这种做法效率很低,而且来一个数据中断一次数据处理起来也麻烦。 这里基于STM32F407提供一种串口空闲中断+DMA接收的方式,通过库函数编程

    2024年02月16日
    浏览(46)
  • STM32学习笔记(五)串口空闲中断+DMA实现不定长收发(stm32c8t6)

    记录一下学习过程 DMA DMA,全称为: Direct Memory Access,即直接存储器访问, DMA 传输将数据从一个 地址空间复制到另外一个地址空间。 这一过程无需cpu的参与,从而提高cpu使用的效率 DMA相关的参数:1 数据的源地址、2 数据传输的目标地址 、3 传输宽度,4 传输多少字节,5 传

    2024年02月14日
    浏览(43)
  • STM32-HAL库串口DMA空闲中断的正确使用方式+解析SBUS信号

    能够点进这篇文章的小伙伴肯定是对STM32串口DMA空闲中断接收数据感兴趣的啦,今天用这一功能实现串口解析航模遥控器sbus信号时,查阅了很多网友发布的文章(勤劳的搬运工~),包括自己之前写过一篇博客 STM32_HAL库_CubeMx串口DMA通信(DMA发送+DMA空闲接收不定长数据)。本文

    2024年02月09日
    浏览(63)
  • STM32使用串口空闲中断(IDLE)和 DMA接收一串数据流

    方法一、使用宏定义判断IDLE标志位 空闲的定义是总线上在一个字节的时间内没有再接收到数据,USART_IT_IDLE空闲中断是检测到有数据被接收后,总线上在一个字节的时间内没有再接收到数据的时候发生的。 串口空闲中断(UART_IT_IDLE):STM32的IDLE的中断在串口无数据接收的情况

    2024年01月23日
    浏览(56)
  • stm32串口空闲中断+DMA传输接受不定长数据+letter shell 实现命令行

    空闲中断(IDLE),俗称帧中断,即第一帧数据接收完毕到第二帧数据开始接收期间存在一个空闲状态(每接收一帧数据后空闲标志位置1),检测到此空闲状态后即执行中断程序。 空闲中断的优点在于省去了帧头帧尾的检测 ,进入中断程序即意味着已经接收到一组完整数据,仅需

    2024年02月03日
    浏览(58)
  • GD32实现串口空闲(IDLE)中断 + DMA机制接收数据

    前言 串口功能在单片机开发中,是比较常用的外设,熟练使用串口功能也是驱动开发必备的技能之一。 DMA 是一种CPU辅助手段,可以在CPU不参与的情况下,是做一些辅助CPU的事情,如通常的数据搬运。 在没有DMA之前,数据读取时,需要CPU的处理,在多任务处理时,增加资源紧

    2023年04月13日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包