0、友情提示《零死角玩转STM32》系列教程由初级篇、中级篇、高级篇、系统篇、四个部分组成,根据野火STM32开发板旧版教程升级而来,且经过重新深入编写,重新排版,更适合初学者,步步为营,从入门到精通,从裸奔到系统,让您零死角玩转STM32。
M3的世界,与野火同行,乐意惬无边。
另外,野火团队历时一年精心打造的《STM32库开发实战指南》将于今年10月份由机械工业出版社出版,该书的排版更适于纸质书本阅读以及更有利于查阅资料。
内容上会给你带来更多的惊喜。
是一本学习STM32必备的工具书。
敬请期待!1、调试必备-串口(USART1)当我们在学习一款CPU的时候,最经典的实验莫过于流水灯了,会了流水灯的话就基本等于学会会操作I/O口了。
那么在学会操作I/O之后,面对那么多的片上外设我们又应该先学什么呢?有些朋友会说用到什么就学什么,听起来这也不无道理呀。
但对于野火来说会把学习串口的操作放在第二位。
在程序运行的时候我们可以通过点亮一个LED来显示代码的执行的状态,但有时候我们还想把某些中间量或者其他程序状态信息打印出来显示在电脑上,那么这时串口的作用就可想而知了。
1.1 异步串口通讯协议阅读过《STM32中文参考手册》的读者会发现,STM32的串口非常强大,它不仅支持最基本的通用串口同步、异步通讯,还具有LIN总线功能(局域互联网)、IRDA功能(红外通讯)、SmartCard功能。
为实现最迫切的需求,利用串口来帮助我们调试程序,本章介绍的为串口最基本、最常用的方法,全双工、异步通讯方式。
图 1-1为串口异步通讯协议。
图 1-1 异步串口通讯协议重温串口的通讯协议,我们知道要配置串口通讯,至少要设置以下几个参数:字长(一次传送的数据长度)、波特率(每秒传输的数据位数)、奇偶校验位、还有停止位。
对ST库函数的使用已经上手的读者应该能猜到,在初始化串口的时候,必然有一个串口初始化结构体,这个结构体的几个成员肯定就是有来存储这些控制参数的。
1.2 直通线和交叉线野火STM32开发板串口硬件原理图图 1-2 野火开发板串口硬件图见图 1-2,这是野火STM32开发板的接线图,使用的为MAX3232芯片,把STM32的PA10引脚(复用功能为USART1的Rx)接到了DB9接口的第2针脚,把PA9引脚(复用功能为USART的Tx)连接到了DB9接口的第3针脚。
Tx(发送端)接第3针脚,Rx(接收端)接第2针脚。
这种接法是跟PC 的串口接法一样的,如果要实现PC跟野火板子通讯,就要使用两头都是母的交叉线。
串口线主要分两种,直通线(平行线)和交叉线。
它们的区别见图 1-3。
假如PC与板子之间要实现全双工串口通讯,必然是PC的Tx针脚要连接到板子的Rx针脚,而PC的Rx针脚则要连接至板子的Tx针脚了。
由于板子和pc 的串口接法是相同的,就要使用交叉线来连接了。
如果有的开发板是Tx连接至DB9的第2针脚,而Rx连接至第3针脚,这与PC接法是相反的,这样的板子与PC通讯就需要使用直通线了。
为什么野火板子要使用PC的接法?假如使用非PC接法,由于板子与PC的接法相反,通讯就要使用直通线;但两个板子之间想要进行串口通讯时,由于接法相同,就要使用交叉线。
如果使用PC接法,板子与PC之间接法相同,通讯使用交叉线;两个相同板子之间接法也相同,通讯也是使用交叉线。
所以野火建议大家设计板子时,尽量采用与PC相同的标准串口接法。
图 1-3 交叉线与直通线的区别介绍直通线与交叉线的区别,一来是野火发现某些读者因为线的问题而花费了大量宝贵的时间。
二来是介绍串口线的DIY方法。
要实现基本的全双工异步通讯,只要3条线,分别为Rx、Tx、和GND。
如果读者正在为直通线、交叉线、公头、母头不匹配而烦恼,可以根据自己的需要,参照图 1-3,用三根杜邦线连接即可。
1.3 串口工作过程分析图 1-4 串口架构图串口外设的架构图看起来十分复杂,实际上对于软件开发人员来说,我们只需要大概了解串口发送的过程即可。
从下至上,我们看到串口外设主要由三个部分组成,分别是波特率的控制部分、收发控制部分及数据存储转移部分。
1.3.1波特率控制波特率,即每秒传输的二进制位数,用 b/s (bps)表示,通过对时钟的控制可以改变波特率。
在配置波特率时,我们向波特比率寄存器USART_BRR写入参数,修改了串口时钟的分频值USARTDIV。
USART_BRR寄存器包括两部分,分别是DIV_Mantissa(USARTDIV的整数部分)和DIVFraction(USARTDIV 的小数)部分,最终,计算公式为USARTDIV=DIV_Mantissa+(DIVFraction/16)。
USARTDIV是对串口外设的时钟源进行分频的,对于USART1,由于它是挂载在APB2总线上的,所以它的时钟源为f PCLK2;而USART2、3挂载在APB1上,时钟源则为f PCLK1,串口的时钟源经过USARTDIV分频后分别输出作为发送器时钟及接收器时钟,控制发送和接收的时序。
1.3.2收发控制围绕着发送器和接收器控制部分,有好多个寄存器:CR1、CR2、CR3、SR,即USART的三个控制寄存器(Control Register)及一个状态寄存器(Status Register)。
通过向寄存器写入各种控制参数,来控制发送和接收,如奇偶校验位,停止位等,还包括对USART中断的控制;串口的状态在任何时候都可以从状态寄存器中查询得到。
具体的控制和状态检查,我们都是使用库函数来实现的,在此就不具体分析这些寄存器位了。
1.3.3数据存储转移部分收发控制器根据我们的寄存器配置,对数据存储转移部分的移位寄存器进行控制。
当我们需要发送数据时,内核或DMA外设(一种数据传输方式,在下一章介绍)把数据从内存(变量)写入到发送数据寄存器TDR后,发送控制器将适时地自动把数据从TDR加载到发送移位寄存器,然后通过串口线Tx,把数据一位一位地发送出去,在数据从TDR转移到移位寄存器时,会产生发送寄存器TDR已空事件TXE,当数据从移位寄存器全部发送出去时,会产生数据发送完成事件TC,这些事件可以在状态寄存器中查询到。
而接收数据则是一个逆过程,数据从串口线Rx一位一位地输入到接收移位寄存器,然后自动地转移到接收数据寄存器RDR,最后用内核指令或DMA 读取到内存(变量)中。
1.4 串口通讯实验分析1.4.1实验描述及工程文件清单实验描述重新实现C库中的printf() 函数到串口1,这样我们就可以像用C库中的printf() 函数一样将信息通过串口打印到电脑,非常方便我们程序的调试。
硬件连接PA9 - USART1(Tx)PA10 - USART1(Rx)用到的库文件startup/start_stm32f10x_hd.cCMSIS/core_cm3.cCMSIS/system_stm32f10x.cFWlib/stm32f10x_gpio.cFWlib/stm32f10x_rcc.cFWlib/stm32f10x_usart.c用户编写的文件USER/main.cUSER/stm32f10x_it.cUSER/usart1.c1.4.2 配置工程环境串口实验中我们用到了GPIO、RCC、USART这三个外设的库文件stm32f10x_gpio.c、stm32f10x_rcc.c、stm32f10x_usart.c,所以我们先要把这个库文件添加进工程,新建用户文件usart1.c 。
并在stm32f10x_conf.h 中把相应的头文件的注释去掉。
1./**2. ****************************************************************3. * @file Project/STM32F10x_StdPeriph_Template/stm32f10x_conf.h4. * @author MCD Application Team5. * @version V3.5.06. * @date 08-April-20117. * @brief Library configuration file.8. ******************************************************** /9.10.#include "stm32f10x_gpio.h"11.#include "stm32f10x_rcc.h"12.#include "stm32f10x_usart.h"1.4.3 main文件配置好要用的库的环境之后,我们就从main函数看起,层层剥离源代码。
1./*2. * 函数名:main3. * 描述:主函数4. * 输入:无5. * 输出:无6. */7.int main(void)8.{9./* USART1 config 115200 8-N-1 */10. USART1_Config();11.12. printf("\r\n this is a printf demo \r\n");13.14. printf("\r\n 欢迎使用野火M3实验板:) \r\n");15.16. USART1_printf(USART1, "\r\n This is a USART1_printf demo \r\n");17.18. USART1_printf(USART1, "\r\n ("__DATE__ " -" __TIME__ ") \r\n");19.20.for(;;)21. {22.23. }24.}首先调用函数USART1_Config(),函数USART1_Config()主要做了如下工作:1.使能了串口1的时钟2.配置好了usart1的I/O3.配置好了usart1的工作模式,具体为波特率为 115200 、8个数据位、1个停止位、无硬件流控制。
即 115200 8-N-1。
1.4.4 USART初始化配置具体的USART1_Config()在usart1.c这个用户文件中实现:1./*2. * 函数名:USART1_Config3. * 描述:USART1 GPIO 配置,工作模式配置。
115200 8-N-14. * 输入:无5. * 输出 : 无6. * 调用:外部调用7. */8.void USART1_Config(void)9.{10. GPIO_InitTypeDef GPIO_InitStructure;11. USART_InitTypeDef USART_InitStructure;12.13./* config USART1 clock */14. RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);15.16./* USART1 GPIO config */17./* Configure USART1 Tx (PA.09) as alternate function push-pull */18. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;19. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;20. GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;21. GPIO_Init(GPIOA, &GPIO_InitStructure);22./* Configure USART1 Rx (PA.10) as input floating */23. GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;24. GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;25. GPIO_Init(GPIOA, &GPIO_InitStructure);26.27./* USART1 mode config */28. USART_ART_BaudRate = 115200;29. USART_ART_WordLength = USART_WordLength_8b;30. USART_ART_StopBits = USART_StopBits_1;31. USART_ART_Parity = USART_Parity_No ;32. USART_ART_HardwareFlowControl = USART_HardwareFlowControl_None;33. USART_ART_Mode = USART_Mode_Rx | USART_Mode_Tx;34. USART_Init(USART1, &USART_InitStructure);35. USART_Cmd(USART1, ENABLE);36.}在代码的第14行,调用了库函数RCC_APB2PeriphClockCmd()初始化了USART1和GPIOA的时钟,这是因为使用了GPIOA的PA9和PA10的默认复用USART1的功能,在使用复用功能的时候,要开启相应的功能时钟USART1。