前言
Q: 为什么写这篇文章?
笔者其实也是小白,看了一些书籍和网上的教程,发现有一个比较严重的问题——网络及书籍的方法不够简 明,导致笔者学习时废了很大力气。网络及书籍的方法大多着重在数据的接收校验上,导致教程难以理解,其实芯片并没有想象的这么不稳定,对于稳定性要求不高的设计,接收时可以睁一只眼闭一只眼,只考虑最简单的方法是最有利于初学者入门的。
本人使用的芯片:
STM32F103RCT6正文
定义一个void USARTINIT(void)函数用于初始化在使用USART1串口通信时,首先肯定要初始化,初始化函数内,可以分为以下几个步骤:
1.定义用于初始化的结构体变量:
GPIO_InitTypeDef PIN; //RX:PA10, TX:PA9
NVIC_InitTypeDef NV; //NV用于初始化中断
USART_InitTypeDef US; //US为串口配置
2.使能外设
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE );
USART_DeInit( USART1 ); //重置串口1
3.RX和TX的gpio配置:
PIN.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽
PIN.GPIO_Pin = GPIO_Pin_9; //PA9对应TX
PIN.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &PIN );
PIN.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮动输入
PIN.GPIO_Pin = GPIO_Pin_10;
PIN.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &PIN );
4.NVIC配置:
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_1 );
NV.NVIC_IRQChannel = USART1_IRQn;//打开串口中断
NV.NVIC_IRQChannelPreemptionPriority = 1;//数值在规定范围内可以随意,因为就只有这个中断
NV.NVIC_IRQChannelSubPriority = 1;
NV.NVIC_IRQChannelCmd = ENABLE;//使能中断
NVIC_Init( &NV );
5.USART配置:
笔者推荐的波特率为4800或9600,出问题可以尝试1200或2400
US.USART_BaudRate = BOUND; //BOUND可以自己#define一个值,如果读出的数有问题,可以尝试调低
US.USART_Parity = USART_Parity_No; //校验位为无
US.USART_StopBits = USART_StopBits_1;//停止位为1
US.USART_WordLength = USART_WordLength_8b;//字长为8位
US.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//关硬件流操作
US.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//设置串口模式,RX和TX都设置
USART_Init( USART1, &US );//串口初始化
USART_Cmd( USART1, ENABLE );//串口使能,这个很重要!很多教程都没有
现在不得不讲一下比较常用的寄存器,在串口中,有两个和数据收发有直接联系的寄存器:DR(数据寄存器)和SR(状态寄存器);DR既存放接收的数据也可以发送数据,SR寄存器存放的是DR寄存器的状态
如下图:
从上图可以看到几个SR寄存器常用的位:TC位、RXNE位。TC位在程序中用于判断发送操作是否完成;而RXNE位用于判断数据接收是否完成。
这样一来,我们就可以理清串口数据收发的思路了:
1.首先读出RS寄存器的TC位或RXNE位,判断收发是否完成。
2.将数据接收或发送。
在STM32中,我们可以使用库函数,来获取TC和RXNE的值
USART_GetFlagStatus(USART_TypeDef* USARTx, uint16_t USART_FLAG); //获得SR某一位的值
所以,在上文的初始化后,可以调用函数USART_SendData或直接给DR寄存器赋值对串口发送数据
USART_SendData( USART1, 'H' ); //发送字符‘H’
或 USART1 -> DR = 0x48;
但是在发送前,我们还需要读状态,所以
while( USART_GetFlagStatus( USART1, USART_FLAG_TC ) == 0 );//如果TC位为0则等待
USARTx -> DR = dat; //上面确认可以传输数据后发送dat数据
同样的,读之前也要读RXNE位,于是读操作为
unsigned char res = 0;//存放数据
while(1){
//如果RXNE等于1,代表接收完成,则接收数据给res并发送回串口
if( USART_GetFlagStatus( USART1, USART_FLAG_RXNE ) != 0 ){
res = USART1 -> DR;
//res = USART_ReceiveData( USART1 );
while( USART_GetFlagStatus( USART1, USART_FLAG_TC ) == 0 );
USART1 -> DR = res;
//USART_SendData( USART1, res );
}
}
例程
下载例程:https://github.com/TTowFive/USART
main.c#include "stm32f10x_conf.h"
#include "UART.h"
int main(){
uint8_t res; //接收到的数据
USARTNV();
USART_SEND_DATA( USART1, "你好!" );
while(1){
if( USART_GetFlagStatus( USART1, USART_FLAG_RXNE ) != 0 ){
res = USART1 -> DR;
while( USART_GetFlagStatus( USART1, USART_FLAG_TC ) == 0 );
USART1 -> DR = res;
}
}
}
UART.h
#ifndef UART_H
#define UART_H
#include "stm32f10x_conf.h"
#define BOUND 9600
void USARTNV(void);
void USART_SEND_DATA( USART_TypeDef* USARTx, uint8_t * dat );
#endif
UART.c
#include "UART.h"
void USARTNV(){
GPIO_InitTypeDef PIN;
NVIC_InitTypeDef NV;
USART_InitTypeDef US;
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1, ENABLE );
USART_DeInit( USART1 );
PIN.GPIO_Mode = GPIO_Mode_AF_PP;
PIN.GPIO_Pin = GPIO_Pin_9;
PIN.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &PIN );
PIN.GPIO_Mode = GPIO_Mode_IN_FLOATING;
PIN.GPIO_Pin = GPIO_Pin_10;
PIN.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init( GPIOA, &PIN );
NVIC_PriorityGroupConfig( NVIC_PriorityGroup_1 );
NV.NVIC_IRQChannel = USART1_IRQn;
NV.NVIC_IRQChannelPreemptionPriority = 1;
NV.NVIC_IRQChannelSubPriority = 1;
NV.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init( &NV );
US.USART_BaudRate = BOUND;
US.USART_Parity = USART_Parity_No;
US.USART_StopBits = USART_StopBits_1;
US.USART_WordLength = USART_WordLength_8b;
US.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
US.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init( USART1, &US );
USART_Cmd( USART1, ENABLE );
}
void USART_SEND_DATA( USART_TypeDef* USARTx, uint8_t * dat ){
while( *dat != '\0' ){
while( USART_GetFlagStatus( USARTx, USART_FLAG_TC ) == 0 );
USARTx -> DR = *dat;
dat++;
}
}
部分内容参考博客:
https://blog.csdn.net/Lzinner/article/details/80711517