STMCU小助手
发布时间:2021-11-25 15:00
|
1.从 GPIO 到 UART ; |. X. n& T3 x+ J 前面几节我们讲了MCU如何启动,如何用翻转IO引脚,以及用按键去触发中断。接下来我们介绍的也是最常用的一个模块,串口(UART)。 串口可以说是最古老,而且生命力最强的一种通信接口了。RS485总线更是久经考验。虽然串口早已经从大多数PC的标配中去掉了,但是嵌入式系统跟上位PC机通信用的最多的应该还是通过串口转USB吧。: X% G0 G2 L5 {' n# n3 S 我们用 Keil 打开下面这个工程: STM32Cube_FW_F0_V1.11.0\Projects\STM32F030R8-Nucleo\Examples\UART\UART_TwoBoards_ComPolling\MDK-ARM\Project.uvprojx* z) T+ ]; w9 o , d7 r. H7 s* b 这个代码配置串口为 9600,8 N 1,我们把代码编译下载后,可以通过 UART to USB 转换器连接到 PC 的 USB 口,在PC端用串口观察MCU发送的数据。 b$ @& h1 ~; Q F# U& ^0 U9 K
K" D0 _. `$ Q6 `; t 2.UART 的初始化 我们看一下代码,串口参数的设置是在主程序里完成的,还有一部分是在stm32f0xx_hal_msp.c 里完成的。为什么要这么费事儿,而不把初始化代码全放在一个主程序里完成呢?# k: a$ y4 L, R) V( o - t6 }/ x6 b; N$ I 我们要慢慢体会这样做带来的好处。我们调用一个驱动时,这个驱动难免会跟底层硬件打交道,比如串口驱动,它最终是利用用户选择的某一个串口模块,和与此模块连接的收发引脚进行数据收发的。: }9 G3 L# c8 |# ?* V HAL(Hardware Abstract Layer) 把跟具体硬件细节相关的代码单独剥离了出来,并在Cube库中引入了 MSP(MCU Support Package) 的概念, 具体的硬件细节交给用户在这里面配置。% W4 W% i1 Y2 T& g% _2 D HAL库里面对应每个硬件模块有两个函数 例如: HAL_UART_Init( ) 功能上的描述:设置收发模式、奇偶校验位、停止位数等等(与芯片无关)。3 I7 `( m* ~0 P8 a1 Q, X( B 6 x, @! U4 m2 c" u5 x* x& q) C4 D4 W HAL_UART_MspInit( ) 硬件的描述: IO初始化,不同芯片,不同引脚设置不同。 # S# v( Y7 R! H/ d/ y, k6 s
回到程序,我们要使用串口时要调用驱动层的初始化函数 HAL_UART_Init( ),这个初始化函数回过头来调用了 HAL_UART_MspInit( ) 这个函数来完成 UART 时钟和收发引脚时钟的使能,以及收发引脚的配置。之后初始化函数继续进行 UART 端口的参数配置。 8 T6 U* y- |7 F) I0 f 这样做的一个好处就是使驱动层的初始化函数与硬件无关。一般我们做好一块板子后,所用的串口和引脚也就固定下来了,在 HAL_UART_MspInit( ) 里配置一次就好了,之后不需要频繁的改变这些代码。3 _% Y( {) F4 C( n) j % l& c- b5 H" ^4 @2 ]/ E7 \' Y' ?
\, f- w# t6 u @4 x3 h+ m0 z6 g1 E
3.熟悉 Handle 跟 GPIO 的初始化有所不同,在UART这个模块引入了 Handle 这一概念。在看 Handle 之前我们先熟悉一下在驱动里经常用到的结构体及其指针的用法:$ E9 j8 f4 w0 }" R7 ^ 7 [6 r, l2 m6 x) I" C2 S
typedef struct __MY_TypeDef: c7 }, Q) M( B' ^ |. V$ } 2 I- y4 _: u# Q { uint8_t Var1; 0 T- F( e3 }, \, @# a& K9 c% c uint8_t Var2; j( u+ L4 ~' @- i1 J8 X3 R x" X uint16_t Var3;: u3 d C3 V# x9 r# m uint8_t* Var4; . A; \2 M) t8 G) B/ ?! o/ E }MY_TypeDef;7 X& k I2 {1 G& K4 ? MY_TypeDef* MY_VAR; 6 Q4 C6 s7 h8 y1 v6 Q MY_VAR 是一个 MY_TypeDef 类型的指针,我们看看把它指向不同的地址时会发生什么?$ I4 n- {# U( [' q MY_VAR = (MY_TypeDef*) 0x20000018;
1 |! F$ O8 j' \3 b S 需要注意 MY_VAR->Var4 是个字节型指针变量,这个变量本身占用4个字节,它的值是 0x20000018, 而 * MY_VAR->Var4 的值是 0x02。+ I: b2 ^# Z3 j, f4 ]& t1 ?3 g1 n8 C 把 MY_VAR 指向另一个地址:+ ` b+ s! r# H ~ # ] l0 G' E# D MY_VAR = (MY_TypeDef*) 0x2000001C;, P/ x' [( e( [" g* P6 Y0 y/ ~
4 ?2 J7 N: V0 _( U: s. { 与此类似,对于串口模块,驱动定义了一个结构体类型 UART_HandleTypeDef,我们可以用这个类型定义多个结构体,并通过把串口模块寄存器区的起始地址付给一个结构体,使该结构体和串口之间建立起联系:
我们运行的当前程序操作串口的方式为查询(polling)方式,结构体中和DMA,中断方式相关的内容可以先忽略,只需要关注结构体中下面这些成员即可:) S- `# R% w! w. g0 i" U+ Y( | : Q' P a7 d% D7 i USART_TypeDef *Instance; ! w5 i& `1 |, Z4 u4 ^( f7 q USART_TypeDef 类型的指针,需要指向欲操作的串口寄存器区起始地址。以把此 Handle 和该串口建立起联系。 5 _- B; T- S+ P. q UART_InitTypeDef Init;$ A3 K4 }0 {8 j3 y; e3 F ; U6 @# n/ Y, b' k7 m% k 在调用初始化函数前,需要把初始化参数如 波特率,是否奇偶校验等写入此结构体。 UART_AdvFeatureInitTypeDef AdvancedInit;. t& i. J: f1 @. J1 G 串口扩展功能初始化参数。当前未用到扩展功能。: d' \) I2 f$ n+ i 使用 Handle 的好处是,我们操作某个模块时,把这个模块对应的 Handle 的首地址传给驱动函数就行了。此函数通过 Handle 就可以找到所有需要的东西。如:5 f' z* v# c5 r4 w+ @ r( c; ^" o+ e" ] HAL_UART_Transmit(&UartHandle, (uint8_t*)aTxBuffer, TXBUFFERSIZE, 5000);4 Q e; b& I- `% O &UartHandle 为 UART1 对应的 Handle 的首地址。 Handle 除了保存自己对应模块的参数信息,还保存缓冲数据,以及当前工作状态。它可以保证各模块之间互不干扰,在代码执行过程中被打断,恢复后又可以正确继续执行。这样也便于把驱动集成到操作系统中。在以后的中断方式和 DMA 操作模式中,我们可以更深刻的体会到这种方法的优点。在理解了串口模块的工作方式后,理解其它模块就非常容易了。 需要提到的是,在M0芯片内,有一些共享的或系统级的硬件模块不使用 Handle 的方式来处理: ( R2 w- P/ V' V$ J. o GPIO SYSTICK/ G4 _3 Y- v2 Z. | NVIC% N; `4 O% H* q* D* a" i PWR 5 C, K# U" u! b: s0 r- w) @- }$ E RCC 5 c+ n' s8 s" h7 ]$ e6 q* s FLASH. 8 X* ?. K8 c1 ^+ P2 ?: I |
【评分有奖】STM32 AI Sidekick聊天工具上线,快来体验一下吧!
ST TouchGFX 实操系列视频+如何选择正确的帧缓冲区策略
ST TouchGFX 实操系列视频++如何添加图像
ST TouchGFX 实操系列视频+如何进行屏幕转换
ST TouchGFX 实操系列视频+控件的淡入淡出
ST TouchGFX 实操系列视频+移动控件的三种方法
ST TouchGFX 实操系列视频+如何创建用户界面(UI)
ST TouchGFX 实操系列视频+如何安装TouchGFX Designer和STM32Cube软件环境
【经验分享】STM32F1 GPIO工作原理
【经验分享】STM32F0xx_DMA收发USART数据配置详细过程
微信公众号
手机版