这些小活动你都参加了吗?快来围观一下吧!>>
电子产品世界 » 论坛首页 » 嵌入式开发 » MCU » STM32系统学习——USART(串口通信)

共1条 1/1 1 跳转至

STM32系统学习——USART(串口通信)

高工
2018-12-20 18:39:47     打赏

串口通信是一种设备间非常常用的串行通行方式,其简单便捷,大部分电子设备都支持。 


一、物理层 


常用RS-232标准,主要规定了信号的用途、通信接口以及信号的电平标准。 

 


因为控制器一般使用TTL电平标准,所以常常使用MA3232芯片将TTL以及RS-232电平的信号进行互相转换。


2.RS-232信号线 


最初RS-232串口标准常用于计算机、路由与调制调节器(“猫”)之间通信,设备被分为数据终端设备DTE(计算机、路由)和数据通信设备DCE(调制调节器)。旧台式计算机,一般都有RS-232标准的COM口,也称DB9接口。


DB9信号线说明


下文结合图片看加深理解。 


1、功能引脚 


TX:发送数据输出引脚。 


RX:接收。 


SW_RX:数据接收引脚,属于内部引脚。 


nRTS:请求以发送,n表示低电平有效。如果使能 RTS 流控制,当USART接收器准备好接收新数据时就会将nRTS变成低电平;当接收寄存器已满时,nRTS将被设置为高电平。该引脚只适用于硬件流控制。 


nCTS:清除以发送(Clear To Send),n表示低电平有效。如果使能 CTS流控制,发送器在发送下一帧数据之前会检测 nCTS 引脚,如果为低电平,表示可以发送数据,如果为高电平则在发送完当前数据帧之后停止发送。该引脚只适用于硬件流控制。 


SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。 


USART:下图是STM32F103VET6芯片的USART引脚 

 

USART1的时钟来源于APB2总线时钟,最大频率为72MHZ,其他4个时钟来源于APB1总线时钟,最大频率36MHZ。UART只有异步传输功能,没有SCLK、nCTS和nRTS功能引脚。


其中,f PLCK 为 USART 时钟, USARTDIV 是一个存放在波特率寄存器(USART_BRR)的一个无符号定点数。其中 DIV_Mantissa[11:0]位定义 USARTDIV 的整数部分,DIV_Fraction[3:0]位定义 USARTDIV 的小数部分。 


例如:DIV_Mantissa=24(0x18),DIV_Fraction=10(0x0A),此时 USART_BRR 值为0x18A;那么USARTDIV的小数位10/16=0.625;整数位24,最终USARTDIV的值为24.625。 


如果知道 USARTDIV 值为 27.68,那么 DIV_Fraction=16*0.68=10.88,最接近的正整数为 11,所以 DIV_Fraction[3:0]为 0xB;DIV_Mantissa=整数(27.68)=27,即为 0x1B。 


波特率的常用值有 2400、9600、19200、115200。下面以实例讲解如何设定寄存器值得到波特率的值。 


我们知道 USART1 使用 APB2 总线时钟,最高可达 72MHz,其他 USART 的最高频率为 36MHz。我们选取 USART1 作为实例讲解,即 f PLCK =72MHz。为得到 115200bps 的波特率,此时:115200 =72000000/(16 ∗ USARTDIV)   解 得 USARTDIV=39.0625 , 可 算 得 DIV_Fraction=0.0625*16=1=0x01 ,DIV_Mantissa=39=0x17,即应该设置 USART_BRR 的值为 0x171。


5.校验控制 


STM32F103系列控制器USART支持奇偶校验。使用校验位时,串口传输的长度将在8位数据帧

上加上1位的校验位,总共9位,此时USART_CR1寄存器的M位需要设置位1,即9数据位。将USART_CR1寄存器的PCE位置1就可以启动奇偶校验控制,奇偶校验由硬件自动完成。启动了奇偶校验控制之后,发送数据帧时会自动添加校验位,接收数据自动验证校验位。接收数据时如果出现奇偶校验位验证失败,会将USART_SR寄存器的PE置1,并可以产生奇偶校验中断。 


使用了奇偶校验控制位后,每个字符帧的格式变成了:起始位+数据帧+校验位+停止位。


6.中断控制 



五、USART初始化结构体 


初始化结构体的定义在stm32f10x_usart.h文件中,初始化库函数定义在stm32f10x_usart.c中。


                                    USART初始化结构体


typedef struct {

 uint32_t USART_BaudRate; // 波特率

 uint16_t USART_WordLength; // 字长

 uint16_t USART_StopBits; // 停止位

 uint16_t USART_Parity; // 校验位

 uint16_t USART_Mode; // USART 模式

 uint16_t USART_HardwareFlowControl; // 硬件流控制

 } USART_InitTypeDef;


1)USART_BaudRate:波特率设置。标准库函数会根据设定值计算得到USARTDIV值,从而设置USART_BRR的寄存器值。 


2)USART_WordLength:.数据帧字长,它设定USART_CR1寄存器M位的值。如果没有使能奇偶位校验控制,一般使用8数据位。 


3)USART_StopBits停止位设置。 


4)USART_Parity:奇偶校验控制选择。 


5)USART_Mode:USART模式选择有USART_Mode_Rx和USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定 USART_CR1寄存器的 RE 位和 TE位。 


6)USART_HardwareFlowControl:硬件流控制选择,只有在硬件流控制模式下才有效,可选有,使能RTS、使能CTS、同时使能RTS和CTS、不使用硬件流。


当使用同步模式,需配置SCLK引脚输出脉冲的属性,标准库使用一个时钟初始化结构体USART_ClockInitTypeDef来设置,该结构体内容只有在同步模式下才设置。


                USART时钟初始化结构体


 typedef struct {

 uint16_t USART_Clock; // 时钟使能控制

 uint16_t USART_CPOL; // 时钟极性

 uint16_t USART_CPHA; // 时钟相位

 uint16_t USART_LastBit; // 最尾位时钟脉冲

 } USART_ClockInitTypeDef;


1)USART_Clock:同步模式下SCLK引脚上时钟输出使能控制,可选禁止时钟输出(USART_Clock_Disable)或开启时钟输出(USART_Clock_Enable);如果使用同步模式发送,一般都需要开启时钟。它设定 USART_CR2 寄存器的 CLKEN 位的值。 ݶ 


2)USART_CPOL:同步模式下 SCLK 引脚上输出时钟极性设置,可设置在空闲时SCLK引脚为低电平(USART_CPOL_Low)或高电平(USART_CPOL_High)。它设定USART_CR2寄存器的 CPOL位的值。 


3)USART_CPHA:同步模式下 SCLK 引脚上输出时钟相位设置,可设置在时钟第一个变化沿捕获数据(USART_CPHA_1Edge)或在时钟第二个变化沿捕获数据。它设定 USART_CR2寄存器的 CPHA位的值。USART_CPHA与 USART_CPOL配合使用可以获得多种模式时钟关系。 


4)USART_LastBit:选择在发送最后一个数据位的时候时钟脉冲是否在SCLK引脚输出,可以是不输出脉冲(USART_LastBit_Disable) 、 输出脉冲(USART_LastBit_Enable)。它设定 USART_CR2 寄存器的 LBCL位的值。


六、USART1接发通信实验 


USART 只需两根信号线即可完成双向通信,对硬件要求低,使得很多模块都预留USART 接口来实现与其他模块或者控制器进行数据传输,比如 GSM 模块,WIFI 模块、蓝牙模块等等。在硬件设计时,注意还需要一根“共地线”。 


来编写一个程序实现开发板与电脑通信,在开发板上电时通过USART发送一串字符串给电脑,然后开发板进入中断接收等待状态,如果电脑有发送数据过来,开发板就会产生中断,我们在中断服务函数接收数据,并马上把数据返回发送给电脑。 


1、编程思路 


1) 使能 RX和 TX 引脚 GPIO时钟和 USART时钟; 


2) 初始化 GPIO,并将 GPIO复用到 USART上; 


3) 配置 USART 参数; 


4) 配置中断控制器并使能 USART接收中断; 


5) 使能 USART; 


6) 在 USART接收中断服务函数实现数据接收和发送。 


2、代码分析 


1)GPIO和USART宏定义


 /**

 * 串口宏定义,不同的串口挂载的总线和 IO 不一样,移植时需要修改这几个宏

 */ 

 // 串口 1-USART1

 #define DEBUG_USARTx USART1

 #define DEBUG_USART_CLK RCC_APB2Periph_USART1

 #define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd

 #define DEBUG_USART_BAUDRATE 115200 

 // USART GPIO 引脚宏定义

 #define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)

 #define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd 


 #define DEBUG_USART_TX_GPIO_PORT GPIOA

 #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9

 #define DEBUG_USART_RX_GPIO_PORT GPIOA

 #define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10 


 #define DEBUG_USART_IRQ USART1_IRQn

 #define DEBUG_USART_IRQHandler USART1_IRQHandler


使用宏定义方便程序移植和升级 。开发板中的 CH340G 的收发引脚默认通过跳帽连接到 USART1,如果想使用其他串口,可以把 CH340G 跟 USART1 直接的连接跳帽拔掉,然后再把其他串口的 IO用杜邦线接到 CH340G的收发引脚即可。 


这里我们使用USART1,设定波特率为115200,选定USART的GPIO为PA9和PA10。


2)嵌套向量中断控制器NVIC配置


 static void NVIC_Configuration(void)

 {

 NVIC_InitTypeDef NVIC_InitStructure; 

 /* 嵌套向量中断控制器组选择 */

 NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);


 /* 配置 USART 为中断源 */

 NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;

 /* 抢断优先级为 1 */

 NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;

 /* 子优先级为 1 */

 NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;

 /* 使能中断 */

 NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

 /* 初始化配置 NVIC */

 NVIC_Init(&NVIC_InitStructure);

 }


3)USART初始化配置


 void USART_Config(void)

 {

 GPIO_InitTypeDef GPIO_InitStructure;

 USART_InitTypeDef USART_InitStructure;


 // 打开串口 GPIO 的时钟

 DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);


 // 打开串口外设的时钟

 DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);


 // 将 USART Tx 的 GPIO 配置为推挽复用模式

 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;

 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

 GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);


 // 将 USART Rx 的 GPIO 配置为浮空输入模式

 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;

 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

 GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);


 // 配置串口的工作参数

 // 配置波特率

 USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;

 // 配置 针数据字长

 USART_InitStructure.USART_WordLength = USART_WordLength_8b;

 // 配置停止位

 USART_InitStructure.USART_StopBits = USART_StopBits_1;

 // 配置校验位

 USART_InitStructure.USART_Parity = USART_Parity_No ;

 // 配置硬件流控制

 USART_InitStructure.USART_HardwareFlowControl =

 USART_HardwareFlowControl_None;

 // 配置工作模式,收发一起

 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

 // 完成串口的初始化配置

 USART_Init(DEBUG_USARTx, &USART_InitStructure);


 // 串口中断优先级配置

 NVIC_Configuration();


 // 使能串口接收中断

 USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);


 // 使能串口

 USART_Cmd(DEBUG_USARTx, ENABLE);

 }


调用RCC_APB2PeriphClockCmd函数开启GPIO端口时钟,使用GPIO之前必须开启对应的时钟。RCC_APB2PeriphClockCmd函数开启USART时钟。 


使用GPIO之前需要初始化配置它,并且还要添加特殊设置,因为我们使用它作为外设引脚,一般都有特殊功能,模式设置为复用功能,把串口的Tx引脚配置为复用推挽输出,Rx引脚为浮空输入,数据完全由外部输入决定。 


配置USAT1通信参数为:波特率115200,字长8,1个停止位,没有校验位,不使用硬件流控制,收发一体工作模式,然后调用USART初始化函数完成配置。 


USART接收中断,需要配置NVIC,这里调用NVIC_Configuration函数完成配置,然后调用USART_ITConfig函数使能USART接收中断。 


最后 调用USART_Cmd函数使能USART,最终配置的是USART_CR1的UE位,具体作用是开启USART工作时钟,没有时钟那USART这个外设就工作不了。


4)字符发送


/***************** 发送一个字符 **********************/

 void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)

 {

 /* 发送一个字节数据到 USART */

 USART_SendData(pUSARTx,ch);


 /* 等待发送数据寄存器为空 */

 while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);

 }


 /***************** 发送字符串 **********************/

 void Usart_SendString( USART_TypeDef * pUSARTx, char *str)

 {

 unsigned int k=0;

 do {

 Usart_SendByte( pUSARTx, *(str + k) );

 k++;

 } while (*(str + k)!='\0');


 /* 等待发送完成 */

 while (USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET) {

 }

 }

Usart_SendByte用来指定USART发送一个ASCLL码字符,它有2个形参:第一个

USART第二个待发送的字符,它通过调用库函数USART_SendData来实现等待,并且增加了等待发送完成功能,它接收两个参数:一个是USART,一个是事件标志。这里循环检测发送数据寄存器这个标志,当跳出while循环时,说明发送数据寄存器为空。 


Usart_SendString函数用来发送一个字符串,实际调用Usar_SendByte函数发送每个字符,直到遇到空字符才停止发送。最后使用循环检测发送完成的事件标志TC,保证数据完成后才退出函数。


5)USART中断服务函数


void DEBUG_USART_IRQHandler(void)

 {

 uint8_t ucTemp;

 if (USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET) 

 {

 ucTemp = USART_ReceiveData( DEBUG_USARTx );

 USART_SendData(USARTx,ucTemp);

 }


 }


该代码在stm32f10x_it.c文件中的,用来集中存放中断服务函数。当使能了中断并中断发生时,就会执行这里的中断服务函数。 


6)main函数


int main(void)

 {

 /*初始化 USART 配置模式为 115200 8-N-1,中断接收*/

 USART_Config();


 Usart_SendString( DEBUG_USARTx,"这是一个串口中断接收回显实验\n");


 while (1) {


 }

 }


USART_Config()函数完成USART初始化配置,包括GPIO USART配置,接收中断使能等。 


接下来调用字符发送函数把数据发给串口调试助手。 


最后什么也不做,等待接收中断产生,并在中断服务函数中回传数据。


七、USART1指令控制RGB彩灯实验 


1、思路要点 


1)初始化配置RGB彩色灯GPIO 


2)使能RX和TX引脚GPIO时钟和USART时钟 


3)初始化GPIO,并将GPIO复用到USART上 


4)配置USART参数 


5)使能USART 


6)获取指令输入,根据控制RGB彩色灯 


2、代码分析 


1)GPIO和USART宏定义


1 #define DEBUG_USARTx USART1

2 #define DEBUG_USART_CLK RCC_APB2Periph_USART1

3 #define DEBUG_USART_APBxClkCmd RCC_APB2PeriphClockCmd

4 #define DEBUG_USART_BAUDRATE 115200

6 // USART GPIO 引脚宏定义

7 #define DEBUG_USART_GPIO_CLK (RCC_APB2Periph_GPIOA)

8 #define DEBUG_USART_GPIO_APBxClkCmd RCC_APB2PeriphClockCmd

10 #define DEBUG_USART_TX_GPIO_PORT GPIOA

11 #define DEBUG_USART_TX_GPIO_PIN GPIO_Pin_9

12 #define DEBUG_USART_RX_GPIO_PORT GPIOA

13 #define DEBUG_USART_RX_GPIO_PIN GPIO_Pin_10

14 

15 #define DEBUG_USART_IRQ USART1_IRQn

16 #define DEBUG_USART_IRQHandler USART1_IRQHandler


2)USART初始化配置


1 void USART_Config(void)

2 {

3 GPIO_InitTypeDef GPIO_InitStructure;

4 USART_InitTypeDef USART_InitStructure;

6 // 打开串口 GPIO 的时钟

7 DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);

9 // 打开串口外设的时钟

10 DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);

11 

12 // 将 USART Tx 的 GPIO 配置为推挽复用模式

13 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;

14 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;

15 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

16 GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);

17 

18 // 将 USART Rx 的 GPIO 配置为浮空输入模式

19 GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;

20 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;

21 GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);

22 

23 // 配置串口的工作参数

24 // 配置波特率

25 USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;

26 // 配置 针数据字长

27 USART_InitStructure.USART_WordLength = USART_WordLength_8b;

28 // 配置停止位

29 USART_InitStructure.USART_StopBits = USART_StopBits_1;

30 // 配置校验位

31 USART_InitStructure.USART_Parity = USART_Parity_No ;

32 // 配置硬件流控制

33 USART_InitStructure.USART_HardwareFlowControl =

34 USART_HardwareFlowControl_None;

35 // 配置工作模式,收发一起

36 USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;

37 // 完成串口的初始化配置

38 USART_Init(DEBUG_USARTx, &USART_InitStructure);

39 

40 // 使能串口

41 USART_Cmd(DEBUG_USARTx, ENABLE);

42 }


与上实验基本一样,唯一不同没使用使能中断 


3)重定向printf和scanf函数


1 ///重定向 c 库函数 printf 到串口,重定向后可使用 printf 函数

2 int fputc(int ch, FILE *f)

3 {

4 /* 发送一个字节数据到串口 */

5 USART_SendData(DEBUG_USARTx, (uint8_t) ch);

7 /* 等待发送完毕 */

8 while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TXE) == RESET);

10 return (ch);

11 }

12 

13 ///重定向 c 库函数 scanf 到串口,重写向后可使用 scanf、getchar 等函数

14 int fgetc(FILE *f)

15 {

16 /* 等待串口输入数据 */

17 while (USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_RXNE) == RESET);

18 

19 return (int)USART_ReceiveData(DEBUG_USARTx);

20 }


fputc函数是printf函数内部的一个函数,功能是将字符ch写入文件指针f所指向文件的当前写指针位置,我们使用USART函数重新修改fputc函数内容,达到类似写入的功能。 


fgetc实现字符读取功能。 


还有一点需要注意的,使用 fput和 fgetc函数达到重定向 C语言标准库输入输出函数必须在 MDK的工程选项把“Use MicroLIB”勾选上,MicoroLIB 是缺省 C库的备选库,它对标准 C库进行了高度优化使代码更少,占用更少资源。 


为使用 printf、scanf 函数需要在文件中包含 stdio.h头文件。 


4)输出提示信息


1 static void Show_Message(void)

2 {

3 printf("\r\n 这是一个通过串口通信指令控制 RGB 彩灯实验 \n");

4 printf("使用 USART 参数为:%d 8-N-1 \n",USART_BAUDRATE);

5 printf("开发板接到指令后控制 RGB 彩灯颜色,指令对应如下:\n");

6 printf(" 指令 ------ 彩灯颜色 \n");

7 printf(" 1 ------ 红 \n");

8 printf(" 2 ------ 绿 \n");

9 printf(" 3 ------ 蓝 \n");

10 printf(" 4 ------ 黄 \n");

11 printf(" 5 ------ 紫 \n");

12 printf(" 6 ------ 青 \n");

13 printf(" 7 ------ 白 \n");

14 printf(" 8 ------ 灭 \n");

15 }


5)main函数


1 int main(void)

2 {

3 char ch;

5 /* 初始化 RGB 彩灯 */

6 LED_GPIO_Config();

8 /* 初始化 USART 配置模式为 115200 8-N-1 */

9 USART_Config();

10 

11 /* 打印指令输入提示信息 */

12 Show_Message();

13 while (1)

14 {

15 /* 获取字符指令 */

16 ch=getchar();

17 printf("接收到字符:%c\n",ch);

18 

19 /* 根据字符指令控制 RGB 彩灯颜色 */

20 switch (ch)

21 {

22 case '1':

23 LED_RED;

24 break;

25 case '2':

26 LED_GREEN;

27 break;

28 case '3':

29 LED_BLUE;

30 break;

31 case '4':

32 LED_YELLOW;

33 break;

34 case '5':

35 LED_PURPLE;

36 break;

37 case '6':

38 LED_CYAN;

39 break;

40 case '7':

41 LED_WHITE;

42 break;

43 case '8':

44 LED_RGBOFF;

45 break;

46 default:

47 /* 如果不是指定指令字符,打印提示信息 */

48 Show_Message();

49 break;

50 }

51 }

52 }


首先我们定义一个字符变量来存放接收到的字符。 


接下来调用 LED_GPIO_Config 函数完成 RGB 彩色 GPIO 初始化配置,该函数定义在bsp_led.c 文件内。 


调用 USART_Config 函完成 USART初始化配置。 


Show_Message函数使用 printf 函数打印实验指令说明信息。 


getchar函数用于等待获取一个字符,并返回字符。我们使用 ch变量保持返回的字符,接下来判断 ch内容执行对应的程序。 


我们使用 switch语句判断 ch 变量内容,并执行对应的功能程序。




共1条 1/1 1 跳转至

回复

匿名不能发帖!请先 [ 登陆 注册 ]