目的

​ 串口通信是非常非常常见的一种通信方式,必须掌握的。可以从如下几个方面掌握串口通信:

  1. 串口通信原理,此处我们只研究异步串口
  2. GD32常见的几种串口通信配置

异步串口通信原理

1. 配置

​ 在了解原理之前,我们先看看串口要如何使用,如下图,只要选择正确的串口号,把收发双方的波特率、校验位、数据位、停止位配置成一致,这么就可以实现双方通信。

那么配置的这些参数分别代表什么意思呢?

串口号:唯一标识一个串口,当设备存在多个串口时,可以用其标识每个串口。

波特率:每秒钟传输的数据位数。表示数据传输的速率,单位bps(位每秒)。比如115200bps就表示1s可以传输115200bits的数据。

校验位:

​ even 每个字节传送整个过程中bit为1的个数是偶数个(校验位调整个数)
​ odd 每个字节穿送整个过程中bit为1的个数是奇数个(校验位调整个数)
​ none 没有校验位
​ space 校验位总为0
​ mark 校验位总为1

数据位:5678共4个选择,这是历史原因,如下

​ 5:用于电报机传26个英文字母,5位足以

​ 6:用于电报机,识别大小写字母,增加一个大小写位

​ 7:用于电脑,ASCII码7位

​ 8:用于电脑,DBCS码用于兼容ASCII和支持中文双字节

停止位:

​ 停止位是按长度来算的。串行异步通信从计时开始,以单位时间为间隔(一个单位时间就是波特率的倒数),依次接受所规定的数据位和奇偶校验位,并拼装成一个字符的并行字节;此后应接收到规定长度的停止位“1”。所以说,停止位都是“1”,1.5是它的长度,即停止位的高电平保持1.5个单位时间长度。一般来讲,停止位有1,1.5,2个单位时间三种长度。

2. 帧格式

​ 下面我们看下串行协议的帧格式,如图

一个帧由4部分组成,起始位+数据位+校验位+停止位,正好跟上面的配置一一对应,其中,起始位必须是低电平,停止位必须是高电平。

至此,也大致明白串口是怎么回事了。

3. 常见的串口电平标准

​ 下面几种都是串口,只是电平标准不同,导致其应用场景存在差异,通信协议和配置都是相同的,通信原理是相同的,软件实现相同,硬件电路存在差异。

TTL:

  1. 接线方式如图

  2. 高电平表示逻辑1, 低电平表示逻辑零

     [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-51f3Uyne-1623987121021)(assets/1536739271596.png)]
    

RS232和RS485对比

  1. 抗干扰性:RS485 接口是采用平衡驱动器和差分接收器的组合,抗噪声干扰性好。RS232 接口使用一根信号线和一根信号返回线而构成共地的传输形式,这种共地传输容易产生共模干扰。
  2. 传输距离:RS485 接口的最大传输距离标准值为 1200 米(9600bps 时),实际上可达 3000 米。RS232 传输距离有限,最大传输距离标准值为 50 米,实际上也只能用在 15 米左右。
  3. 通信能力:RS-485 接口在总线上是允许连接多达128个收发器,用户可以利用单一的 RS-485 接口方便地建立起设备网络。RS-232只允许一对一通信。
  4. 传输速率:RS-232传输速率较低,在异步传输时,波特率为 20Kbps。RS-485 的数据最高传输速率为 10Mbps 。
  5. 信号线:RS485 接口组成的半双工网络,一般只需二根信号线。RS-232 口一般只使用 RXD、TXD、GND 三条线 。
  6. 电气电平值:RS-485的逻辑"1"以两线间的电压差为+(2-6) V 表示;逻辑"0"以两线间的电压差为-(2-6)V 表 示 。在 RS-232-C 中任何一条信号线的电压均为负逻辑关系。即:逻辑"1",-5- -15V;逻辑"0 " +5- +15V 。

4. 芯片如何实现串口功能

​ 我们知道串口的作用是为CPU和其它设备之间提供通信,本质上是把数据从其他设备搬移到自身MCU的内存中去,如上图,MCU为实现串口功能会做如上的模块划分。

  1. GPIO

    串口总线状态,默认是高电平,所以Tx应该是上拉输出,Rx应该是浮空输入。

  2. 移位器

    我们知道串口是一位位传输的,所以移位器即可以实现串口的收发。

  3. 数据寄存器

    用于存储将要发送和接收的数据,其实只要收发共用一个字节就足以。

  4. 时钟

    上述的运行过程都需要在固定时钟下才能正确运行,例如波特率。

  5. 数据由寄存器搬移到内存

    1. CPU方式,由CPU控制数据如何在数据寄存器和内存之间进行转移,例如当数据寄存器空时,将内存转移到数据寄存器中,即发送过程
    2. DMA方式,过程同CPU,那为什么还有有DMA呢?因为数据搬移省去CPU的参与,也就意味着CPU可以去忙其它事情,效率自然就高了。
  6. 状态寄存器

    1. 我们粗略的思考下,在整个串口的传输过程中,肯定会有各式各样的状态,例如,收到数据,数据异常,帧错误,数据发送完毕,数据寄存器空了等等,这些都需要状态寄存器存储。
    2. 再深入思考下,当我们需要及时的处理上述状态时,靠CPU轮询显然太慢了,所以肯定需要中断,再增加一组中断状态寄存器。
  7. 配置寄存器

    ​ 上述情况那么多,代表不同的配置,肯定需要几组配置寄存器。例如,中断的使能控制等

功能设计

​ 如果明白了原理,那么自然就知道该如何配置一个串口了,无非就是从芯片手册中找到相应的寄存器进行配置而已。

​ 在”串口发送“例子中,已经接触了串口的发送功能,现在我们把这个例子再度深入,实现串口的接收功能。实现一个回显功能,即PC通过串口向GD32写入数据,然后GD32把数据原封不动返回给PC。

轮询方式
VOID DRV_UART1_PollTest(VOID)
{U8 ch = 0;while (1){if (USART_GetBitState(USART1, USART_FLAG_RBNE) != RESET){ch = (U8)USART_DataReceive(USART1);UART1_SendChar(ch);}}
}VOID DRV_UART1_PollInit(VOID)
{UART1_GpioInit();UART1_Config();USART_Enable(USART1, ENABLE);
}

效果如图

中断方式

注:中断优先级部分,我会抽单独章节分析。

必须注意下面这两个函数的区别,

USART_GetBitState(USART1, USART_FLAG_RBNE); /* 非中断使用 */
USART_GetIntBitState(USART1, USART_INT_RBNE);/* 中断内使用 */

中断方式处理代码如下:

VOID USART1_IRQHandler(VOID)
{if (USART_GetIntBitState(USART1, USART_INT_RBNE) != RESET){if (gUart1RxCount >= DRV_UART1_BUFLEN){memset(gUart1RxBuf, 0, sizeof(gUart1RxBuf));gUart1RxCount = 0;}gUart1RxBuf[gUart1RxCount] = (U8)USART_DataReceive(USART1);gUart1RxCount++;}if (USART_GetIntBitState(USART1, USART_INT_IDLEF) != RESET){gUart1RxBufFlag++;}
}VOID DRV_UART1_InterruptTest(VOID)
{U8 rxCount = 0;while (1){if (gUart1RxBufFlag > 0){for (rxCount = 0; rxCount < gUart1RxCount; rxCount++){UART1_SendChar(gUart1RxBuf[rxCount]);}memset(gUart1RxBuf, 0, sizeof(gUart1RxBuf));gUart1RxCount = 0;gUart1RxBufFlag = 0;}}
}VOID DRV_UART1_InterruptInit(VOID)
{UART1_GpioInit();UART1_Config();UART1_NvicConfiguration();USART_Enable(USART1, ENABLE);USART_INT_Set(USART1, USART_INT_RBNE, ENABLE);USART_INT_Set(USART1, USART_INT_IDLEF, ENABLE);
}
DMA方式

​ 注:DMA细节我会抽单独章节分析,此处只写一个DMA轮询方式的例子。

static VOID UART1_DmaRxConfig(IN U8 *buf, IN U32 len)
{DMA_InitPara DMA_InitStructure;DMA_Enable(DMA1_CHANNEL5, DISABLE);/* USART1 RX DMA1 Channel (triggered by USART1 Rx event) Config */DMA_DeInit(DMA1_CHANNEL5);DMA_InitStructure.DMA_PeripheralBaseAddr = (U32) &(USART1->DR);DMA_InitStructure.DMA_MemoryBaseAddr = (U32)buf;DMA_InitStructure.DMA_DIR = DMA_DIR_PERIPHERALSRC;DMA_InitStructure.DMA_BufferSize = len;DMA_InitStructure.DMA_PeripheralInc = DMA_PERIPHERALINC_DISABLE;DMA_InitStructure.DMA_MemoryInc = DMA_MEMORYINC_ENABLE;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PERIPHERALDATASIZE_BYTE;DMA_InitStructure.DMA_MemoryDataSize = DMA_MEMORYDATASIZE_BYTE;DMA_InitStructure.DMA_Mode = DMA_MODE_NORMAL;DMA_InitStructure.DMA_Priority = DMA_PRIORITY_VERYHIGH;DMA_InitStructure.DMA_MTOM = DMA_MEMTOMEM_DISABLE;DMA_Init(DMA1_CHANNEL5, &DMA_InitStructure);DMA_Enable(DMA1_CHANNEL5, ENABLE);
}VOID DRV_UART1_DmaInit(VOID)
{UART1_GpioInit();UART1_Config();RCC_AHBPeriphClock_Enable(RCC_AHBPERIPH_DMA1, ENABLE);UART1_DmaRxConfig(gUart1RxBuf, DRV_UART1_BUFLEN);USART_Enable(USART1, ENABLE);USART_DMA_Enable(USART1, (USART_DMAREQ_TX | USART_DMAREQ_RX), ENABLE);
}static VOID UART1_DmaSend(IN U8 *buf, IN U32 len)
{DMA_InitPara DMA_InitStructure;DMA_Enable(DMA1_CHANNEL4, DISABLE);/* USART1_Tx_DMA_Channel (triggered by USART1 Tx event) Config */DMA_DeInit(DMA1_CHANNEL4);DMA_InitStructure.DMA_PeripheralBaseAddr = (U32) &(USART1->DR);DMA_InitStructure.DMA_MemoryBaseAddr = (U32)buf;DMA_InitStructure.DMA_DIR = DMA_DIR_PERIPHERALDST;DMA_InitStructure.DMA_BufferSize = len;DMA_InitStructure.DMA_PeripheralInc = DMA_PERIPHERALINC_DISABLE;DMA_InitStructure.DMA_MemoryInc = DMA_MEMORYINC_ENABLE;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PERIPHERALDATASIZE_BYTE;DMA_InitStructure.DMA_MemoryDataSize = DMA_MEMORYDATASIZE_BYTE;DMA_InitStructure.DMA_Mode = DMA_MODE_NORMAL;DMA_InitStructure.DMA_Priority = DMA_PRIORITY_VERYHIGH;DMA_InitStructure.DMA_MTOM = DMA_MEMTOMEM_DISABLE;DMA_Init(DMA1_CHANNEL4, &DMA_InitStructure);DMA_Enable(DMA1_CHANNEL4, ENABLE);while (DMA_GetBitState(DMA1_FLAG_TC4) == RESET){}
}VOID DRV_UART1_DmaTest(VOID)
{while (1){if (USART_GetBitState(USART1, USART_FLAG_IDLEF) != RESET){UART1_DmaSend(gUart1RxBuf, DRV_UART1_BUFLEN);memset(gUart1RxBuf, 0, DRV_UART1_BUFLEN);UART1_DmaRxConfig(gUart1RxBuf, DRV_UART1_BUFLEN);USART_DataReceive(USART1); /* 清除USART_FLAG_IDLEF */}}
}

总结

​ 串口是一种非常常见的通信总线,必须掌握。如果上面的原理和例子理解了,我相信用GPIO口虚拟一个窗口并不是什么难事。

代码路径

https://github.com/YaFood/GD32F103/tree/master/TestUART
https://gitee.com/YaFOOD/GD32F103/tree/master/TestUART

GD32实战6__串口读写相关推荐

  1. 数据分析从零开始实战,Pandas读写Excel/XML数据

    点击查看第一篇文章: 数据分析从零开始实战,Pandas读取HTML页面+数据处理解析_ 数据分析 从零开始到实战,Pandas读写CSV数据_ 数据分析 从零开始到实战,Pandas读写CSV数据 ...

  2. 纯API函数实现串口读写。

    以最后决定用纯API函数实现串口读写. 先从网上搜索相关代码(关键字:C# API 串口),发现网上相关的资料大约来源于一个版本,那就是所谓的msdn提供的样例代码(msdn的具体出处,我没有考证), ...

  3. read接收不全linux,linux下串口读写有关问题 read 一次读不全(5)

    当前位置:我的异常网» Linux/Unix » linux下串口读写有关问题 read 一次读不全 linux下串口读写有关问题 read 一次读不全(5) www.myexceptions.net ...

  4. STM32 HAL库 UART 串口读写功能笔记

    https://www.cnblogs.com/Mysterious/p/4804188.html STM32L0 HAL库 UART 串口读写功能 串口发送功能: uint8_t TxData[10 ...

  5. Linux环境下2410开发板串口读写关键代码

    今天偶然整理原来的项目开发文档,找到了曾经在2410开发板上做的串口读写程序的代码. 现在贴出来供大家参考. #include <qtopia/qpeapplication.h> /*** ...

  6. 基于Arm板linux嵌入式系统RS485串口读写通讯

    最近在做基于Arm板linux嵌入式系统的RS485串口读写通讯首先参考 http://bbs.chinaunix.net/thread-3650543-1-1.html上的文章,该文章写道,读的时候 ...

  7. javaRXTX串口读写,实现浏览器页面设置访问,数据库存储

    serialPortCommunication 码云源码:serialPortCommunication: JAVA实现的串口读写程序,用于不同电台温度模块记录,分为root用户,操作员用户和普通用户 ...

  8. linux串口read几次才能接收完,linux下串口读写有关问题 read 一次读不全

    当前位置:我的异常网» Linux/Unix » linux下串口读写有关问题 read 一次读不全 linux下串口读写有关问题 read 一次读不全 www.myexceptions.net  网 ...

  9. C# API方式串口读写(转自叶帆工作室)

    在调试ICU通信设备的时候,由于串口通信老出现故障,所以就怀疑CF实现的SerialPort类是否有问题,所以最后决定用纯API函数实现串口读写. 先从网上搜索相关代码(关键字:C# API 串口), ...

  10. 数据分析 从零开始到实战,Pandas读写CSV数据

    知识点概要 1.创建一个虚拟python运行环境,专门用于本系列学习: 2.数据分析常用模块pandas安装 3.利用pandas模块读写CSV格式文件 开始动手动脑 1.创建虚拟环境 我平时比较喜欢 ...

最新文章

  1. EF 5.0 帮助类 增删改查
  2. 淘宝网商品库优化实践访谈
  3. cassandra集群搭建
  4. python采集_Python3做采集
  5. 机器学习文献中的英文(part1)
  6. 2021上半年朋友圈都在传的10本书,找到了
  7. 利用sender的Parent获取GridView中的当前行
  8. 李宏毅2017机器学习homework1-利用gradient descent拟合宝可梦CP值代码并利用adagrad进行优化
  9. violin 结构介绍
  10. Lync Server 2010迁移至Lync Server 2013部署系列 Part14:A/V服务器目录迁移
  11. 用数据库表填充下拉列表框
  12. java笔试题库_java笔试题50道 收藏版
  13. Xposed 框架检测机制
  14. matlab 偏相关系数,偏相关分析(spss偏相关性分析结果解读)
  15. 计算机数据库安全研究目的,浅论计算机数据库安全管理
  16. JavaWeb笔记01(mysql)
  17. 使用Fiddler快速保存微信视频号上的视频
  18. srand((unsigned int)time(NULL))的理解(C语言)
  19. 13.深入浅出:负反馈放大电路稳定性(自激振荡)——参考《模拟电子技术基础》清华大学华成英主讲
  20. STM32MP157-Linux音频应用编程-语音转文字项目

热门文章

  1. 重学Java 8新特性 | 第1讲——我们为什么要学习Java 8新特性?
  2. 二级倒立摆建模(一)
  3. 深度学习一(PyTorch物体检测实战)
  4. k3 cloud oracle,调试K3Cloud的管理中心未能加载Oracle问题
  5. 2022广深Java中小厂面试记录
  6. 基于wincc的虚拟电梯设计_基于WinCC的电梯PLC控制仿真.doc
  7. APP专项测试方法和工具的使用
  8. 超全现代虚幻UE4素材网站整理
  9. ARINC429总线收发器 -- HI-3582调试记录
  10. 爱普生690k打印针测试软件_打印机断针测试软件下载