今日实验:实现STM32F407开发板与PC端的串口通信
两台计算机进行通信时,最少可以只要三根线,分别为RXD、TXD、GND ;单片机也是一个微型计算机。
前期知识
关于TTL电平
TTL是Transistor-Transistor Logic,即晶体管-晶体管逻辑的简称,它是计算机处理器控制的设备内部各部分之间通信的标准技术。TTL电平信号应用广泛,是因为其数据表示采用二进制规定,+5V等价于逻辑”1”,0V等价于逻辑”0”。
数字电路中,由TTL电子元器件组成电路的电平是个电压范围,规定:
输出高电平>=2.4V,输出低电平<=0.4V;
输入高电平>=2.0V,输入低电平<=0.8V。
关于RS232电平
RS232电平是串口的一个标准;在TXD和RXD数据线上:
逻辑1为-3~-15V的电压
逻辑0为3~15V的电压
为什么需要CH340?
CH340是一个电平转换芯片,我们都知道,计算机识别的是TTL电平,TTL电平在实际中不适合远距离传输;所以我们将其转换为RS232电平,以适应远距离传输
上面补充了一些理论知识;接下来采用ST公司提供的官方固件库编写代码
//打开文件stm32f4xx_usart.c ,发现它在开头已经写好了一堆注释告诉你如何使用它的库文件进行开发
//那么我们先分析一下步骤,如何写出可以进行串口通信的代码,本例中采用USART1
===============================================================================
##### How to use this driver #####
===============================================================================
[..]
(#) Enable peripheral clock using the following functions
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USARTx, ENABLE) for USART1 and USART6
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USARTx, ENABLE) for USART2, USART3,
UART4 or UART5.
//这第一步便是告诉我们首先要使能外设时钟,采用RCC_APB2PeriphClockCmd(RCC_APB2Periph_USARTx, ENABLE)
// 这句话可以使能USART1 和 USART6 这两个外设的时钟,那我们用USART1就好了
(#) According to the USART mode, enable the GPIO clocks using
RCC_AHB1PeriphClockCmd() function. (The I/O can be TX, RX, CTS,
or/and SCLK).
//第二步,告诉我们使用函数RCC_AHB1PeriphClockCmd()打开相应的GPIO时钟
(#) Peripheral's alternate function:
(++) Connect the pin to the desired peripherals' Alternate
Function (AF) using GPIO_PinAFConfig() function
(++) Configure the desired pin in alternate function by:
GPIO_InitStruct->GPIO_Mode = GPIO_Mode_AF
(++) Select the type, pull-up/pull-down and output speed via
GPIO_PuPd, GPIO_OType and GPIO_Speed members
(++) Call GPIO_Init() function
//第三步,使用函数GPIO_PinAFConfig()配置相应的GPIO端口复用模式,并调用函数GPIO_Init()进行GPIO初始化
(#) Program the Baud Rate, Word Length , Stop Bit, Parity, Hardware
flow control and Mode(Receiver/Transmitter) using the USART_Init()
function.
//第四步,使用函数USART_Init()配置波特率,数据位,停止位,校验位,控制流
(#) For synchronous mode, enable the clock and program the polarity,
phase and last bit using the USART_ClockInit() function.
//这一步是采用USART_ClockInit()函数配置同步模式的,因为我们在使用串口通信时没有用到同步模式,所以不配置
(#) Enable the NVIC and the corresponding interrupt using the function
USART_ITConfig() if you need to use interrupt mode.
//这里是告诉我们要配置中断向量(NVIC_Init),并使用USART_ITConfig()函数开启接收中断
(#) When using the DMA mode
(++) Configure the DMA using DMA_Init() function
(++) Active the needed channel Request using USART_DMACmd() function
//这个DMA模式我们没用到
(#) Enable the USART using the USART_Cmd() function.
//这个是使能串口,必须的
(#) Enable the DMA using the DMA_Cmd() function, when using DMA mode.
//使能DMA,这里没用上
-@- Refer to Multi-Processor, LIN, half-duplex, Smartcard, IrDA sub-sections
for more details
[..]
In order to reach higher communication baudrates, it is possible to
enable the oversampling by 8 mode using the function USART_OverSampling8Cmd().
This function should be called after enabling the USART clock (RCC_APBxPeriphClockCmd())
and before calling the function USART_Init().
@endverbatim
******************************************************************************
好了,分析了一下驱动工程师给我们的提示(因为他既然把这个做出来,就肯定要告诉别人怎么用啦),接下来我们使用工程师写好的固件库来编写代码;噢,还有一点,得看看原理图我们具体用到哪些引脚
可以看到,PA9对应着USART1的发送端,PA10对应着USART1的接收端;因此在配置相关GPIO时,我们就要把PA9和PA10配置为端口复用模式
根据上面的分析步骤,我们来编写一下USART1的初始化代码
/*
*功 能:USART1初始化
*参 数:BaudRate 传入的波特率值
*返回值:无
*/
void UART1_Init(uint32_t BaudRate)
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//1、开启USART11时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//2、开启 GPIOA 时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA ,ENABLE);
//3、将 PA9 PA10 复用为 串口1 的特殊功能引脚
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9|GPIO_Pin_10; //选择引脚PA9 PA10
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; //带上拉输出
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz; //输出速率50MHz
GPIO_Init(GPIOA, &GPIO_InitStruct); //初始化GPIO
//4、配置串口的模式
USART_InitStruct.USART_BaudRate = BaudRate; //波特率
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//关闭硬件流控,因为目前只用到三根线,收(RXT),发(TXD),地(GND);不同于九头串口线有9根线
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//收发模式兼具
USART_InitStruct.USART_Parity = USART_Parity_No; //无检验位
USART_InitStruct.USART_StopBits = USART_StopBits_1; //1位停止位
USART_InitStruct.USART_WordLength = USART_WordLength_8b; //8位数据位
USART_Init(USART1 ,&USART_InitStruct);
//5、开启 接收 中断
USART_ITConfig(USART1 , USART_IT_RXNE ,ENABLE);
//6、使能 NVIC ,使用到中断,一定要配置NVIC
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
NVIC_Init(&NVIC_InitStruct);
//7、使能USART1
USART_Cmd(USART1 ,ENABLE);
}
写好USART1的初始化函数,接下来要写它的中断服务函数
uint32_t r_value;
void USART1_IRQHandler(void)
{
//检测数据接收标志位
if((USART_GetFlagStatus(USART1, USART_IT_RXNE)) != RESET)
{
//清除标志位
USART_ClearFlag(USART1, USART_IT_RXNE);
r_value = USART_ReceiveData(USART1); //接收数据,开发板接收来自电脑串口调试终端发送的数据
if(r_value == 'd') //如果接收到字符d ,可执行相应操作,这里做个小测试,通过串口传输控制LED的亮灭
{
//LED0亮
GPIO_ResetBits(GPIOF, GPIO_Pin_9);
}else{
GPIO_SetBits(GPIOF, GPIO_Pin_9);
}
USART_SendData(USART1, r_value); //发送数据 ,开发板向电脑发送数据 ;将显示在串口调试终端上
}
}
#include "stm32f4xx.h"
#include "led.h"
#include "uart.h"
//主程序代码
int main(void)
{
LED_Init(); //LED初始化
USART1_Init(115200); //串口初始化
while(1)
{
//空循环,等待串口中断的发生
}
}
以上代码便实现了两个STM332与电脑之间的串口通信;
不过我们也发现个问题,使用官方提供的库函数,我们就一次只能发送或接收一个字符,那如果我想一次发送一字符串咋办呢?那就自己再另外写个函数呗
/*
*功 能:发送一串字符串
*参 数:传入字符串首地址
*返回值:无
*/
void UART1_SendString(const char *str)
{
uint8_t i = 0;
//判断是否达到字符串末尾
while( *(str+i) != '\0')
{
//检测是否发送完成
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
//发送数据
USART_SendData(USART1, *(str+i));
i++;
}
}