USART 串口通信配置步骤 在上面的介绍中,可能有的朋友很不理解,不过没有关系,下面我们讲解如何使用库函数对 USART 进行配置。这个也是在编写程序中必须要了解的。具体步骤如下:(USART 相关库函数在 stm32f10x_usart.c 和 stm32f10x_usart.h文件中) (1)使能串口时钟及GPIO端口时钟 前面说过 STM32F103ZET6芯片具有 5 个串口,对应不同的引脚,串口1 挂接在 APB2 总线上,串口 2-串口 5 挂接在 APB1 总线上,根据自己所用串口使能总线时钟和端口时钟。例如使用 USART1,其挂接在 APB2 总线上,并且 USART1 对应 STM32F103ZET6 芯片管脚的PA9 和 PA10,因此使能时钟函数如下: - <font face="Tahoma"><font size="3">RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); //使能 GPIOA时钟</font></font><font face="Tahoma"><font size="3">RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);// 使 能USART1时钟</font></font>
复制代码
(2)GPIO 端口模式设置,设置串口对应的引脚为复用功能 因为使用引脚的串口功能,所以在配置 GPIO 时要将设置为复用功能,这里把串口的 Tx 引脚配置为复用推挽输出, Rx 引脚为浮空输入,数据完全由外部输入决定。如下: - GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX //串口输出PA9
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
- GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化串口输入IO */
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX //串口输入PA10
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //模拟输入
- GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化 GPIO */
复制代码
(3)初始化串口参数,包含波特率、字长、奇偶校验等参数 要使用串口功能,必须对串口通信相关参数初始化,其库函数如下: - void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef*USART_InitStruct);
复制代码
想必不用说,大家也知道第一个参数是什么意思,它是用来选择串口。第二个参数是一个结构体指针变量,结构体类型是 USART_InitTypeDef,其内包含了串口初始化的成员变量。下面我们就来看下这个结构体: - 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;
复制代码
下面就来简单介绍下每个成员变量的功能: USART_BaudRate:波特率设置。常用的波特率为 4800、9600、115200 等。 标准库 函 数 会 根 据 设 定 值 计 算 得 到 USARTDIV 值 , 并 设 置 USART_BRR 寄存器值。 USART_WordLength:数据帧字长。可以选择为 8 位或者 9 位, 通过 USART_CR1寄存器的 M 位的值决定。如果没有使能奇偶校验控制,一般使用 8 数据位;如果使能了奇偶校验则一般设置为 9 数据位。 USART_StopBits:停止位设置。可选 0.5 个、 1 个、 1.5 个和 2 个停止 位, 它设定 USART_CR2 寄存器的 STOP[1:0]位的值, 一般我们选择 1 个停止位。 USART_Parity:奇偶校验控制选择。可选 USART_Parity_No( 无 校 验 ) 、USART_Parity_Even( 偶 校 验 ) 以 及 USART_Parity_Odd( 奇 校 验 ) ,它设 定USART_CR1 寄存器的 PCE 位和 PS 位的值。 USART_Mode:USART模式选择。可以为 USART_Mode_Rx 和 USART_Mode_Tx,允许使用逻辑或运算选择两个,它设定 USART_CR1 寄存器的 RE 位和 TE 位。 USART_HardwareFlowControl:硬件流控制选择。只有在硬件流控制模式才有效,可以选择无硬件 流 USART_HardwareFlowControl_None、RTS 控制USART_HardwareFlowControl_RTS、CTS 控制USART_HardwareFlowControl_CTS、RTS 和CTS 控制USART_HardwareFlowControl_RTS_CTS。 了解结构体成员功能后,就可以进行配置,例如我们配置 USART1,如下: - USART_InitTypeDef USART_InitStructure;
- USART_InitStructure.USART_BaudRate = 9600;//波特率设置
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为 8位数据格式
- 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(USART1, &USART_InitStructure); //初始化串口1
复制代码
(5)使能串口 配置好串口后,我们还需要使能它,使能串口库函数如下: - void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState);
复制代码
例如我们要使能 USART1,如下: - USART_Cmd(USART1, ENABLE); //使能串口 1
复制代码
(6)设置串口中断类型并使能 对串口中断类型和使能设置的函数如下: - void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT,FunctionalState NewState);
复制代码
第一个参数用来选择串口,第二个参数用来选择串口中断类型,第三个参数用来使能或者失能对应中断。由于串口中断类型比较多,所以使用哪种中断,我们就需要对它进行配置。比如在接收到数据的时候( RXNE 读数据寄存器非空),我们要产生中断,那么我们开启中断的方法是: - USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启接收中断
复制代码
又比如我们发送完数据时,要产生中断,可以配置如下: - USART_ITConfig(USART1,USART_IT_TC, ENABLE);
复制代码
对应的串口中断类型可在stm32f10x_usart.h 中查找到,如下: - #define USART_IT_PE ((uint16_t)0x0028)
- #define USART_IT_TXE ((uint16_t)0x0727)
- #define USART_IT_TC ((uint16_t)0x0626)
- #define USART_IT_RXNE ((uint16_t)0x0525)
- #define USART_IT_IDLE ((uint16_t)0x0424)
- #define USART_IT_LBD ((uint16_t)0x0846)
- #define USART_IT_CTS ((uint16_t)0x096A)
- #define USART_IT_ERR ((uint16_t)0x0060)
- #define USART_IT_ORE ((uint16_t)0x0360)
- #define USART_IT_NE ((uint16_t)0x0260)
- #define USART_IT_FE ((uint16_t)0x0160)
复制代码
(7)设置串口中断优先级,使能串口中断通道 在上一步我们已经使能了串口的接收中断, 只要使用到中断, 就必须对NVIC初始化,NVIC 初始化库函数是 NVIC_Init(),这个在前面讲解 STM32 中断时就已经介绍过,不清楚的可以回过头看下。 (8)编写串口中断服务函数 最后我们还需要编写一个串口中断服务函数, 通过中断函数处理串口产生的相关中断。串口中断服务函数名在 STM32F1 启动文件内就有,USART1 中断函数名如下:
因为串口的中断类型有很多,所以进入中断后,我们需要在中断服务函数开头处通过状态寄存器的值判断此次中断是哪种类型,然后做出相应的控制。库函数中用来读取串口中断状态标志位的函数如下: - ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_tUSART_IT);
复制代码
此函数功能是判断 USARTx的中断类型 USART_IT 是否产生中断, 例如我们要判断 USART1 的接收中断是否产生,可以调用此函数: - if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
- {
- ...//执行 USART1接收中断内控制
- }
复制代码
如果产生接收中断,那么调用 USART_GetITStatus 函数后返回值为 1,就会进入到if 函数内执行中断控制功能程序。否则就不会进入中断处理程序。 在编写串口中断服务函数时,最后通常会调用一个清除中断标志位的函数,如下: - void USART_ClearFlag(USART_TypeDef* USARTx, uint16_t USART_FLAG);
复制代码
第二个参数为状态标志选项,可选参数可在 stm32f10x_usart.h中查找到,如下: - #define USART_FLAG_CTS
- #define USART_FLAG_LBD
- #define USART_FLAG_TXE
- #define USART_FLAG_TC
- #define USART_FLAG_RXNE
- #define USART_FLAG_IDLE
- #define USART_FLAG_ORE
- #define USART_FLAG_NE
- #define USART_FLAG_FE
- #define USART_FLAG_PE
复制代码
比如本实验中判断串口进入接收中断后, 我们就会把串口接收寄存器内数据读取出来,然后再通过串口发送至上位机,等待发送完成后我们就会清除发送完成标志位 USART_FLAG_TC。代码如下: - void USART1_IRQHandler(void) //串口 1中断服务程序
- {
- u8 r;
- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
- {
- r =USART_ReceiveData(USART1);//(USART1->DR); //读取接收到的数据
- USART_SendData(USART1,r);
- while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
- }
- USART_ClearFlag(USART1,USART_FLAG_TC);
- }
复制代码
串口接收函数是: - uint16_t USART_ReceiveData(USART_TypeDef* USARTx);
复制代码
串口发送函数是: - void USART_SendData(USART_TypeDef* USARTx, uint16_t Data);
复制代码
库函数中还有一个函数用来读取串口状态标志位: - FlagStatus USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_tUSART_FLAG);
复制代码
USART_GetITStatus 与 USART_GetFlagStatus 功 能 类 似 , 区 别 就 是USART_GetITStatus 函数会先判断是否使能串口中断,使能后才读取状态标志,而 USART_GetFlagStatus函数直接读取状态标志。 将以上几步全部配置好后,我们就可以正常使用串口中断了。
硬件设计 在我们开发板上,含有一个 USB 转串口,一个 RS232串口。 通过 CH340 芯片把 STM32F1 的串口 1 与 PC 机的USB 口进行连接,实现串口通信。电路是按照 RS232 接口标准搭建,使用了一个 DB9 的**和母头,电平转换芯片使用的是 SP3232,与 MAX3232 一样。母头可作为下位机和上位机 PC 进行串口通信,**可作为上位机和其他母头设备进行串口通信,当然也可以和计算机的**进行通信,需使用交叉型串口线。
软件设计 本章所要实现的功能是:STM32F1 通过 USART1 实现与 PC 机对话,STM32F1的 USART1 收到 PC 机发来的数据后原封不动的返回给 PC 机显示。同时使用 D1指示灯不断闪烁提示系统正常运行。程序框架如下: (1)初始化 USART1,并使能串口接收中断等 (2)编写 USART1 中断函数 (3)编写主函数 在前面介绍串口配置步骤时,就已经讲解如何初始化串口。下面我们打开 “USART 串口通信实验”工程,在 Public 工程组中可以看到添加了 usart.c 文件,在 StdPeriph_Driver 工程组中添加了stm32f10x_usart.c 库文件。串口操作的库函数都放在 stm32f10x_usart.c 和stm32f10x_usart.h 文件中,所以使用到串口就必须加入 stm32f10x_usart.c文件,同时还要包含对应的头文件路径。
USART1初始化函数 要使用串口中断,我们必须先对它进行配置。USART1 初始化代码如下: - /****************************************************************
- * 函 数 名 : USART1_Init
- * 函数功能 : USART1 初始化函数
- * 输 入 : bound:波特率
- * 输 出 : 无
- ******************************************************************/
- void USART1_Init(u32 bound)
- {
- //GPIO 端口设置
- GPIO_InitTypeDef GPIO_InitStructure;
- USART_InitTypeDef USART_InitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
- /* 配置 GPIO 的模式和IO 口 */
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;//TX //串口输出 PA9
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
- GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化串口输入IO */
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;//RX //串口输入 PA10
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING; //模拟输入
- GPIO_Init(GPIOA,&GPIO_InitStructure); /* 初始化 GPIO *//*USART1 初始化设置*/
- USART_InitStructure.USART_BaudRate = bound;//波特率设置
- USART_InitStructure.USART_WordLength = USART_WordLength_8b;//字长为8 位数据格式
- 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(USART1, &USART_InitStructure); //初始化串口1
- USART_Cmd(USART1, ENABLE); //使能串口 1
- USART_ClearFlag(USART1, USART_FLAG_TC);
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);//开启相关中断
- /*Usart1 NVIC配置*/
- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;//串口 1中断通道
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3;// 抢 占优先级3
- NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级 3
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
- NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化 VIC 寄存器、
- }
复制代码
在USART1_Init()函数中, 首先使能 USART1 串口及端口时钟, 并初始化GPIO为复用功能。其次配置串口结构体 USART_InitTypeDef,使能串口并开启接收中断,为了防止串口发送状态标志位的影响,我们清除下串口状态标志位(TC),最后配置相应的 NVIC 并使能对应中断通道, 我们将 USART1 的抢占优先级设置为3,响应优先级设置为 3。这一过程在前面步骤介绍中已经提了。 USART1_Init()函数有一个参数 bound,用来设置 USART1 串口的波特率,方便大家修改。
USART1中断函数 初始化 USART1 后, 接收中断就已经开启了, 当上位机发送数据过来, STM32F1的串口接收寄存器内即为非空,触发接收中断,具体代码如下: - /****************************************************************
- * 函 数 名 : USART1_IRQHandler
- * 函数功能 : USART1 中断函数
- * 输 入 : 无
- * 输 出 : 无
- *****************************************************************/
- void USART1_IRQHandler(void)
- {
- u8 rec_data;
- char r2='b';
- char r3='c';
-
- if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //½ÓÊÕÖжÏ
- {
- rec_data =USART_ReceiveData(USART1);//(USART1->DR); //¶ÁÈ¡½ÓÊÕµ½µÄÊý¾Ý
- if(rec_data == '2')
- {
- USART_SendData(USART1,r2);
- led2=!led2;
- delay_ms(3000);
- while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
- }
- else if(rec_data == '3')
- {
- USART_SendData(USART1,r3);
- led3=!led3;
- delay_ms(3000);
- while(USART_GetFlagStatus(USART1,USART_FLAG_TC) != SET);
- }
- LED_Init();
- }
- USART_ClearFlag(USART1,USART_FLAG_TC);
- }
复制代码
为了确认 USART1 是否发生接收中断,调用了读取串口中断状态标志位函数USART_GetITStatus,如果确实产生接收中断事件,那么就会执行if内的语句,将串口接收到的数据保存在变量 r 内,然后有通过串口发送出去,通过USART_GetFlagStatus 函数读取串口状态标志, 如果数据发送完成, 则退出while循环语句,且清除发送完成状态标志位 USART_FLAG_TC。 主函数 编写好串口初始化和中断服务函数后,接下来就可以编写主函数了,代码如 下: - /***************************************************************
- * 函 数 名 : main
- * 函数功能 : 主函数
- * 输 入 : 无
- * 输 出 : 无
- ****************************************************************/
- int main()
- {
- u8 i=0;
- SysTick_Init(72);
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级
- 分组 分2 组
- LED_Init();
- USART1_Init(9600);
- while(1)
- {
- i++;
- if(i%20==0)
- {
- led1=!led1;
- }
- delay_ms(10);
- }
- }
复制代码
主函数实现的功能很简单,首先调用之前编写好的硬件初始化函数,包括 SysTick 系统时钟,中断分组,LED 初始化等。然后调用我们前面编写的USART1初始化函数,这里我们设定串口通信波特率为 9600。最后进入 while 循环语句,不断让 D1 指示间隔 200ms 闪烁。如果发生接收中断事件,即会进入中断执行,执行完后回到主函数内继续运行。 其实如果你学会了 USART1的使用,对于其他的串口都是类似的。
Labview程序
实验现象 将工程程序编译后下载到开发板内,可以看到 D1 指示灯不断闪烁,表示程序正常运行。打开Labview程序,选择FALSE或者TRUE,上位机分别发送‘1’、‘2’到下位机,可以分别看到触发中断发生时D1熄灭,同时D2或者D3闪亮,之后read buffer接受到单片机发送给上位机的‘b’或者‘c’。
|