STM32F10x 学习笔记5(USART实现串口通讯 1)
从前面的介绍可知USART模块功能非常的强大。这里我只简单讲讲如何用USART模块来实现标准EIA-232串口通讯。
用过单片机的人肯定都接触过串口,设置串口无非就是设置波特率、数据位、停止位、奇偶校验位。发送接收也就三种基本方式,轮询、中断和DMA。STM32F10x的USART模块也不过如此。所以我重点讲讲我在调试代码时犯得各种错误,那些很容易得到的代码就不详细的讲解了。
首先说说我的硬件环境。还是那块神舟4号开发板,用的是串口2,对应的是USART2。默认情况下USART2是连接到IO端口A的,但是我这里需要将USART的管腿重定向到IO端口D上。具体的管腿的关系参见下表。这个表是从STM32参考手册上拷下来的。
初始化USART的代码很简单。USART2连接到APB1总线上了,先要打开USART2的时钟,然后设置波特率一类的参数。
- USART_InitTypeDefUSART_InitStructure;
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
- USART_InitStructure.USART_BaudRate=9600;
- 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(USART2,&USART_InitStructure);
这样设置了还不能使用。因为我们将USART2重定向了。重定向操作需要写复用重映射和调试I/O配置寄存器(AFIO_MAPR)。GPIO_PinRemapConfig()可以完成这项任务。
- GPIO_PinRemapConfig(GPIO_Remap_USART2,ENABLE);
光这样操作还不够。STM32参考手册上有这么一段话:
对寄存器AFIO_EVCR,AFIO_MAPR和AFIO_EXTICRX进行读写操作前,应当首先打开AFIO的时钟。参考第6.3.7节APB2外设时钟使能寄存器(RCC_APB2ENR)。
所以需要先打开AFIO的时钟。因此,USART2的重定向需要两步操作:
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO,ENABLE);
- GPIO_PinRemapConfig(GPIO_Remap_USART2,ENABLE);
我原以为这样就能工作了,可是结果还是什么都没有输出。没办法只能继续研究。在读GPIO的相关章节时看到下图让我恍然大悟。

USART2的输入输出都是借用PD口管腿,PD口的时钟却还没给。用到的几个IO端口也没有设置相应的输入输出状态。在读到8.1.9复用功能配置这一小节时发现了如下的表格。
按照上面给出的配置,写好程序:
- GPIO_InitTypeDefGPIO_InitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD,ENABLE);
- /*ConfigureUSARTTxasalternatefunctionpush-pull*/
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOD,&GPIO_InitStructure);
- /*ConfigureUSARTRxasinputfloating*/
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
- GPIO_Init(GPIOD,&GPIO_InitStructure);
再次测试,一切正常。
发送一个字符的函数可以这么写:
- voidUART_PutChar(USART_TypeDef*USARTx,uint8_tData)
- {
- while(USART_GetFlagStatus(USART2,USART_FLAG_TC)==RESET){};
- USART_SendData(USARTx,Data);
- }
发送字符串的函数如下:
- voidUART_PutStr(USART_TypeDef*USARTx,uint8_t*str)
- {
- while(0!=*str)
- {
- UART_PutChar(USARTx,*str);
- str++;
- }
- }
上面串口初始化的代码可以放到一个函数中:
- voidUSART2_init(void)
- {
- GPIO_InitTypeDefGPIO_InitStructure;
- USART_InitTypeDefUSART_InitStructure;
- RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOD|RCC_APB2Periph_AFIO,ENABLE);
- /*ConfigureUSARTTxasalternatefunctionpush-pull*/
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
- GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
- GPIO_Init(GPIOD,&GPIO_InitStructure);
- /*ConfigureUSARTRxasinputfloating*/
- GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
- GPIO_InitStructure.GPIO_Pin=GPIO_Pin_6;
- GPIO_Init(GPIOD,&GPIO_InitStructure);
- GPIO_PinRemapConfig(GPIO_Remap_USART2,ENABLE);
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
- USART_InitStructure.USART_BaudRate=9600;
- 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(USART2,&USART_InitStructure);
- USART_Cmd(USART2,ENABLE);
- }
今天先写这么多。接收字符的函数与发送字符的函数差不多,但是这种轮询方式效率很低,不建议使用。下次写一篇介绍如何用中断方式发送接收串口数据,中断方式的效率会高很多。如果有时间再写一篇DMA方式发送接收数据的文章。
评论