STM32学习----RS232串口通讯

这篇具有很好参考价值的文章主要介绍了STM32学习----RS232串口通讯。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

一、RS232相关概念

       RS ==Recommend Standard ==推荐标准;

        232==标识号,第232号;

        时间:1962年

        地点:美国

        人物:美国电子工业协会 == Electronic Industries Association ==(美国)电子工业协会

        事件:发布了一个串行通信的物理接口结合逻辑电平的规范文件,就是这个232号文件。

        

        现在关于串口通讯的叫法太多,什么RS232通讯、串口通讯、DB9通讯、UART通讯等等,其实这些称呼都跟这个额232号文件有些关系,随便怎么叫吧,理解就行。

        串行通讯,从字面意思理解就行,就是把数据串成一串发出去,比如一个字节8位,高位先发出去,那么发送顺序就是bit7--bit6--bit5--bit4--bit3--bit2--bit1--bit0。

        就某种单片机来说,比如STM32F103,它有好几个UART口,俗称串口,但是它的引脚高电平是3.3V,低电平是0V,正好满足TTL的电平标准,2.4V--5V表示逻辑1,0V--0.4V表示逻辑0。

        但是电压低了传输距离就比较短,为了传输远点,就把TTL电平转换成RS232电平,通过某种电平转换芯片,比如MAX232。RS232电平逻辑是-3V到-15V表示逻辑1,3V到15V表示逻辑0。这相当于把电平扩大,但是也只能传输十几米。

        人们为了传输更远,就用上了差分传输,比如RS422和RS485,用两根线的电压差来表示逻辑1和逻辑0,两根线的压差为2V至6V表示逻辑1,两线的压差为-2V至-6V表示逻辑0,这样就能传输1000米以上。

数据在一根线上传输,那什么时候是开始,什么时候是结束,每位数据的宽度是多少、数据有没有传输错误。那就需要约定一下,不是谁都像孙悟空一样有觉悟,头上敲三下就是让他凌晨3点过来,还是要讲清楚点好。

线顶一个空闲的状态,就是没传输数据的时候,传输线的电平逻辑是1,用单片机TTL的电平标准就是3.3V,高电平;

开始:发出1位逻辑0电平

结束:发出1位逻辑1电平

数据宽度:要定义每一位的宽度是多少,不然你发两位1我却认为是1位1,怎么办,发送和接收的双发要统一度量衡,才不会有误解。这个数据宽度就是用波特率的约定。

数据有没有传错:那就把收到的数据大家数一数,算一算,我给你发100块钱,我还告诉你是100张一块的,那你收到之后,要数一数,是不是100张,是不是100块,都对了,那就表示是我给你的。

传输一个字节数据的示意图

STM32学习----RS232串口通讯

STM32F103单片机USART内部结构图

STM32学习----RS232串口通讯

 这个图太大了,对新人实在是不友好,对老人也不友好,还是需要好好的梳理一下,总共就4块

1、串口引脚:发送和接收数据的外部引脚;

2、波特率发生器:产生波特率,就是为了控制数据每个bit的宽度;

3、发送与接收的控制单元:控制数据怎么发送,怎么接收;

4、发送与接收数据寄存器:单片机的发送的数据在内部怎么产生,接收的数据怎么吸收;

串口数据收发:

1、轮询收发

  发送数据

        1、声明一个UART_HandleTypeDef结构

        2、配置波特率、字符长度、停止位、校验位等

        3、UART 管脚配置:配置管脚位置和时钟

        4、UART初始化

        5、开始发送数据

       用STM32CubeMX生成代码,前面4步都自动生成好了,只需要在第五步调用发送函数就可以

以下是自动生成的配置代码

#include "usart.h"


//声明一个UART句柄结构体,应为使用串口1 ,结构体命名为huart1
UART_HandleTypeDef huart1;


//配置串口波特率、数据长度、停止位、校验位等
//调用HAL_UART_Init函数,再调用HAL_UART_MspInit函数,
//完成UART管脚和时钟的配置,以及串口初始化
void MX_USART1_UART_Init(void)
{
  huart1.Instance = USART1;
  huart1.Init.BaudRate = 115200;
  huart1.Init.WordLength = UART_WORDLENGTH_8B;
  huart1.Init.StopBits = UART_STOPBITS_1;
  huart1.Init.Parity = UART_PARITY_NONE;
  huart1.Init.Mode = UART_MODE_TX_RX;
  huart1.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart1.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart1) != HAL_OK)
  {
    Error_Handler();
  }

}

//配置UART管脚位置和时钟
void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)
{

  GPIO_InitTypeDef GPIO_InitStruct = {0};
  if(uartHandle->Instance==USART1)
  {

    __HAL_RCC_USART1_CLK_ENABLE();

    __HAL_RCC_GPIOA_CLK_ENABLE();

    //串口1使用GPIOA的,PIN9和PIN10
    GPIO_InitStruct.Pin = GPIO_PIN_9;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_10;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    /* 串口中断配置,轮询模式可以不用配置中断*/
    HAL_NVIC_SetPriority(USART1_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(USART1_IRQn);

  }
}


//相当于还原,还原到配置之前的状态
void HAL_UART_MspDeInit(UART_HandleTypeDef* uartHandle)
{
  if(uartHandle->Instance==USART1)
  {

    __HAL_RCC_USART1_CLK_DISABLE();

    HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9|GPIO_PIN_10);

    HAL_NVIC_DisableIRQ(USART1_IRQn);

  }
}

  在main文件中去完成串口数据发送

        串口发送使用函数

//huart:用哪个串口发送
//pData:被发送的数据指针
//Size:发送的数据个数
//Timeout:超时时间
HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

        串口接收使用函数

//huart:用哪个串口接收
//pData:接收的数据存放位置
//Size:接收的数据个数
//Timeout:超时时间
HAL_UART_Receive(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)

收发实验放在一起比较好,本实验是单片机与PC之间进行数据传输:

PC向单片机发送5个数据 0xAA、 0x11 、0x22 、0x33 、0xBB

单片机每次接收5个数据,并判断第一个数据是不是AA,最后一个数据是不是BB;

如果判断正确,就把事先准备好的6个数据发出去;

还要把接收到的数据清空,等待下一次接收新数据。

#include "main.h"
#include "usart.h"
#include "gpio.h"

//将要发送的数据
uint8_t Tx_Buffer[6] = {0x5A, 0x01,0x02,0x03,0x04,0xA5};

//接收的数据存放位置
uint8_t Rx_Buffer[5] = {0};

void SystemClock_Config(void);

int main(void)
{

  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  MX_GPIO_Init();
  MX_USART1_UART_Init();

  while (1)
  {
    //开始接收数据
    if(HAL_UART_Receive(&huart1, Rx_Buffer, 5, 10))
    {
      //如果收到的第一个数据是AA,第五个数据是BB
      //就把要发送的数据发出去
      if((Rx_Buffer[0]==0xAA)&&(Rx_Buffer[4]==0xBB))
      {
        HAL_UART_Transmit(&huart1, Tx_Buffer, 6, 10);
        
        //也可以在收到数据的时候点个灯
        //当然,也可以根据其他的数据来做该做的事
        HAL_GPIO_TogglePin(GPIOA, LED0_Pin);
      }
      
      //把接收到的数据清空,等待下一次接收
      for(uint8_t i=0; i<5;i++)
      {
        Rx_Buffer[i] = 0;
      }
    }
  }
}

2、中断收发

//串口中断发送函数
//huart: 要发送数据的串口
//pData:要发送的数据指针
//Size:要发送的数据个数,
HAL_UART_Transmit_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)


//串口中断接收函数
//huart: 要接收数据的串口
//pData:接收的数据存放位置
//Size:要接收的数据个数
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size);

针对发送中断函数,把设置的Size个数据发送完之后,才执行中断,就可以执行回调函数了,一般数据发送完,我们也不用做什么事情,回调函数也懒得写,有需求的话就去写吧。

针对接收中断函数,也是接收到Size个数据之后才产生中断,,然后执行回调函数,可以在回调函数中做一些操作,比如判断数据的对错,以及收到数据要做的事情,一般是比较简短的事情,太复杂的事情就不要再回调函数里面做了,可以在回调函数中做个标记,然后再大循环中处理。

以下例程是中断收发数据

1、单片机设置接收中断使能,

2、等待PC发送5个数据0xAA、 0x11 、0x22 、0x33 、0xBB

3、收到5个数据后,串口接收中断触发

4、在接收回调函数中判断第一个数据和最后一个数据是否正确;

5、正确的话就用中断的方式把事先准备好的数据发给PC(本例程是准备6个数据);

6、把前面接收的数据清空,等待下一次接收(当然,接收的数据你想怎么处理就怎么处理)

7、在中断回调函数中,把接收中断重新使能,因为中断执行的过程中把中断使能关闭了。

中断代码

//将要发送的数据
extern uint8_t Tx_Buffer[6];

//接收的数据存放位置
extern uint8_t Rx_Buffer[5];
/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart1);
}


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
  if(huart->Instance == USART1)
  {
    //只要串口接收中断产生了让灯改变一下亮灭的状态
    HAL_GPIO_TogglePin(GPIOA, LED0_Pin);
    
    //判断接收到的数据是否正确,本例程只判断首尾的数据
    if((Rx_Buffer[0]==0xAA)&&(Rx_Buffer[4]==0xBB))
    {
      //收到的数据正确,就把事先准备好的数据发出去
      HAL_UART_Transmit_IT(&huart1, Tx_Buffer, 5);
    }
    
    //把收到的数据清空,准备下一次接收
    for(uint8_t i=0; i<5;i++)
    {
      Rx_Buffer[i] = 0;
    }
  }
  
  //因为中断函数执行完之后,就把中断关闭了,需要重新开启中断
  HAL_UART_Receive_IT(&huart1, Rx_Buffer, 5);
}

main文件代码

#include "main.h"
#include "usart.h"
#include "gpio.h"

//将要发送的数据
uint8_t Tx_Buffer[6] = {0x5A, 0x01,0x02,0x03,0xA5};

//接收的数据存放位置
uint8_t Rx_Buffer[5] = {0};

void SystemClock_Config(void);

int main(void)
{

  HAL_Init();

  /* Configure the system clock */
  SystemClock_Config();

  MX_GPIO_Init();
  MX_USART1_UART_Init();
  
  //使能接收中断
  HAL_UART_Receive_IT(&huart1, Rx_Buffer, 5);

  while (1)
  { 
    //什么都不做
  }
}

中断跟捕猎有点像

        你设置好陷阱,等待猎物的到来,没有猎物的时候,陷阱是不会被触发的;当陷阱被触发之后,你就可以去把里面的猎物取出来了,猎物怎么处理是你的事,是放大自然,还是大孜然;当然了,此时你的陷阱已经被破坏了,如果你还要捕猎,那就把你的陷阱重新弄好。

        这种HAL库里面弄好的中断收发函数有个不好的地方,主要在串口接收,你要收到设定数量的数据才开始触发中断,比如设置收到5个数据才执行中断,第一次PC先给单片机发送4个数据,跟设定的5个数据相比,没达到触发中断的条件,那就继续等着。第二次PC接着又发送3个数据,这时候问题出现了,单片机怎么处理,当然是在第二次的数据还没发送完,只发送一个的时候,这时触发条件成熟了(达到5个数据),单片机开始中断,去处理数据。如果PC的两次发送时间间隔很久怎么办,凉拌,什么都不办了。这肯定是不行的。就像你的陷阱是想逮住5只猎物,但是只逮住了4只,然后再也没有猎物来,你就永远不能去取你的猎物,或者后面又来了3只,一共7只了,你拿走5只,还留两只等下次凑数,那这个猎人脑子肯定是有问题。

        解决这个问题的办法也是有,猎物逮一个收一个,就是来回跑有点累。代码演示一下。

1、每次接收到一个数据就中断;数据存在Rx_Data;

2、中断回调函数里面,把Rx_Data的数据转存到Rx_Buffer数组;

3、一共收到5个数据之后,就把收到的数据发出去,数据个数计数也清零,重新开始。

//串口收到的数据给Rx_Data
extern uint8_t Rx_Data;

//串口接收数据缓存,Rx_Data就是个二道贩子,最终数据还是放在Rx_Buffer
extern uint8_t Rx_Buffer[5];

//接收数据计数
uint8_t Rx_Count = 0;
/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart1);
}


void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

  if(huart->Instance == USART1)
  {
    //只要串口接收中断产生了让灯改变一下亮灭的状态
    HAL_GPIO_TogglePin(GPIOA, LED0_Pin);
    
    //把串口收到的数据转存在Rx_Buffer里
    Rx_Buffer[Rx_Count++] = Rx_Data;
 
    //把收到的数据清空,准备下一次接收
    Rx_Data = 0;
    
    //判断是不是收到了5个数据
    if(Rx_Count ==5 )
    {
      //收到了5个数据,就把收到的数据发出去
      HAL_UART_Transmit_IT(&huart1, Rx_Buffer, 5);
      
      //收到5个数据之后,再重新计数
      Rx_Count = 0;
    }
  }
  
  //因为中断函数执行完之后,就把中断关闭了,需要重新开启中断
  HAL_UART_Receive_IT(&huart1, &Rx_Data, 1);
}

        上面每次收一个数据就中断的情况,在数据少的时候用用还可以,在大项目里面,数据多的话,最好还是别用,不然单片机就一直在中断了,不要干别的事情了。就好比你在干几个亿的项目,隔几秒钟就有人来汇报一下,你是什么心态。

        那肯定有解决方案的,为了解决大量的数据传送,不要想着轮询的方式了,轮询是最傻瓜的工作方式,在控制上想都别想,问题太多,简单项目用用还可以,比如打印信息。那可不可以等数据都发送完了,通讯线空下来的时候给我个中断,通知我来处理数据。就像你在跟客户讨论需求的时候,你不会客户提一个需求你就去干,再接等客户提下一个需求,再继续干,那人就废了,让客户一口气把需求提完,等客户安静下来,我再来处理那一堆的需求。STM32单片机就可以这样。


//huart:那个串口收数据
//pData:收到的数据存在哪里
//Size:收多少个数据
HAL_UARTEx_ReceiveToIdle_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

测试例程

1、PC给单片机发送10个数据,随便哪10个;

2、单片机通过空闲中断的方式接收;

3、接收到之后把数据拷贝出来;

4、把数据通过中断发出去

//将要发送的数据
extern uint8_t Tx_Buffer[6];

//接收的数据存放位置
extern uint8_t Rx_Buffer[20];

uint8_t RX_BUFFER[20];
/**
  * @brief This function handles USART1 global interrupt.
  */
void USART1_IRQHandler(void)
{
  HAL_UART_IRQHandler(&huart1);
}

void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef *huart, uint16_t Size)
{
  //if((huart->Instance == USART1)&&(Size==10))
  if(huart->Instance == USART1)
  {
    //只要串口接收中断产生了让灯改变一下亮灭的状态
    HAL_GPIO_TogglePin(GPIOA, LED0_Pin);
    
    //把串口收到的数据转到RX_BUFFER里
    memcpy(RX_BUFFER,Rx_Buffer,sizeof(Rx_Buffer) );
    //收到的数据正确,就把事先准备好的数据发出去
    HAL_UART_Transmit_IT(&huart1, RX_BUFFER, 10);

    
    //把收到的数据清空,准备下一次接收
    for(uint8_t i=0; i<20;i++)
    {
      Rx_Buffer[i] = 0;
    }
  }
  
  //因为中断函数执行完之后,就把中断关闭了,需要重新开启中断
  HAL_UARTEx_ReceiveToIdle_IT(&huart1, Rx_Buffer, 10);
}

还有DMA收发数据,放在DMA专题那边说。文章来源地址https://www.toymoban.com/news/detail-457985.html

到了这里,关于STM32学习----RS232串口通讯的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 学习笔记|串口通信的基础知识|同步/异步|RS232|常见的串口软件的参数|STC32G单片机视频开发教程(冲哥)|第二十集:串口通信基础

    百度百科:串口通信的概念 什么是通信? 例如U盘和电脑,我们电脑需要往U盘存东西,而U盘上只有四个触点,除去一个电源一个地,只剩下两个引脚了。此时我们坑定不能像点亮LED那样单纯的给他两个引脚上输出个高低电平就能写数据了对吧。总不至于输出一个高电平就能

    2024年02月07日
    浏览(58)
  • 【学习笔记】串口通信RS232

         UART 是一种通用的数据通信协议,也是异步串行通信口(串口)的总称,它在发送数据时将并行数据转换成串行数据来传输,在接收数据时将接收到的串行数据转换成并行数据。串口作为常用的三大低速总线之一。不同于 SPI、IIC 是同步通信接口,UART是全双工异步通信

    2024年02月06日
    浏览(39)
  • STM32 RS232通信

    RS-232是是串行通信接口,由电子工业协会(Electronic Industries Association, EIA)所制定的异步传输标准接口。在1962年发布,命名为EIA-232-E,作为工业标准,以保证不同厂家产品之间的兼容。RS是英文“推荐标准”的缩写,232为标识号。RS232是全双工通信,通信距离最远15米内, 以前的计算机

    2024年02月06日
    浏览(35)
  • 【正点原子STM32】RS485串行通信标准(串口基础协议 和 MODBUS协议、总线连接、通信电路、通信波形图、RS485相关HAL库驱动、RS485配置步骤、)

    一、RS485介绍 二、RS485相关HAL库驱动介绍 三、RS485配置步骤 四、编程实战 五、总结 串口、UART、TTL、RS232、RS422和RS485之间的关系可以如此理解: 串口 :是一个广义术语,通常指的是采用串行通信协议的接口,它可以包括多种具体的物理接口标准和逻辑电平标准。 UART (通用

    2024年04月13日
    浏览(64)
  • STM32学习:串口通讯(proteus仿真)

    本次通过CubeMx+proteus进行stm32串口仿真 具体功能: 1、开机后,向串口1发送“Welcome” 2、串口1接收字节指令“0xa1\\\",打开LED1,回传“LED1 OPEN!” 3、串口1接收字节指令“0xa2\\\",关闭LED1,回传“LED1 Close!” 4、在串口发送过程中,打开LED2作为发送数据指示灯 1、COMPIM元件 作用:把仿

    2024年02月03日
    浏览(42)
  • RS232&RS485串口的区别

    一、接口物理结构不同  1、RS232接口以9个引脚 (DB-9) 或是25个引脚 (DB-25) 的型态出现,一般个人计算机上会有两组RS232 接口,分别称为COM1和COM2。 2、RS485接口没有具体的物理形状,是根据工程的实际情况而采用的接口。 二、接口电子特性不同 RS232接口的信号电平值较高,易损

    2024年02月05日
    浏览(42)
  • 串口RS232、RS485最本质区别

    由下图可看出不管是RS232还是RS485,其本质都是串口通信,只不过是串口通信电平上的变种而已。所以,我们首先从串口通信讲起。 1、串口通信 任何一种通信都要有物理接口和通信协议。串口通信物理接口如下图: 串口通信协议首先要约定好帧格式和波特率。下图是我们常

    2024年02月09日
    浏览(46)
  • .NET串口通信 RS232 、RS485

    1.RS232是全双工的,RS485是半双工的,RS422是全双工的。 在信息传输通道中,携带数据信息的信号单元叫码元,每秒钟通过信道传输的码元数称为码元传输速率,简称波特率。波特率是传输通道频宽的指标。 波特率9600与波特率19200的区别就是:波特率19200传输快但传输距离近,波

    2024年02月12日
    浏览(51)
  • 串口通信协议--UART、RS-232、RS-485、RS-422

    近期学习了单片机及数电相关知识,故决定系统地学习一些通信协议。手边书本仅介绍了串口低速接口的相关协议。 串行通信 是指通过使用一条数据线(需要地线或控制线),将数据一位一位地一次传输,每一位数据占据一个固定的时间长度。 特点 :抗干扰,节省数据线,

    2024年02月05日
    浏览(58)
  • QT-如何使用RS232进行读写通讯

    以下是一个使用Qt进行RS232通讯的具体示例,包括读取和写入数据的操作: 在这个示例中,我们定义了一个全局的 QSerialPort 对象 serial 用于串口通讯。首先设置串口名称和波特率,并打开串口。通过连接 readyRead 信号到 readData 槽函数来读取串口数据。 readData 函数读取串口数据

    2024年02月13日
    浏览(48)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包