文章内容
:
1、了解何为 USART 串口通信,了解串口协议和 RS-232 标准,以及 RS232 电平与 TTL 电平的区别;
了解" USB/TTL转232 "模块(以 CH340 芯片模块为例)的工作原理。
2、完成一个 STM32 的 USART串口通讯程序(查询方式即可,暂不要求采用中断方式)。
要求:1)设置波特率为115200,1位停止位,无校验位;2)STM32系统给上位机(win10)连续发送“hello windows!”。win10 采用“串口助手”工具接收。
目录
1 何为 USART 串口通信
1.1 串口通信简介
- 串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单便捷,因此大部分电子设备都支持该通讯方式,电子工程师在调试设备时也经常使用该通讯方式输出调试信息。
通信方式 | 传输原理 | 优点 | 缺点 |
---|---|---|---|
并行通信 | 数据各个位同时传输 | 抗干扰能力较强 | 速度快、占用引脚资源多 |
串行通信 | 数据按位顺序传输 | 抗干扰能力较弱 | 速度相对较慢、占用引脚资源少 |
- 串行通信的通信方式:
(1)同步通信:带时钟同步信号传输。比如,SPI、IIC通信接口。
(2)异步通信:不带时钟同步信号。比如,UART(通用异步收发器)、单总线。
1.2 串口通讯协议简介
串口通讯协议分为物理层和协议层。
物理层规定通讯系统中具有机械、电子功能部分的特性,确保原始数据在物理媒体的传输。
协议层主要规定通讯逻辑,统一收发双方的数据打包、解包标准。
1.2.1 RS-232 标准(物理层)
RS-232 标准是串口通讯的物理层标准的一种,主要规定了信号的用途、通讯接口以及信号的电平标准。
- RS-232 标准的串口设备间常见的通讯结构 如下图:
两个通讯设备的“DB9 接口”之间通过串口信号线建立起连接,串口信号线中使用“RS-232标准”传输数据信号。由于 RS-232 电平标准的信号不能直接被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的“TTL 标准”的电平信号,才能实现通讯。 - RS-232 标准的电平标准
根据通讯使用的电平标准不同,串口通讯可分为 TTL标准及 RS-232标准。
因为控制器一般使用 TTL 电平标准,所以常常会使用 MA3232 芯片对 TTL 及 RS-232 电平的信号进行互相转换。
1.2.2 协议层
串口通讯的数据包由发送设备通过自身的 TXD 接口传输到接收设备的 RXD 接口。在串口通讯的协议层中,规定了数据包的内容,它由启始位、主体数据、校验位以及停止位组成,通讯双方的数据包格式要约定一致才能正常收发数据。
1.3 STM32 的 USART
通用同步异步收发器(Universal Synchronous Asynchronous Receiver and Transmitter)是一个串行通信设备,可以灵活地与外部设备进行全双工数据交换。
串行通信一般是以帧格式传输数据,即是一帧一帧的传输,每帧包含有起始信号、数据信息、停止信息,可能还有校验信息。USART 就是对这些传输参数有具体规定,当然也不是只有唯一一个参数值,很多参数值都可以自定义设置,只是增强它的兼容性。
1.3.1 USART 的功能
- USART 的功能框图:
具体的 USAR 功能可以参考百度网盘资料:《零死角玩转 STM32F103—指南者》提取码:luha
1.3.2 硬件设计
为利用 USART 实现开发板与电脑通信,需要用到一个 USB 转 USART 的 IC。
我们选择 CH340G 芯片来实现这个功能,CH340G 是一个 USB 总线的转接芯片,实现 USB 转USART、USB 转 lrDA 红外或者 USB 转打印机接口,我们使用其 USB 转 USART 功能。
具体电路设计见下图:
将 CH340G 的 TXD 引脚与 USART1 的 RX 引脚连接,CH340G 的 RXD 引脚与 USART1 的 TX 引脚连接。CH340G 芯片集成在开发板上,其地线(GND)已与控制器的 GND 连通。
我们这次任务暂时还用不到 USB 转串口硬件,大家了解即可。
2 串口输出 hello windows!
通过KEIL编写汇编程序,采用串口助手工具,完成一个输出 “ hello windows!” 到串口的程序。
2.1 Keil 创建新工程
- 先创建一个新的工程,可以参考另一篇文章:基于 MDK 创建汇编语言的STM32工程并分析HEX文件的1.1节。
注意:
选择芯片时可根据自己的型号选择,这里我使用的是STM31FI03C8芯片。
特别注意
:在“Manage Run-Time Environment“界面不用做任何选择,直接点击OK跳过。
2.2 添加汇编文件
-
添加
hellowindows.s
文件
-
在
hellowindows.s
文件中添加下面代码:
;RCC寄存器地址映像
RCC_BASE EQU 0x40021000
RCC_CR EQU (RCC_BASE + 0x00)
RCC_CFGR EQU (RCC_BASE + 0x04)
RCC_CIR EQU (RCC_BASE + 0x08)
RCC_APB2RSTR EQU (RCC_BASE + 0x0C)
RCC_APB1RSTR EQU (RCC_BASE + 0x10)
RCC_AHBENR EQU (RCC_BASE + 0x14)
RCC_APB2ENR EQU (RCC_BASE + 0x18)
RCC_APB1ENR EQU (RCC_BASE + 0x1C)
RCC_BDCR EQU (RCC_BASE + 0x20)
RCC_CSR EQU (RCC_BASE + 0x24)
;AFIO寄存器地址映像
AFIO_BASE EQU 0x40010000
AFIO_EVCR EQU (AFIO_BASE + 0x00)
AFIO_MAPR EQU (AFIO_BASE + 0x04)
AFIO_EXTICR1 EQU (AFIO_BASE + 0x08)
AFIO_EXTICR2 EQU (AFIO_BASE + 0x0C)
AFIO_EXTICR3 EQU (AFIO_BASE + 0x10)
AFIO_EXTICR4 EQU (AFIO_BASE + 0x14)
;GPIOA寄存器地址映像
GPIOA_BASE EQU 0x40010800
GPIOA_CRL EQU (GPIOA_BASE + 0x00)
GPIOA_CRH EQU (GPIOA_BASE + 0x04)
GPIOA_IDR EQU (GPIOA_BASE + 0x08)
GPIOA_ODR EQU (GPIOA_BASE + 0x0C)
GPIOA_BSRR EQU (GPIOA_BASE + 0x10)
GPIOA_BRR EQU (GPIOA_BASE + 0x14)
GPIOA_LCKR EQU (GPIOA_BASE + 0x18)
;GPIO C口控制
GPIOC_BASE EQU 0x40011000
GPIOC_CRL EQU (GPIOC_BASE + 0x00)
GPIOC_CRH EQU (GPIOC_BASE + 0x04)
GPIOC_IDR EQU (GPIOC_BASE + 0x08)
GPIOC_ODR EQU (GPIOC_BASE + 0x0C)
GPIOC_BSRR EQU (GPIOC_BASE + 0x10)
GPIOC_BRR EQU (GPIOC_BASE + 0x14)
GPIOC_LCKR EQU (GPIOC_BASE + 0x18)
;串口1控制
USART1_BASE EQU 0x40013800
USART1_SR EQU (USART1_BASE + 0x00)
USART1_DR EQU (USART1_BASE + 0x04)
USART1_BRR EQU (USART1_BASE + 0x08)
USART1_CR1 EQU (USART1_BASE + 0x0c)
USART1_CR2 EQU (USART1_BASE + 0x10)
USART1_CR3 EQU (USART1_BASE + 0x14)
USART1_GTPR EQU (USART1_BASE + 0x18)
;NVIC寄存器地址
NVIC_BASE EQU 0xE000E000
NVIC_SETEN EQU (NVIC_BASE + 0x0010)
;SETENA寄存器阵列的起始地址
NVIC_IRQPRI EQU (NVIC_BASE + 0x0400)
;中断优先级寄存器阵列的起始地址
NVIC_VECTTBL EQU (NVIC_BASE + 0x0D08)
;向量表偏移寄存器的地址
NVIC_AIRCR EQU (NVIC_BASE + 0x0D0C)
;应用程序中断及复位控制寄存器的地址
SETENA0 EQU 0xE000E100
SETENA1 EQU 0xE000E104
;SysTick寄存器地址
SysTick_BASE EQU 0xE000E010
SYSTICKCSR EQU (SysTick_BASE + 0x00)
SYSTICKRVR EQU (SysTick_BASE + 0x04)
;FLASH缓冲寄存器地址映像
FLASH_ACR EQU 0x40022000
;SCB_BASE EQU (SCS_BASE + 0x0D00)
MSP_TOP EQU 0x20005000
;主堆栈起始值
PSP_TOP EQU 0x20004E00
;进程堆栈起始值
BitAlias_BASE EQU 0x22000000
;位带别名区起始地址
Flag1 EQU 0x20000200
b_flas EQU (BitAlias_BASE + (0x200*32) + (0*4))
;位地址
b_05s EQU (BitAlias_BASE + (0x200*32) + (1*4))
;位地址
DlyI EQU 0x20000204
DlyJ EQU 0x20000208
DlyK EQU 0x2000020C
SysTim EQU 0x20000210
;常数定义
Bit0 EQU 0x00000001
Bit1 EQU 0x00000002
Bit2 EQU 0x00000004
Bit3 EQU 0x00000008
Bit4 EQU 0x00000010
Bit5 EQU 0x00000020
Bit6 EQU 0x00000040
Bit7 EQU 0x00000080
Bit8 EQU 0x00000100
Bit9 EQU 0x00000200
Bit10 EQU 0x00000400
Bit11 EQU 0x00000800
Bit12 EQU 0x00001000
Bit13 EQU 0x00002000
Bit14 EQU 0x00004000
Bit15 EQU 0x00008000
Bit16 EQU 0x00010000
Bit17 EQU 0x00020000
Bit18 EQU 0x00040000
Bit19 EQU 0x00080000
Bit20 EQU 0x00100000
Bit21 EQU 0x00200000
Bit22 EQU 0x00400000
Bit23 EQU 0x00800000
Bit24 EQU 0x01000000
Bit25 EQU 0x02000000
Bit26 EQU 0x04000000
Bit27 EQU 0x08000000
Bit28 EQU 0x10000000
Bit29 EQU 0x20000000
Bit30 EQU 0x40000000
Bit31 EQU 0x80000000
;向量表
AREA RESET, DATA, READONLY
DCD MSP_TOP ;初始化主堆栈
DCD Start ;复位向量
DCD NMI_Handler ;NMI Handler
DCD HardFault_Handler ;Hard Fault Handler
DCD 0
DCD 0
DCD 0
DCD 0
DCD 0
DCD 0
DCD 0
DCD 0
DCD 0
DCD 0
DCD 0
DCD SysTick_Handler ;SysTick Handler
SPACE 20 ;预留空间20字节
;代码段
AREA |.text|, CODE, READONLY
;主程序开始
ENTRY
;指示程序从这里开始执行
Start
;时钟系统设置
ldr r0, =RCC_CR
ldr r1, [r0]
orr r1, #Bit16
str r1, [r0]
;开启外部晶振使能
;启动外部8M晶振
ClkOk
ldr r1, [r0]
ands r1, #Bit17
beq ClkOk
;等待外部晶振就绪
ldr r1,[r0]
orr r1,#Bit17
str r1,[r0]
;FLASH缓冲器
ldr r0, =FLASH_ACR
mov r1, #0x00000032
str r1, [r0]
;设置PLL锁相环倍率为7,HSE输入不分频
ldr r0, =RCC_CFGR
ldr r1, [r0]
orr r1, #(Bit18 :OR: Bit19 :OR: Bit20 :OR: Bit16 :OR: Bit14)
orr r1, #Bit10
str r1, [r0]
;启动PLL锁相环
ldr r0, =RCC_CR
ldr r1, [r0]
orr r1, #Bit24
str r1, [r0]
PllOk
ldr r1, [r0]
ands r1, #Bit25
beq PllOk
;选择PLL时钟作为系统时钟
ldr r0, =RCC_CFGR
ldr r1, [r0]
orr r1, #(Bit18 :OR: Bit19 :OR: Bit20 :OR: Bit16 :OR: Bit14)
orr r1, #Bit10
orr r1, #Bit1
str r1, [r0]
;其它RCC相关设置
ldr r0, =RCC_APB2ENR
mov r1, #(Bit14 :OR: Bit4 :OR: Bit2)
str r1, [r0]
;IO端口设置
ldr r0, =GPIOC_CRL
ldr r1, [r0]
orr r1, #(Bit28 :OR: Bit29)
;PC.7输出模式,最大速度50MHz
and r1, #(~Bit30 & ~Bit31)
;PC.7通用推挽输出模式
str r1, [r0]
;PA9串口0发射脚
ldr r0, =GPIOA_CRH
ldr r1, [r0]
orr r1, #(Bit4 :OR: Bit5)
;PA.9输出模式,最大速度50MHz
orr r1, #Bit7
and r1, #~Bit6
;10:复用功能推挽输出模式
str r1, [r0]
ldr r0, =USART1_BRR
mov r1, #0x271
str r1, [r0]
;配置波特率-> 115200
ldr r0, =USART1_CR1
mov r1, #0x200c
str r1, [r0]
;USART模块总使能 发送与接收使能
;71 02 00 00 2c 20 00 00
;AFIO 参数设置
;Systick 参数设置
ldr r0, =SYSTICKRVR
;Systick装初值
mov r1, #9000
str r1, [r0]
ldr r0, =SYSTICKCSR
;设定,启动Systick
mov r1, #0x03
str r1, [r0]
;NVIC
;ldr r0, =SETENA0
;mov r1, 0x00800000
;str r1, [r0]
;ldr r0, =SETENA1
;mov r1, #0x00000100
;str r1, [r0]
;切换成用户级线程序模式
ldr r0, =PSP_TOP
;初始化线程堆栈
msr psp, r0
mov r0, #3
msr control, r0
;初始化SRAM寄存器
mov r1, #0
ldr r0, =Flag1
str r1, [r0]
ldr r0, =DlyI
str r1, [r0]
ldr r0, =DlyJ
str r1, [r0]
ldr r0, =DlyK
str r1, [r0]
ldr r0, =SysTim
str r1, [r0]
;主循环
main
ldr r0, =Flag1
ldr r1, [r0]
tst r1, #Bit1
;SysTick产生0.5s,置位bit 1
beq main ;0.5s标志还没有置位
;0.5s标志已经置位
ldr r0, =b_05s
;位带操作清零0.5s标志
mov r1, #0
str r1, [r0]
bl LedFlas
mov r0, #'H'
bl send_a_char
mov r0, #'e'
bl send_a_char
mov r0, #'l'
bl send_a_char
mov r0, #'l'
bl send_a_char
mov r0, #'o'
bl send_a_char
mov r0, #' '
bl send_a_char
mov r0, #'w'
bl send_a_char
mov r0, #'i'
bl send_a_char
mov r0, #'n'
bl send_a_char
mov r0, #'d'
bl send_a_char
mov r0, #'o'
bl send_a_char
mov r0, #'w'
bl send_a_char
mov r0, #'s'
bl send_a_char
mov r0, #'!'
bl send_a_char
mov r0, #'\n'
bl send_a_char
b main
;子程序 串口1发送一个字符
send_a_char
push {r0 - r3}
ldr r2, =USART1_DR
str r0, [r2]
b1
ldr r2, =USART1_SR
ldr r2, [r2]
tst r2, #0x40
beq b1
;发送完成(Transmission complete)等待
pop {r0 - r3}
bx lr
;子程序 led闪烁
LedFlas
push {r0 - r3}
ldr r0, =Flag1
ldr r1, [r0]
tst r1, #Bit0
;bit0 闪烁标志位
beq ONLED ;为0 打开led灯
;为1 关闭led灯
ldr r0, =b_flas
mov r1, #0
str r1, [r0]
;闪烁标志位置为0,下一状态为打开灯
;PC.7输出0
ldr r0, =GPIOC_BRR
ldr r1, [r0]
orr r1, #Bit7
str r1, [r0]
b LedEx
ONLED
;为0 打开led灯
ldr r0, =b_flas
mov r1, #1
str r1, [r0]
;闪烁标志位置为1,下一状态为关闭灯
;PC.7输出1
ldr r0, =GPIOC_BSRR
ldr r1, [r0]
orr r1, #Bit7
str r1, [r0]
LedEx
pop {r0 - r3}
bx lr
;异常程序
NMI_Handler
bx lr
HardFault_Handler
bx lr
SysTick_Handler
ldr r0, =SysTim
ldr r1, [r0]
add r1, #1
str r1, [r0]
cmp r1, #500
bcc TickExit
mov r1, #0
str r1, [r0]
ldr r0, =b_05s
;大于等于500次 清零时钟滴答计数器 设置0.5s标志位
;位带操作置1
mov r1, #1
str r1, [r0]
TickExit
bx lr
ALIGN
;通过用零或空指令NOP填充,来使当前位置与一个指定的边界对齐
END
2.3 编译形成hex文件
点击编译形成 hex 文件,警告可忽略。
2.4 串口输出结果
- 硬件需求及连接:
可以参考另一篇文章:STM 寄存器点亮流水灯其中的3.2节
注意
:只需要完成USB转TTL模块
和stm32f103c8t6
的连接,以及 BOOT0 与 BOOT1 的配置。
- 烧录:
通过烧录软件 mcuisp 对板子进行烧录:
可以参考另一篇文章:STM 寄存器点亮流水灯其中的3.3节 - 串口调试:
① 安装串口调试助手:
百度网盘链接:https://pan.baidu.com/s/1L6pOyU-0HqbNTvAOtyxg_Q
提取码:luha
② 打开串口调试助手,在其中打开刚才生成的 hex 文件,并插上板子,选中端口号
③ 设置参数
在串口设置里打开,设置波特率(Baud rate)为115200,1位停止位(Stop bits),无校验位(Parity),这些参数一般已经默认好,一般不用修改。
④ 串口输出效果
点击“打开串口”,可以看到输出效果如下:
3 Keil仿真观察串口输出波形
在没有示波器条件下,可以使用 Keil 的软件仿真逻辑分析仪功能观察管脚的时序波形,更方便动态跟踪调试和定位代码故障点。
现在我们用 Keil5 的仿真逻辑分析功能观察串口输出波形
注意
:具体的 Keil5 仿真逻辑分析波形产生方式可以参考我的上一篇文章:STM32CubeMX实现流水灯及Keil仿真逻辑分析中的第5节。
- 选择需要观测的端口
拖入UEART1_SR
到逻辑分析仪里,并将 Display Type 改为 Bit 显示。
- 开始运行,可以看到串口输出波形
观察发现,串口输出波形会发生高低电平周期性的转换,与串口调试助手输出的结果一致,循环输出 Hello windows! ,波形能正确反映时序状态。
4 总结
文章整体围绕 USART 串口输出的使用,整体比较简单,实现了外部设备与处理器的通信,这便实现了串口通信。串口通信其实还有很多用法,读者可以继续学习。文章如有疏漏差错,敬请读者斧正。
5 参考资料
1、基于 stm32 的应用实例 —— USART 串口通讯(stm32 与主机通讯)
2、基于 MDK 创建 STM32 汇编程序:串口输出 Hello world
3、百度网盘资料:《零死角玩转 STM32F103—指南者》提取码:luha