串口USART
实验目的- 掌握STM32 USART串行口原理。
- 掌握STM32 DMA使用的优势。
实验内容- 编写程序实现串行口简单的收发通信。
- 编写程序实现使用printf函数实现串行口发送。
- 编写程序实现DMA方式进行串行口收发通信。
硬件设计开发板串口硬件电路IK-ZET6开发板上设计了USB转TTL电路,它的主要作用有2个:
- USB转串口通讯,通过USB数据线连接到计算机的USB口即可使用串口通信功能。
- 开发板供电通过USB可以为开发板供电(计算机USB可以提供500mA的电流)。
USB转TTL电路如下图所示,串口接收和发送的管脚上均连接了LED指示灯,收发数据时指示灯会闪烁,这样,更方便我们从硬件的角度观察串口有没有在进行数据收发。电路中的1000mA自恢复保险丝用于保护开发板板和计算机USB口。
图1:开发板USB转TTL电路
表1:串口电路引脚分配
USART | 功能描述 | 引脚 | 说明 |
USART_TX | 串口发送 | PA9 | 独立gpio |
USART_RX | 串口接收 | PA10 | 独立GPIO |
- 注:独立GPIO表示开发板没有其他的电路使用这个GPIO。
STM32串行口简介STM32F103ZET6微处理器有3组USART,2组UART。并且3组USART均带硬件流控。下表列举出各组串行口所占用MCU的引脚情况。
表2:STM32F103ZET6串行口相关引脚
序号 | 串行口 | 引脚名 | 对应IO口 | 功能描述 | 备注 |
1 | USART1 | USART1_RX | PA10 | 串口接收 | APB2总线 |
2 | USART1_TX | PA9 | 串口发送 |
3 | USART1_CK | PA8 | 串口同步时钟线 |
4 | USART1_RTS | PA12 | 串口硬件流控RTS |
5 | USART1_CTS | PA11 | 串口硬件流控CTS |
6 | USART2 | USART2_RX | PA3 | 串口接收 | APB1总线 |
7 | USART2_TX | PA2 | 串口发送 |
8 | USART2_CK | PA4 | 串口同步时钟线 |
9 | USART2_RTS | PA1 | 串口硬件流控RTS |
10 | USART2_CTS | PA0 | 串口硬件流控CTS |
11 | USART3 | USART3_RX | PB11 | 串口接收 | APB1总线 |
12 | USART3_TX | PB10 | 串口发送 |
13 | USART3_CK | PB12 | 串口同步时钟线 |
14 | USART3_RTS | PB14 | 串口硬件流控RTS |
15 | USART3_CTS | PB13 | 串口硬件流控CTS |
16 | UART4 | UART4_RX | PC11 | 串口接收 | APB1总线 |
17 | UART4_TX | PC10 | 串口发送 |
18 | UART5 | UART5_RX | PD2 | 串口接收 | APB1总线 |
19 | UART5_TX | PC12 | 串口发送 |
- 注:针对STM32F103其他型号MCU要根据引脚数、封装等信息去确定有关串行口资源。
STM32F103ZET6微处理器USART框图如下图所示,大致可分4个部分,MCU功能引脚是画原理图使用的芯片引脚,其它3个部分均在芯片内部集成,用户如需详细知晓其每一块的原理可参考数据手册研读。
图2:USART框图
- 注:由上图可知对数据、移位等寄存器的操作既可以CPU实现,也可以DMA实现。关于DMA方式实现USART串行口数据收发可结合DMA原理部分进行理解。
软件设计USART寄存器汇集STM32F103提供了7个用于操作USART的寄存器,如下表所示:
表3:USART相关寄存器
序号 | 寄存器名 | 读/写 | 功能描述 |
1 | USART_SR | 只读 | 状态寄存器。 |
2 | USART_DR | 读/写 | 数据寄存器。 |
3 | USART_BRR | 读/写 | 波特率寄存器。 |
4 | USART_CR1 | 读/写 | 控制寄存器1。 |
5 | USART_CR2 | 读/写 | 控制寄存器2。 |
6 | USART_CR3 | 读/写 | 控制寄存器3。 |
7 | USART_GTPR | 读/写 | 保护时间和预分频寄存器。 |
每一种寄存器详细的描述在这里不做具体的介绍,大家可以参考目录:“第1部分:开发板硬件资料”--->“2 - 芯片资料”中“STM32英文参考手册_V15”或“STM32中文参考手册_V10”对应的USART章节的寄存器部分认真研读。
USART库函数汇集官方提供的最终库函数版本是V3.5版本,该版本库函数提供了25个与USART操作有关的库函数,如下表所示:
表4:USART相关库函数汇集
序号 | 函数名 | 功能描述 |
1 | USART_DeInit | 将外设USARTx寄存器重设为缺省值。 |
2 | USART_Init | 根据USART_InitStruct中指定的参数初始化外设USARTx寄存器。 |
3 | USART_StructInit | 把USART_InitStruct中的每一个参数按缺省值填入。 |
4 | USART_Cmd | 使能或者失能USART外设。 |
5 | USART_ITConfig | 使能或者失能指定的USART中断。 |
6 | USART_DMACmd | 使能或者失能指定USART的 DMA请求。 |
7 | USART_SetAddress | 设置USART节点的地址。 |
8 | USART_WakeUpConfig | 选择USART的唤醒方式。 |
9 | USART_ReceiverWakeUpCmd | 检查USART是否处于静默模式。 |
10 | USART_LINBreakDetectLengthConfig | 设置USART LIN中断检测长度。 |
11 | USART_LINCmd | 使能或者失能USARTx的LIN模式。 |
12 | USART_SendData | 通过外设USARTx发送单个数据。 |
13 | USART_ReceiveData | 返回USARTx最近接收到的数据。 |
14 | USART_SendBreak | 发送中断字。 |
15 | USART_SetGuardTime | 设置指定的USART保护时间。 |
16 | USART_SetPrescaler | 设置USART时钟预分频。 |
17 | USART_SmartCardCmd | 使能或者失能指定USART的智能卡模式。 |
18 | USART_SmartCardNackCmd | 使能或者失能NACK传输。 |
19 | USART_HalfDuplexCmd | 使能或者失能USART半双工模式。 |
20 | USART_IrDAConfig | 设置USART IrDA模式。 |
21 | USART_IrDACmd | 使能或者失能USART IrDA模式。 |
22 | USART_GetFlagStatus | 检查指定的USART标志位设置与否。 |
23 | USART_ClearFlag | 清除USARTx的待处理标志位。 |
24 | USART_GetITStatus | 检查指定的USART中断发生与否。 |
25 | USART_ClearITPendingBit | 清除USARTx的中断待处理位。 |
每一种寄存器详细的描述在这里不一一进行介绍,大家可以参考目录:“第1部分:开发板硬件资料”--->“2 - 芯片资料”中“STM32固件库使用手册的中文翻译版”对应的USART章节的库函数部分认真研读。
USART配置过程针对STM32F103的GPIO口为USART进行串口通信时,软件的配置过程如下:
图3:USART软件配置步骤
- 注:上述软件配置的第2步可省去,第5步和第7步在没有使用中断情况下也可省去。
USART1串口收发实验- 注:本节对应的实验源码是:“实验6-1:USART1串口收发数据”。
工程需要用到的库文件在使用库函数建“实验6-1:USART1串口收发数据”工程时,需要用到的c文件如下表所示。
表5:实验需要用到的C文件
序号 | 文件名 | 后缀 | 功能描述 |
1 | stm32f10x_rcc | .c | 复位与时钟控制器。 |
2 | stm32f10x_gpio | .c | 通用输入输出。 |
3 | stm32f10x_usart | .c | 通用同步/异步收发器。 |
4 | misc | .c | 中断向量控制器。 |
- 注:STM32 V3.5版本库函数中没有原来版本中单独对于NVIC的外设驱动,把NVIC的外设驱动放在了misc.c中,实际上是代替原来的stm32f10x_nvic.c文件。
可按下图所示在新建工程时添加需要的c文件。
图4:在新建工程中添加所需库函数c文件
表6:实验需要用到的H文件
序号 | 文件名 | 后缀 | 功能描述 |
1 | stm32f10x_rcc | .h | 复位与时钟控制器。 |
2 | stm32f10x_gpio | .h | 通用输入输出。 |
3 | stm32f10x_usart | .h | 通用同步/异步收发器。 |
4 | misc | .h | 中断向量控制器。 |
- 注:所建工程必须添加上面h文件所在的目录:..\..\ Lib\ F10x_FWLIB\ inc。
在使用该实验工程时,需要用到的h文件如表5所示。需要在MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
图5:如何添加头文件包含路径
编写代码首先初始化USART1,完成的操作有:使能GPIO和USART1的时钟,复位USART1,配置USART1用到的PA9和PA10模式,初始化USART1参数,开启USART1并初始化NVIC,使能USART1外设。具体代码如下。
代码清单:初始化USART1并配置USART1中断优先级
- /**************************************************************************************
- * 描 述 : 初始化USART1并配置USART1中断优先级
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- void USART1_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- USART_InitTypeDef USART_InitStructure;
- USART_ClockInitTypeDef USART_ClockInitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- //打开所用GPIO及串口1的时钟
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1 , ENABLE);
- //配置的IO是PA9,串口1的TXD
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //推挽输出
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- //配置的IO是PA10,串口1的RXD
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- //配置串口1的硬件参数
- USART_DeInit(USART1); //将外设USART1寄存器重设为缺省值
- USART_InitStructure.USART_BaudRate = 19200; //设置串口1波特率为19200
- USART_InitStructure.USART_WordLength = USART_WordLength_8b; //设置一个帧中传输数据位
- USART_InitStructure.USART_StopBits = USART_StopBits_1; //定义发送的停止位数目为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);
- USART_ClockInitStructure.USART_Clock = USART_Clock_Disable; //时钟低电平活动
- USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low; //设置SCLK引脚上时钟输出的极性为低电平
- USART_ClockInitStructure.USART_CPHA = USART_CPHA_1Edge; //时钟第一个边沿进行数据捕获
- USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable; //最后一位数据的时钟脉冲不从SCLK输出
- USART_ClockInit(USART1, &USART_ClockInitStructure);
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能USART1接收中断
- USART_Cmd(USART1, ENABLE); //使能USART1外设
- //配置串口1中断优先级
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC_Group:先占优先级2位,从优先级2位
- NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn; //配置为串口1中断
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //先占优先级为1
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级为3
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道
- NVIC_Init(&NVIC_InitStructure);
- }
然后,编写USART1的中断服务函数。代码如下。
代码清单:USART1中断服务函数
- /**************************************************************************************
- * 描 述 : USART1全局中断服务
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- void USART1_IRQHandler(void)
- {
- if(USART_GetITStatus(USART1,USART_IT_RXNE)!= RESET) //接收中断
- {
- Rx232buffer= USART_ReceiveData(USART1); //通过外设USART1接收数据
- Flag=TRUE; //接收到数据,接收标识符有效
- USART_ClearITPendingBit(USART1, USART_IT_RXNE); //清除USART1的中断待处理位
- }
- }
- 注:Flag变量是用户自定义的接收标识符,与相关寄存器相关位没有关系,请勿混淆。
之后,用户设计一个串口通信的应用函数,代码如下。该应用函数实现的功能即是本实验实现的功能的体现。
代码清单:USART1_Tx_Puts接收数据并处理函数
- /**************************************************************************************
- * 描 述 : USART1接收到数据后串口发送出去
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- void USART1_Tx_Puts(void)
- {
- if(Flag) //有新数据通过串口被接收到
- {
- USART1_SendByte(Rx232buffer); //发送字符
- USART1_SendByte(0x0D); //发送换行符
- USART1_SendByte(0x0A); //发送换行符
- Flag=FALSE; //清除接收标识符
- }
- }
最后,主函数初始化相关串口配置后,在while循环中调用用户应用函数。如下图所示。
代码清单:主函数
- int main(void)
- {
- //初始化USART1并配置USART1中断优先级
- USART1_Init();
- //主循环
- while(1)
- {
- USART1_Tx_Puts(); //串口1接收到1个字符后返回该字符
- }
- }
实验步骤- 解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验6-1:USART1串口收发数据”,打开解压后的文件夹,将“USART1”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。
- 启动MDK5.23。
- 在MDK5中执行“Project→Open Project”打开“…\ USART1 \projec”目录下的工程“USART1.uvproj”。
- 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“USART1.hex”位于工程目录下的“Objects”文件夹中。
- 点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
- 程序运行后,打开串口调试助手选择正确的串口号,波特率设置为19200,数据位为8、停止位为1。然后点击打开串口,在发送区任意写一个字符点击发送,则在窗口可看到接收到的字符与发送字符一致。
- 思考题1:分析代码是如何实现接收到的字符与发送的字符一致?查看串口调试助手接收与发送字节数有什么关系?为什么?
USART1串口使用printf函数实验- 注:本节对应的实验源码是:“实验6-2: USART1串口使用printf函数”。
工程需要用到的库文件在使用库函数建“实验6-2: USART1串口使用printf函数”工程时,需要用到的c文件以及添加头文件包含路径的方法与介绍“实验6-1:USART1串口收发数据”完全一样,在此不再赘述。
编写代码首先初始化USART1,函数名是:USART1_Init。该函数代码与“实验6-1:USART1串口收发数据”中USART1_Init()代码完全一样,在此不再介绍。
其次,串口要使用printf函数,需要重定向两个c库函数到USART1,如下所示。
代码清单:重定向c库函数printf到USART1
- /**************************************************************************************
- * 描 述 : 重定向c库函数printf到USART1
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- int fputc(int ch, FILE *f)
- {
- /* 发送一个字节数据到USART1 */
- USART_SendData(USART1, (uint8_t) ch);
- /* 等待发送完毕 */
- while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
- return (ch);
- }
代码清单:重定向c库函数scanf到USART1
- /**************************************************************************************
- * 描 述 : 重定向c库函数scanf到USART1
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- int fgetc(FILE *f)
- {
- /* 等待串口1输入数据 */
- while (USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == RESET);
- return (int)USART_ReceiveData(USART1);
- }
- 注:除了需要重定向两个c库函数到USART1,还需要添加:#include <stdio.h> 。
最后,主函数初始化相关串口配置后,直接调用printf函数。如下图所示。
代码清单:主函数
- int main(void)
- {
- //初始化USART1并配置USART1中断优先级
- USART1_Init();
- printf("\r\n 对PRINTF函数测试OK \r\n");
- //主循环
- while(1)
- {
- ;
- }
- }
实验步骤- 解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验6-2:USART1串口使用printf函数”,打开解压后的文件夹,将“USART1”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。
- 启动MDK5.23。
- 在MDK5中执行“Project→Open Project”打开“…\ USART1 \projec”目录下的工程“USART1.uvproj”。
- 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“USART1.hex”位于工程目录下的“Objects”文件夹中。
- 点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
- 打开串口调试助手选择正确的串口号,波特率设置为19200,数据位为8、停止位为1,并点击打开串口。
- 按一下复位按键,让程序从新执行,可在串口调试助手的接收窗口显示“对PRINTF函数测试OK”内容。
USART2串口收发数据实验- 注:本节对应的实验源码是:“实验6-3:USART2串口收发数据”。
工程需要用到的库文件在使用库函数建“实验6-3:USART2串口收发数据”工程时,需要用到的c文件以及添加头文件包含路径的方法与介绍“实验6-1:USART1串口收发数据”完全一样,在此不再赘述。
编写代码首先初始化USART2,完成的操作有:使能GPIO和USART2的时钟,复位USART2,配置USART2用到的PA2和PA3模式,初始化USART2参数,开启USART2并初始化NVIC,使能USART2外设。具体代码如下。
代码清单:初始化USART2并配置USART2中断优先级
- /**************************************************************************************
- * 描 述 : 初始化USART2并配置USART2中断优先级
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- void USART2_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- USART_InitTypeDef USART_InitStructure;
- USART_ClockInitTypeDef USART_ClockInitStructure;
- NVIC_InitTypeDef NVIC_InitStructure;
- //打开所用GPIO及串口2的时钟
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA , ENABLE);
- RCC_APB1PeriphClockCmd( RCC_APB1Periph_USART2 , ENABLE);
- //配置的IO是PA2,串口2的TXD
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- //配置的IO是PA3,串口2的RXD
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- //配置串口2的硬件参数
- USART_DeInit(USART2); //将外设USART2寄存器重设为缺省值
- USART_InitStructure.USART_BaudRate = 19200; //设置串口2波特率为19200
- USART_InitStructure.USART_WordLength = USART_WordLength_8b; //设置一个帧中传输数据位
- USART_InitStructure.USART_StopBits = USART_StopBits_1; //定义发送的停止位数目为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(USART2, &USART_InitStructure);
- USART_ClockInitStructure.USART_Clock = USART_Clock_Disable; //时钟低电平活动
- USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low; //设置SCLK引脚上时钟输出的极性为低电平
- USART_ClockInitStructure.USART_CPHA = USART_CPHA_1Edge; //时钟第一个边沿进行数据捕获
- USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable; //最后一位数据的时钟脉冲不从SCLK输出
- USART_ClockInit(USART2, &USART_ClockInitStructure);
- USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //使能USART2接收中断
- USART_Cmd(USART2, ENABLE); //使能USART2外设
- //配置串口2中断优先级
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //NVIC_Group:先占优先级2位,从优先级2位
- NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn; //配置为串口2中断
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=3; //先占优先级为3
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3; //从优先级为3
- NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能中断通道
- NVIC_Init(&NVIC_InitStructure);
- }
然后,编写USART2的中断服务函数。代码如下。
代码清单:USART2中断服务函数
- /**************************************************************************************
- * 描 述 : USART2全局中断服务
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- void USART2_IRQHandler(void)
- {
- if(USART_GetITStatus(USART2,USART_IT_RXNE)!= RESET) //接收中断
- {
- Rx232buffer= USART_ReceiveData(USART2); //通过外设USART2接收数据
- Flag=TRUE; //接收到数据,接收标识符有效
- USART_ClearITPendingBit(USART2, USART_IT_RXNE); //清除USART2的中断待处理位
- }
- }
- 注:Flag变量是用户自定义的接收标识符,与相关寄存器相关位没有关系,请勿混淆。
之后,用户设计一个串口通信的应用函数,代码如下。该应用函数实现的功能即是本实验实现的功能的体现
代码清单:USART1_Tx_Puts接收数据并处理函数
- /**************************************************************************************
- * 描 述 : USART2接收到数据后串口发送出去
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- void USART2_Tx_Puts(void)
- {
- if(Flag) //有新数据通过串口被接收到
- {
- USART2_SendByte(Rx232buffer); //发送字符
- USART2_SendByte(0x0D); //发送换行符
- USART2_SendByte(0x0A); //发送换行符
- Flag=FALSE; //清除接收标识符
- }
- }
最后,主函数初始化相关串口配置后,在while循环中调用用户应用函数。如下图所示。
代码清单:主函数
- int main(void)
- {
- //初始化USART2并配置USART2中断优先级
- USART2_Init();
- //主循环
- while(1)
- {
- USART2_Tx_Puts(); //串口2接收到1个字符后返回该字符
- }
- }
实验步骤- 解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验6-3:USART2串口收发数据”,打开解压后的文件夹,将“USART2”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。
- 启动MDK5.23。
- 在MDK5中执行“Project→Open Project”打开“…\ USART2 \projec”目录下的工程“USART2.uvproj”。
- 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“USART2.hex”位于工程目录下的“Objects”文件夹中。
- 点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
- 程序运行后,打开串口调试助手选择正确的串口号,波特率设置为19200,数据位为8、停止位为1。然后点击打开串口,在发送区任意写一个字符点击发送,则在窗口可看到接收到的字符与发送字符一致。
USART2串口使用printf函数实验- 注:本节对应的实验源码是:“实验6-4:USART2串口使用printf函数”。
工程需要用到的库文件在使用库函数建“实验6-4:USART2串口使用printf函数”工程时,需要用到的c文件以及添加头文件包含路径的方法与介绍“实验6-1:USART1串口收发数据”完全一样,在此不再赘述。
编写代码首先初始化USART2,函数名是:USART2_Init。该函数代码与“实验6-3:USART2串口收发数据”中USART2_Init()代码完全一样,在此不再介绍。
其次,串口要使用printf函数,需要重定向两个c库函数到USART2,如下所示。
代码清单:重定向c库函数printf到USART2
- /**************************************************************************************
- * 描 述 : 重定向c库函数printf到USART2
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- int fputc(int ch, FILE *f)
- {
- /* 发送一个字节数据到USART2 */
- USART_SendData(USART2, (uint8_t) ch);
- /* 等待发送完毕 */
- while (USART_GetFlagStatus(USART2, USART_FLAG_TXE) == RESET);
- return (ch);
- }
代码清单:重定向c库函数scanf到USART2
- /**************************************************************************************
- * 描 述 : 重定向c库函数scanf到USART2
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- int fgetc(FILE *f)
- {
- /* 等待串口1输入数据 */
- while (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) == RESET);
- return (int)USART_ReceiveData(USART2);
- }
- 注:除了需要重定向两个c库函数到USART2,还需要添加:#include <stdio.h> 。
最后,主函数初始化相关串口配置后,直接调用printf函数。如下图所示。
代码清单:主函数
- int main(void)
- {
- //初始化USART2并配置USART2中断优先级
- USART2_Init();
- printf("\r\n 对PRINTF函数测试OK \r\n");
- //主循环
- while(1)
- {
- ; //无任务
- }
- }
实验步骤- 解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验6-4:USART2串口使用printf函数”,打开解压后的文件夹,将“USART2”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。
- 启动MDK5.23。
- 在MDK5中执行“Project→Open Project”打开“…\ USART2 \projec”目录下的工程“USART2.uvproj”。
- 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“USART2.hex”位于工程目录下的“Objects”文件夹中。
- 点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
- 打开串口调试助手选择正确的串口号,波特率设置为19200,数据位为8、停止位为1,并点击打开串口。
- 按一下复位按键,让程序从新执行,可在串口调试助手的接收窗口显示“对PRINTF函数测试OK”内容。
USART1串口发送数据(DMA)实验- 注:本节对应的实验源码是:“实验6-5:USART1串口发送数据(DMA)”。
工程需要用到的库文件在使用库函数建“实验6-5:USART1串口发送数据(DMA)”工程时,需要用到的c文件如下表所示。
表7:实验需要用到的C文件
序号 | 文件名 | 后缀 | 功能描述 |
1 | stm32f10x_rcc | .c | 复位与时钟控制器。 |
2 | stm32f10x_gpio | .c | 通用输入输出。 |
3 | stm32f10x_usart | .c | 通用同步/异步收发器。 |
4 | misc | .c | 中断向量控制器。 |
5 | stm32f10x_dma | .c | 直接内存存取控制器。 |
- 注:STM32 V3.5版本库函数中没有原来版本中单独对于NVIC的外设驱动,把NVIC的外设驱动放在了misc.c中,实际上是代替原来的stm32f10x_nvic.c文件。
可按下图所示在新建工程时添加需要的c文件。
图6:在新建工程中添加所需库函数c文件
表8:实验需要用到的H文件
序号 | 文件名 | 后缀 | 功能描述 |
1 | stm32f10x_rcc | .h | 复位与时钟控制器。 |
2 | stm32f10x_gpio | .h | 通用输入输出。 |
3 | stm32f10x_usart | .h | 通用同步/异步收发器。 |
4 | misc | .h | 中断向量控制器。 |
5 | stm32f10x_dma | .h | 直接内存存取控制器。 |
- 注:所建工程必须添加上面h文件所在的目录:..\..\ Lib\ F10x_FWLIB\ inc。
在使用该实验工程时,需要用到的h文件如表5所示。需要在MDK中点击魔术棒,打开工程配置窗口,按照下图所示添加头文件包含路径。
图7:如何添加头文件包含路径
编写代码首先初始化USART1,函数名是:USART1_Init。该函数代码与“实验6-1:USART1串口收发数据”中USART1_Init()代码不完全一样,本实验中没有使能USART中断。代码如下。
代码清单:初始化USART1
- /**************************************************************************************
- * 描 述 : 初始化USART1并配置USART1中断优先级
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- void USART1_Init(void)
- {
- GPIO_InitTypeDef GPIO_InitStructure;
- USART_InitTypeDef USART_InitStructure;
- USART_ClockInitTypeDef USART_ClockInitStructure;
- //打开所用GPIO及串口1的时钟
- RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA | RCC_APB2Periph_USART1 , ENABLE);
- //配置的IO是PA9,串口1的TXD
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //推挽输出
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- //配置的IO是PA10,串口1的RXD
- GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
- GPIO_Init(GPIOA, &GPIO_InitStructure);
- //配置串口1的硬件参数
- USART_DeInit(USART1); //将外设USART1寄存器重设为缺省值
- USART_InitStructure.USART_BaudRate = 19200; //设置串口1波特率为19200
- USART_InitStructure.USART_WordLength = USART_WordLength_8b; //设置一个帧中传输数据位
- USART_InitStructure.USART_StopBits = USART_StopBits_1; //定义发送的停止位数目为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);
- USART_ClockInitStructure.USART_Clock = USART_Clock_Disable; //时钟低电平活动
- USART_ClockInitStructure.USART_CPOL = USART_CPOL_Low; //设置SCLK引脚上时钟输出的极性为低电平
- USART_ClockInitStructure.USART_CPHA = USART_CPHA_1Edge; //时钟第一个边沿进行数据捕获
- USART_ClockInitStructure.USART_LastBit = USART_LastBit_Disable; //最后一位数据的时钟脉冲不从SCLK输出
- USART_ClockInit(USART1, &USART_ClockInitStructure);
- USART_ITConfig(USART1, USART_IT_RXNE, ENABLE); //使能USART1接收中断
- USART_Cmd(USART1, ENABLE); //使能USART1外设
- }
其次初始化DMA,设置有关参数使之操作对象是USART1,代码如下。
代码清单:初始化DMA
- /**************************************************************************************
- * 描 述 : 初始化DMA
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- void DMA_USART1_Init(void)
- {
- DMA_InitTypeDef DMA_InitStructure;
- RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE); //开启DMA时钟
- DMA_InitStructure.DMA_PeripheralBaseAddr = ( uint32_t ) ( & ( USART1->DR ) ); //设置DMA源:串口1数据寄存器地址
- DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)SendBuff; //DMA内存基地址(要传输的变量的指针)
- DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST; //数据传输方向,从内存读取发送到外设
- DMA_InitStructure.DMA_BufferSize = SENDSIZE_MAX; //DMA通道的DMA缓存的大小
- DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; //外设地址寄存器不变
- DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; //内存地址寄存器递增
- DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte; //外设数据宽度为8位
- DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte; //内存数据宽度为8位
- DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; //工作在循环缓存模式
- DMA_InitStructure.DMA_Priority = DMA_Priority_Medium; //DMA通道拥有中优先级
- DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; //禁止DMA通道内存到内存传输
- DMA_Init(DMA1_Channel4, &DMA_InitStructure); //配置DMA1的4通道
- DMA_Cmd (DMA1_Channel4,ENABLE); //使能DMA1的4通道
- }
然后,用户设计一个向发送缓存区存放数值函数,代码如下。
代码清单:向发送缓存区存放数值
- /**************************************************************************************
- * 描 述 : 向发送缓存区存放数值
- * 入 参 : 无
- * 返回值 : 无
- **************************************************************************************/
- void SendBuff_Init(void)
- {
- uint16_t i;
- for(i=0;i<SENDSIZE_MAX;i )
- {
- if(i%2)
- {
- SendBuff[i] = 'O'; //发送缓存奇数空间存放'O'
- }
- else
- {
- SendBuff[i] = 'K'; //发送缓存偶数空间存放'K'
- }
- }
- }
最后,主函数初始化相关串口配置后,在while循环中调用用户应用函数。如下图所示。
代码清单:主函数
- int main(void)
- {
- //初始化4个用户指示灯D1、D2、D3、D4
- leds_init();
- //初始化USART1并配置USART1中断优先级
- USART1_Init();
- //初始化DMA
- DMA_USART1_Init();
- //初始化用于检测按键S1的引脚PE2
- key_init();
- //向发送缓存区存放数值
- SendBuff_Init();
- //USART1 向 DMA发出TX请求
- USART_DMACmd(USART1, USART_DMAReq_Tx, ENABLE);
- //主循环 ,该部分占用CPU资源
- while(1)
- {
- //调用库函GPIO_ReadInputDataBit()检测按键S1对应引脚PE2的电平是否为低电平
- if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) == 0 )
- {
- //软件延时10ms,如果延时后按键S1的电平依然没有变化,说明按键确实被有效操作,简称按键防抖动
- sw_delay_ms(10);
- //检测按键S1对应引脚PE2的电平依然为低电平,即按键S1确实被按下
- if(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) == 0 )
- {
- led_on(LED_1); //点亮D1
- //等待按键S1释放,即如果PE2一直为低电平,会一直执行空命令
- while(GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) == 0 )
- {
- ; //条件GPIO_ReadInputDataBit(GPIOE,GPIO_Pin_2) == 0成立,会执行这个空命令
- }
- //按键S1松开,会执行下一行语句,即熄灭D1
- led_off(LED_1);
- }
- }
- }
- }
实验步骤- 解压“…\第3部分:标准库教程和实验源码\ 1 - 基础实验程序\”目录下的压缩文件“实验6-5:USART1串口发送数据(DMA)”,打开解压后的文件夹,将“DMA_USART1”文件夹拷贝到合适的目录,如“D\STM32F103ZET6”。
- 启动MDK5.23。
- 在MDK5中执行“Project→Open Project”打开“…\ DMA_USART1 \projec”目录下的工程“DMA_USART1.uvproj”。
- 点击编译按钮编译工程。注意查看编译输出栏,观察编译的结果,如果有错误,修改程序,直到编译成功为止。编译后生成的HEX文件“DMA_USART1.hex”位于工程目录下的“Objects”文件夹中。
- 点击下载按钮下载程序 。如果需要对程序进行仿真,点击Debug按钮,即可将程序下载到STM32F103ZET6中进行仿真。
- 程序运行后,可按下述操作进行实验:
1)打开串口调试助手选择正确的串口号,波特率设置为19200,数据位为8、停止位为1,并点击打开串口。
2)可在串口调试助手的接收窗口显示“OK”内容。
3)按下用户按键S1可观察到指示灯D1会有相应变化。
- 备注:DMA的强大作用即是会帮助节省CPU资源,本实验占用CPU资源的是按键检测的部分,可以观察到串口在高速不间断发送数据,而丝毫不影响按键部分的检测实验,一定程度上可以说明串口发送部分不占用CPU资源。