stm32串口通信的一个小总结(从底层进行理解)

2023-05-16

从底层理解stm32USART串口通信

以前学串口通信踩过很多坑,过了一段时间又有些忘了,现在问了几个很强很强的人差不多弄懂了,现在写一写总结,免得以后又忘了。

基本知识:

1、TDR和RDR都是USART_DR寄存器的缓冲区,指的是USART_DR的0到8位,TDR和RDR共用一片物理空间。

 

2、

通过向数据寄存器写入数据来将 TXE 位清零。

通过软件对 USART_DR 寄存器执行读操作将 RXNE 位清零.

 

3、TXEIE和TCIE的意义是,TXEIE允许在TXE标志为1时产生中断,而TCIE允许在TC标志为1时产生中断,RXNEIE同理。

 

4、

 

5、

 

6、

有三个主要的寄存器USART_SR,USART_DR,USART_CR1 。USART_SR的S代表STATUS,这个寄存器用来记录串口是否收到了数据。含有RXNE,,TC,TXNE。USART_CR1 寄存器中含有TCIE ,TXEIE,RXNEIE等串口中断使能位。

USART_DR是用来储存数据的寄存器,它的0到8位是用来储存数据的,叫做缓冲区,也称为RDR,TDR,在接受的时候成为RDR,在发送的时候称为TDR,其实是一个东西,这也是为什么TDR与RDR共用同一片物理空间的原因。其余位不用考虑。同时在程序里可以设置,传输的时候是8位还是9位,个人建议传输都用8位,因为现在编码大多采用以字节为单位的方式,在用9位的情况下,如果发生上溢错误,就会改变bit的解码顺序,造成后面传来的数据解码混乱。而如果用8位,丢掉一个字节,后面的字节还是可以正常解码出来。

 

7、

 

8、不论是发送数据还是接受数据,一个串口只有一个中断函数。

 

接受数据的过程:

       RX设置为floating浮空状态,适于被动地在高电平低电平之间的快速转换,它的状态是由发送端传来的电平高低来决定的。

       数据通过串口线一位一位传过来,先传到移位寄存器,“移位”就是形容一位一位bit传过来的过程,一个数据帧有起始位,数据位,校验位,停止位。当移位寄存器识别了这个数据帧之后,通过“并行通信”的方式“一次性”传递给RDR寄存器,就是把八个数据位一次性传给RDR寄存器,我们从内部总线向RDR寄存器写入数据的时候也是并行通信。这也就是为什么在参考手册中讲(第三张图片),RDR寄存器在移位寄存器和内部总线之间提供了并行接口。

为什么数据可以有秩序地传输呢?因为移位寄存器通过起始位,校验位,停止位来“辨识”这个数据帧,一个没有起始位,校验位,停止位(根据通信协议的规定而变化)的裸露数据是不可能由移位寄存器传给RDR寄存器的,这也就是为什么当一个数据发生错误的时候,后面的数据继续传输不会受到前面的影响。(我之前在考虑前一个数据的后半部分与后一个数据的前半部分进行组合形成错误数据帧的情况,我想多了。)

当移位寄存器中的数据位传到RDR中时,RXNE会硬件置为1,这个所谓的硬件置1就是靠单片机“自动”完成的,就相当于提醒我们缓冲区有数据了,我们要去读了。这个时候如果USART_CR1的RXNEIE接受中断使能位开启了,也就是手册中的软件置1,而所谓的软件置1就是指人为去设置,那么就会进入串口中断。我们在串口中断中去读取RDR缓冲区里面的数据内容,而一旦读取了RDR中的内容,RXNE标志位会硬件置0。下次还有数据传进来的时候,RXNE又会置1。如此循环往复。

如果串口中断的时间太久了,或者说迟迟没有读取RDR寄存器的内容,那么就会发生上溢错误。上溢错误在第3张图中有简介,这个时候移位寄存器不会把数据位通过并行通信传给RDR,而是被后面传入的数据所覆盖,这里的覆盖是一位一位的,有人可能会问,如果数据位有一半被后面传来的数据覆盖了,同时这个时候软件进行了读取,RXNE置为0,那么这一半的数据位还会进入RDR吗?我认为不会出现这样的,如果整个数据帧有一位被上溢错误给破坏,那么整个数据帧都会被丢弃,从下一个完整地数据帧开始传送。理由是这样不会干扰到后面的数据传输。

 

 

发送数据的过程:

TX引脚设置为推挽输出模式。发送数据类似于我们从RDR寄存器读取数据,我们发数据就是往TDR寄存器中写数据(这个赋值语句是通过并行通信实现的),RDR和TDR寄存器表示的物理空间是一样的。然后TDR寄存器会通过并行通信把数据传给移位寄存器。这个时候TXE会置1,再次向TDR写入数据会使TXE置0,TDR进入移位寄存器之后,会一位一位发送,如果移位寄存器把数据帧发送完了,同时TDR为空,即TXE仍为1(意味着不仅移位寄存器没事干了,DR寄存器也没事干了,全弄完了),那么TC标志位置1。

具体的过程是:所有位发送结束时(送出停止位后)硬件会设置TC标志,当移位寄存器把数据帧的停止位送出之后,去检测TXE是否为1,如果TXE1,则TC标志位置1,产生中断

TXE--写寄存器DR清零,(完全由硬件控制,有东西就是0,没东西就是1,无法软件清零)

 TC--  第一种方法,读SR寄存器后写寄存器DR清零(好麻烦有木有),第二种方法,也可软件手动清零,直接手动暴力清零。

 

1、只利用TC中断来发送信息

void USART_Config()//开启中断,开启串口

{

  USART_ITConfig(USART1, USART_IT_TC, ENABLE);//Tramsimssion Complete后,才产生中断. TC中断必须放在这里,否则还是会丢失第一字节

 

  USART_Cmd(USART1, ENABLE); //使能USART1

}

 

void USART_SendDataString( u8 *pData )//发送信息的函数。
{
    pDataByte = pData;
  
    USART_ClearFlag(USART1, USART_FLAG_TC);//
清除传输完成标志位,否则可能会丢失第1个字节的数据.网友提供.//在发送之前先把标志位清空一下,保险一点,没有太大含义
    USART_SendData(USART1, *(pDataByte++) ); //必须要++,不然会把第一个字符t发送两次。附加注释:这句话作为一个引线来触发这个串口中断。


}

void USART1_IRQHandler(void)
{
    if( USART_GetITStatus(USART1, USART_IT_TC) == SET  )
//SR寄存器
    {
        if( *pDataByte == '\0' )

            USART_ClearFlag(USART1, USART_FLAG_TC);发完了 把标志位手动清零,因为之后不会再向DR寄存器里面写内容了,无法通过第一种方式将TC标志位清零。    

else
            USART_SendData(USART1, *pDataByte++ );//DR寄存器写内容,同时

TC标志位会清零。
    }

}

extern u8 *pDataByte;在主函数中写好这个指针变量,在USART.C文件中作为外部变量引进来。

C语言小提醒: *p = *(p+1); 而且是后加加。

 

2、只利用TXE中断来发送信息

void USART_SendDataString( u8 *pData )
{
    pDataByte = pData;
    USART_ITConfig(USART1, USART_IT_TXE, ENABLE);//
只要发送寄存器为空,就会一直有中断,因此,要是不发送数据时,把发送中断关闭,只在开始发送时,才打开。
    
}


 

void USART1_IRQHandler(void)
{
    if( USART_GetITStatus(USART1, USART_IT_TXE) == SET  )
    {
        if( *pDataByte == '\0' )//
待发送的字节发到末尾NULL
            USART_ITConfig(USART1, USART_IT_TXE, DISABLE);//因为是 发送寄存器空 的中断,所以发完字符串后必须关掉,否则只要空了,就会进中断
        else
            USART_SendData(USART1, *pDataByte++ );
    }

}

 

 

个人感觉第二种发送方式好一点点。通常的协议一般是8位发送,一位停止位,无数据流控制,无校验位。

代码参考自:https://blog.csdn.net/zyboy2000/article/details/7566647

 

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

stm32串口通信的一个小总结(从底层进行理解) 的相关文章

  • 51单片机 数码管中断操作

    实践目的 1 掌握中断的概念和思想 2 掌握51单片机中断系统和相关软硬件设计 实践内容 1 利用单片机的P0口接数码管的字段脚 P1 0脚接共阴极 P3 2 P3 3引脚接独立按键产生外部中断信号 编写程序 当程序正常运行时数码管显示H字
  • 在地址“0xXXXXXX”处中断,没有可用的调试信息,或在程序代码之外

    配置 使用 Nucleo L476RG 使用 GNU ARM Eclipse 我从 STM32CubeMX 生成了一个极简代码 我已经在我的板载 ST Link 中刷新了 J link 驱动程序 一直在尝试为我的代码运行调试器 但我的程序计
  • 在没有 IDE 的情况下如何使用 CMSIS?

    我正在使用 STM32F103C8T6 并想使用 CMSIS 这本质上只是寄存器定义 没有代码 让我的生活更轻松 同时仍保持在较低水平 问题是我不知道如何安装该库以便在命令行上使用 Makefile 使用 所有文档似乎都与特定于供应商的 I
  • GCC - 如何停止链接 malloc?

    我正在努力将我的代码缩减到最小的骨架大小 我使用的是只有 32k 闪存的 STM32F0 需要很大一部分闪存用于数据存储 我的代码已经有大约 20k 闪存大小 其中一些是由于使用了 STM32 HAL 函数 我可以在以后需要时对其进行解释和
  • CMSIS 库是否应该包含在版本控制中? [复制]

    这个问题在这里已经有答案了 通常 我曾经在版本控制中包含芯片供应商 ST 提供的设备特定标头和源以及 CMSIS Core 标头 数量不多 也没有更新的习惯 我使用STM32微控制器 但我不使用立方体框架 or the 标准外设库 最近 我
  • 139-基于stm32单片机老人居家监护报警系统Proteus仿真+源程序

    资料编号 139 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 MQ4传感器 电位器模拟 MQ2传感器 电位器模拟 蜂鸣器 电机 制作一个基于stm32单片机老人居家监护报警系统Proteus仿真 2 通过MQ2传
  • 134-基于stm32单片机矿井瓦斯天然气浓度温湿度检测自动通风系统Proteus仿真+源程序...

    资料编号 134 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 ds1302时钟 DHT11温湿度 电机 蜂鸣器 制作一个基于stm32单片机矿井瓦斯天然气浓度温湿度检测自动通风系统Proteus仿真 2 通过DH
  • 137-基于stm32单片机智能保温杯控制装置Proteus仿真+源程序

    资料编号 137 一 功能介绍 1 采用stm32单片机 LCD1602显示屏 独立按键 DS18B20传感器 电机 制作一个基于stm32单片机智能保温杯控制装置Proteus仿真 2 通过DS18b20传感器检测当前保温杯水的温度 并且
  • 匹配 STM32F0 和 zlib 中的 CRC32

    我正在研究运行 Linux 的计算机和 STM32F0 之间的通信链路 我想对我的数据包使用某种错误检测 并且由于 STM32F0 有 CRC32 硬件 并且我在 Linux 上有带有 CRC32 的 zlib 所以我认为在我的项目中使用
  • STM32用一个定时器执行多任务写法

    文章目录 main c include stm32f4xx h uint32 t Power check times 电量检测周期 uint32 t RFID Init Check times RFID检测周期 int main Timer
  • STM32超声波——HC_SR04

    文章目录 一 超声波图片 二 时序图 三 超声波流程 四 单位换算 五 取余计算 六 换算距离 七 超声波代码 一 超声波图片 测量距离 2cm 400cm 二 时序图 1 以下时序图要先提供一个至少10us的脉冲触发信号 告诉单片机我准备
  • STM32 GPIO工作原理详解

    STM32 GPIO介绍 1 STM32引脚说明 GPIO是通用输入 输出端口的简称 是STM32可控制的引脚 GPIO的引脚与外部硬件设备连接 可实现与外部通讯 控制外部硬件或者采集外部硬件数据的功能 以STM32F103ZET6芯片为例
  • [屏驱相关]【SWM166-SPI-Y1.28C1测评】+ 有点惊艳的开箱

    耳闻华芯微特许久了 看到论坛得评测活动赶紧上了末班车 毕竟对有屏幕得板子也是很喜欢得 京东快递小哥客客气气 微笑着把快递给了我 好评 直接拆了包 在此之前没看过视频号 所以这个圆盘盘得模具还是有点惊喜的 正面照如下 开机有灯光秀 还有动画
  • [MM32硬件]搭建灵动微MM32G0001A6T的简易开发环境

    作为学习单片机的经典 自然是通过GPIO点亮LED 或者是响应按钮的外部中断例程 这我们看看SOP8封装的芯片MM32G0001A6T得引脚 除了VDD和GND固定外 我们可以使用PA14 PA1 PA13 PA15 PA2 PA3这六个G
  • 硬件基础-电容

    电容 本质 电容两端电压不能激变 所以可以起到稳定电压作用 充放电 电容量的大小 想使电容容量大 使用介电常数高的介质 增大极板间的面积 减小极板间的距离 品牌 国外 村田 muRata 松下 PANASONIC 三星 SAMSUNG 太诱
  • 解决KEIL编译慢问题

    两种方案 使用v6版本的ARM Compiler 如果v6版本编译不过 必须使用v5版本的 则可以勾选掉Browse Information选项 提升很明显 1分多钟能优化到几秒 看代码量 但是这个有个弊端 在KEIL中会影响函数跳转 建议
  • 跟着野火学FreeRTOS:第一段(任务定义,切换以及临界段)

    在裸机系统中 系统的主体就是 C P U CPU CP U 按照预先设定的程序逻辑在 m a i n
  • 擦除后无法写入闪存

    所以我不能在擦除后直接写入内部闪存 如果写操作之前没有擦除操作 那么我可以 有什么想法吗 编程函数返回 成功写入 值 但查看内存时 没有写入任何数据 这是代码 uint32 t pageAddress 0x08008000 uint16 t
  • CMSIS & STM32,如何开始? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我想在 STM32 上使用 CMSIS 启动项目 网上一搜 没找到具体的教程 有些使用 SPL 开始项
  • STM32 传输结束时,循环 DMA 外设到存储器的行为如何?

    我想问一下 在以下情况下 STM32 中的 DMA SPI rx 会如何表现 我有一个指定的 例如 96 字节数组 名为 A 用于存储从 SPI 接收到的数据 我打开循环 SPI DMA 它对每个字节进行操作 配置为 96 字节 是否有可能

随机推荐

  • Linux驱动开发与裸机开发区别

    Linux驱动开发与裸机开发区别 裸机驱动开发回顾Linux驱动开发思维Linux驱动开发分类 裸机驱动开发回顾 1 底层 跟寄存器打交通 xff0c 有些MCU提供了库 Linux驱动开发思维 1 Linux下驱动开发直接操作寄存器不现实
  • Linux下使用U盘

    第一步 xff1a 插入U盘 xff0c 如果能够识别出U盘 xff0c 则会打印出一些信息 xff1b 第二步 xff1a 查看U盘系统分配给U盘的设备名 xff1b 输入如下命令进行查看 xff1a fdisk l dev sda 如果
  • (Deep Learning)交叉验证(Cross Validation)

    交叉验证 xff08 Cross Validation xff09 交叉验证 xff08 Cross Validation xff09 是一种评估模型泛化性能的统计学方法 xff0c 它比单次划分训练集和测试集的方法更加稳定 全面 交叉验证
  • (Linux)在Ubuntu系统中添加新用户并授予root权限

    向Ubuntu系统中添加新用户并为其授予root权限的步骤如下 打开终端Terminal 输入命令 sudo su 以 root 身份登录 注 sudo su 切换root身份 不携带当前用户环境变量 sudo su 切换root身份 携带
  • (深度学习)类别不平衡数据集中IOU和mIOU的选择

    测试集上的mIOU很高 xff0c 但是实际的分割结果很差 xff0c 几乎没有分割出前景 xff0c 主要是因为要分割的目标占总面积之比太少 xff0c 即出现样本不均衡的问题 此时 xff0c 前景所占的比例太小 xff0c 背景所占的
  • 系统调用,API,运行库函数和C标准库函数的区别

    1 为什么用户程序不能直接访问系统内核模式提供的服务 xff1f 在linux中 xff0c 将程序的运行空间分为内核与用户空间 xff08 内核态和用户态 xff09 xff0c 在逻辑上它们之间是相互隔离的 xff0c 因此用户程序不能
  • 学习四旋翼(二):控制方法之串级PID与卡尔曼滤波(含MATLAB示例)

    暑假期间 xff0c 对于四旋翼有一点兴趣 xff0c 没有亲手做 xff0c 但是看了一些资料 这个系列文章只是对自己看的东西的记录 xff0c 对于想要学习了解相关知识的同学没有任何参考价值 xff01 本篇是系列的第二部分 xff1a
  • Git tag标签与branch分支 区别

    Git中的分支和标签有点类似 xff0c 都是引用或者说指针 关于Git引用可以参阅Git References一章节 一 相似的地方 xff1a 图示如下 xff1a heads和tags文件夹存储的是具体分支和标签 xff1a tags
  • 关于字符串结束符'\0'

    字符串结束符 xff1a 39 0 39 xff0c 其本质就是8位的 0000 0000 xff0c 而字符类型中并没有这个字符 xff08 注意与ASCLL码区别 xff0c 在ASCLL中000 代表NULL xff09 所以用0的转
  • extern “C”的作用详解

    extern 34 C 34 的主要作用就是为了能够正确实现C 43 43 代码调用其他C语言代码 加上extern 34 C 34 后 xff0c 会指示编译器这部分代码按C语言的进行编译 xff0c 而不是C 43 43 的 由于C 4
  • Lab2 p3 围棋吃子的算法实现

    简单介绍下框架 xff1a 1 xff0e 声明一维数组block 作为一个临时变量记录一个块的大小 xff0c 声明一个整型blockLength记录这个块的长度 2 xff0e kill 为吃子的主函数 recersion int i
  • Python爬取皮皮虾视频

    背景 xff1a 今天闲着没事做 xff0c 然后想着刷刷视频 xff0c 然后发现前段时间学习了一下网络爬虫的一些基本应用 xff0c 就想着利用爬虫到网上去爬取一点视频来模拟人为的点击 下载操作 因为皮皮虾是手机端的app xff0c
  • C语言——全局变量的定义与声明

    转自 xff1a https www cnblogs com amanlikethis p 3319744 html C语言中全局变量的定义与声明困扰着许多C语言初学者 本文讲述了全局变量定义与声明的用法 xff0c 而且本为也将阐述这种用
  • ResourceNotFound:xxx roslaunch找不到包

    执行命令 xff1a roslaunch xxx 出现如下错误 错误原因 xff1a 这里错误的原因可能有两个 原因1 xff1a ROS path n 没有你的包所在的路径 解决方法 xff1a 对ros path 进行配置 1 xff1
  • 单片机对底层寄存器的操作

    最近项目用到了国产的一款单片机 xff0c 没有例程的支持 xff0c 需要自己从头开始写底层 又感受到了自己本科刚学习51的时候的浮躁 xff0c 不懂得如何操作底层的寄存器 xff0c 只是一味的抄写别人的例程 xff0c 然后进行简单
  • PyQt5自学记录(1)——PyQt5多线程实现详解

    PyQt5自学记录 xff08 1 xff09 PyQt5中多线程实现详解 最近想用PyQt5完成图像识别的一个GUI系统 xff0c 在调用算法模型进行识别的时候 xff0c 界面会卡住没有反应 xff0c 所以想学习一下多线程解决这个问
  • 编写程序的步骤

    编写 C 语言程序的7个步骤 1 定义程序的目标 资深程序员需要养成的良好的思考习惯 在动手写程序之前 xff0c 要在脑中有清晰的思路 想要程序去做什么 1 首先自己要明确自己想做什么 xff0c 2 思考你的程序需要哪些信息 xff0c
  • 看懂英文数据手册、搭建电路

    阅读数据手册是一个工程师的必备技能 xff0c 拿到一份数据手册 xff0c 特别是英文数据手册 xff0c 如何去读 xff0c 才能更快更好的找到自己想要的东西 xff1f 坚信 xff1a 阅读英文手册 xff0c 并没有想象的那么难
  • 英语四级重点短语

    devote to 将 致力于 derive from 61 originate from 61 stem from 源自于 instant adj 立即的 速溶的 instant coffee速溶咖啡 instant noodle 方便面
  • stm32串口通信的一个小总结(从底层进行理解)

    从底层理解stm32USART串口通信 以前学串口通信踩过很多坑 xff0c 过了一段时间又有些忘了 xff0c 现在问了几个很强很强的人差不多弄懂了 xff0c 现在写一写总结 xff0c 免得以后又忘了 基本知识 xff1a 1 TDR