串口作为 MCU 的重要外部接口,基本上所有的 MCU 都会带有串口。而STM32F407ZET6 最多可提供 6 路串口。
通信按传输方向以下几种方式:
a.单工:
数据传输只支持数据在一个方向上传输(收音机、遥控器)
b.半双工:
允许数据在两个方向上传输,但是,在某一时刻,只允许数
据在一个方向上传输,它实际上是一种切换方向的单工通信;(对讲机)
c.全双工:
允许数据同时在两个方向上传输,因此,全双工通信是两个
单工通信方式的结合,它要求发送设备和接收设备都有独立
的接收和发送能力。(电话机)
串行通信的通信方式
a.同步通信:带时钟同步信号传输。 -SPI,IIC通信接口
b.异步通信:不带时钟同步信号。 -UART(通用异步收发器),单总线
USART与UART的区别:
USART:全双工通用同步/异步串行收发器
UART:全双工通用异步串行收发器
USART/UART是一种通用的标准接口,根据导线的电压等不同也分为很多的同类,比如: RS485, RS422,RS232
UART异步通信方式特点:
波特率(band rate)= 1波特=1bps(位/秒)1
void Usart1_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 串口是挂载在 APB2 下面的外设,(多goto函数查看原理有助于理解)
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//使用的是串口 1,串口 1 对应着芯片引脚 PA9,PA10 需要使能PA的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
//设置引脚复用器映射
//引脚复用器映射配置,需要配置PA9,PA10 的引脚,调用函数为:
//PA9 复用为 USART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
//PA10 复用为 USART1
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //GPIOA9 与 GPIOA10
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //配置IO口复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度 50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
//初始化 PA9,PA10
GPIO_Init(GPIOA,&GPIO_InitStructure);
USART_InitStruct.USART_BaudRate = 115200; //一般设置为 115200;
USART_InitStruct.USART_WordLength = USART_WordLength_8b; //字长为 8 位数据格式
USART_InitStruct.USART_StopBits = USART_StopBits_1; //一个停止位
USART_InitStruct.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件控制流
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 双全工
//初始化串口
USART_Init(USART1, &USART_InitStruct);
//配置串口接收中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
USART_Cmd(USART1, ENABLE);
}
void NVIC_Configuration(void)
{
//NVIC分组(一个工程当中只能配置一次分组)抢占优先级2位,值范围:0~3;响应优先级2位,值范围:0~3;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; //NVIC通道,在stm32f4xx.h可查看通道 (可变)
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x01; //响应优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能
//配置中断分组(NVIC),并使能中断。
NVIC_Init(&NVIC_InitStruct);
}
main.c
#include "stm32f4xx.h"
#include "led.h"
#include "key.h"
#include "delay.h"
#include "usart.h"
#define LED0_ON GPIO_ResetBits(GPIOF, GPIO_Pin_9)
#define LED0_OFF GPIO_SetBits(GPIOF, GPIO_Pin_9)
u8 Usart_Data; //值范围:0~255
u8 rx_flag = 0; //接受数据完成 rx_flag = 1
//服务函数
void USART1_IRQHandler(void)
{
//若是非空,则返回值为1,与RESET(0)判断,不相等则判断为真
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
//判断为真后,为下次中断做准备,则需要对中断的标志清零
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
/* DR读取接受到的数据*/
Usart_Data = USART_ReceiveData(USART1);
rx_flag = 1; //接受数据完成 rx_flag = 1
}
}
int main(void)
{
//时钟初始化,详细步骤可看前几篇文章
Delay_Init();
//需要对led进行初始化,详细步骤可看前几篇文章
Led_Init();
Usart1_Init();
while(1)
{
if(rx_flag == 1)
{
//串口输入1
if(Usart_Data == '1') //亮灯
{
LED0_ON;
}
if(Usart_Data == '0') //灭灯
{
LED0_OFF;
}
rx_flag = 0;
}
}
return 0;
}
调用标准库,使用printf函数
#include "stdio.h"
#pragma import(__use_no_semihosting)
//标准库需要的支持函数
struct __FILE
{
int handle;
};
FILE __stdout;
//定义_sys_exit()以避免使用半主机模式
int _sys_exit(int x)
{
x = x;
}
//重定义fputc函数 printf 是一个宏
int fputc(int ch, FILE *f)
{
USART_SendData(USART1,ch); //通过串口发送数据
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
return ch;
}
升级版串口服务函数,接收字符串
void USART1_IRQHandler(void)
{
//若是非空,则返回值为1,与RESET(0)判断,不相等则判断为真
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET)
{
//判断为真后,为下次中断做准备,则需要对中断的标志清零
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
/* DR读取接受到的数据*/
buffer[count++] = USART_ReceiveData(USART1);
if(buffer[count-1] == ':')
{
//输入以‘:’结尾,并剔除
for(rx_i=0; rx_i< (count-1); rx_i++)
{
rx_buffer[rx_i] = buffer[rx_i];
}
memset(buffer, 0, sizeof(buffer));
count = 0; //置为0,下一帧数据从buffer[0]开始存储
rx_flag = 1; //接受数据完成 rx_flag = 1
}
}
}
升级版控制引脚宏定义
sys.h
#ifndef __SYS_H_
#define __SYS_H_
#include "stm32f4xx.h"
//IO口操作宏定义
#define BITBAND(addr, bitnum) ((addr & 0xF0000000)+0x2000000+((addr &0xFFFFF)<<5)+(bitnum<<2))
#define MEM_ADDR(addr) *((volatile unsigned long *)(addr))
#define BIT_ADDR(addr, bitnum) MEM_ADDR(BITBAND(addr, bitnum))
//IO口地址映射
#define GPIOA_ODR_Addr (GPIOA_BASE+20) //0x40020014
#define GPIOB_ODR_Addr (GPIOB_BASE+20) //0x40020414
#define GPIOC_ODR_Addr (GPIOC_BASE+20) //0x40020814
#define GPIOD_ODR_Addr (GPIOD_BASE+20) //0x40020C14
#define GPIOE_ODR_Addr (GPIOE_BASE+20) //0x40021014
#define GPIOF_ODR_Addr (GPIOF_BASE+20) //0x40021414 20 = 0x14
#define GPIOG_ODR_Addr (GPIOG_BASE+20) //0x40021814
#define GPIOH_ODR_Addr (GPIOH_BASE+20) //0x40021C14
#define GPIOI_ODR_Addr (GPIOI_BASE+20) //0x40022014
#define GPIOA_IDR_Addr (GPIOA_BASE+16) //0x40020010 16 = 0x10
#define GPIOB_IDR_Addr (GPIOB_BASE+16) //0x40020410
#define GPIOC_IDR_Addr (GPIOC_BASE+16) //0x40020810
#define GPIOD_IDR_Addr (GPIOD_BASE+16) //0x40020C10
#define GPIOE_IDR_Addr (GPIOE_BASE+16) //0x40021010
#define GPIOF_IDR_Addr (GPIOF_BASE+16) //0x40021410
#define GPIOG_IDR_Addr (GPIOG_BASE+16) //0x40021810
#define GPIOH_IDR_Addr (GPIOH_BASE+16) //0x40021C10
#define GPIOI_IDR_Addr (GPIOI_BASE+16) //0x40022010
//IO口操作,只对单一的IO口!
//确保n的值小于16!
#define PAout(n) BIT_ADDR(GPIOA_ODR_Addr,n) //输出
#define PAin(n) BIT_ADDR(GPIOA_IDR_Addr,n) //输入
#define PBout(n) BIT_ADDR(GPIOB_ODR_Addr,n) //输出
#define PBin(n) BIT_ADDR(GPIOB_IDR_Addr,n) //输入
#define PCout(n) BIT_ADDR(GPIOC_ODR_Addr,n) //输出
#define PCin(n) BIT_ADDR(GPIOC_IDR_Addr,n) //输入
#define PDout(n) BIT_ADDR(GPIOD_ODR_Addr,n) //输出
#define PDin(n) BIT_ADDR(GPIOD_IDR_Addr,n) //输入
#define PEout(n) BIT_ADDR(GPIOE_ODR_Addr,n) //输出
#define PEin(n) BIT_ADDR(GPIOE_IDR_Addr,n) //输入
#define PFout(n) BIT_ADDR(GPIOF_ODR_Addr,n) //输出
#define PFin(n) BIT_ADDR(GPIOF_IDR_Addr,n) //输入
#define PGout(n) BIT_ADDR(GPIOG_ODR_Addr,n) //输出
#define PGin(n) BIT_ADDR(GPIOG_IDR_Addr,n) //输入
#define PHout(n) BIT_ADDR(GPIOH_ODR_Addr,n) //输出
#define PHin(n) BIT_ADDR(GPIOH_IDR_Addr,n) //输入
#define PIout(n) BIT_ADDR(GPIOI_ODR_Addr,n) //输出
#define PIin(n) BIT_ADDR(GPIOI_IDR_Addr,n) //输入
#endif
升级版串口,通过蓝牙手机控制硬件
就是将USART1的初始化全部改成2,并初始化相应引脚,及蓝牙的波特率9600
#include "usart.h"
void Usart2_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
// 串口是挂载在 APB1 下面的外设,所以使能函数为
//使能 USART2 时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2,ENABLE);
//使用的是串口 2,串口 2 对应着芯片引脚 PA2,PA3 需要使能PA的时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
//设置引脚复用器映射
//引脚复用器映射配置,需要配置PA2,PA3 的引脚,调用函数为:
//PA2 复用为 USART2
GPIO_PinAFConfig(GPIOA,GPIO_PinSource2,GPIO_AF_USART2);
//PA3 复用为 USART2
GPIO_PinAFConfig(GPIOA,GPIO_PinSource3,GPIO_AF_USART2);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3; //GPIOA2 与 GPIOA3
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //配置IO口复用功能
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //速度 50MHz
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽复用输出
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP; //上拉
//初始化 PA2,PA3
GPIO_Init(GPIOA,&GPIO_InitStructure);
USART_InitStruct.USART_BaudRate = 9600; //蓝牙为9600;
USART_InitStruct.USART_WordLength = USART_WordLength_8b; //字长为 8 位数据格式
USART_InitStruct.USART_StopBits = USART_StopBits_1; //一个停止位
USART_InitStruct.USART_Parity = USART_Parity_No; //无奇偶校验位
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //硬件控制流
USART_InitStruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式 双全工
//初始化串口
USART_Init(USART2, &USART_InitStruct);
NVIC_InitStruct.NVIC_IRQChannel = USART2_IRQn; //NVIC通道,在stm32f4xx.h可查看通道 (可变)
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0x01; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0x01; //响应优先级
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE; //使能
//配置中断分组(NVIC),并使能中断。
NVIC_Init(&NVIC_InitStruct);
//配置串口接收中断
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);
USART_Cmd(USART2, ENABLE);
}
RS-232-C标准规定的数据传输速率为每秒 50、75、100、150、300、600、1200、2400、4800、9600、19200、115200等波特率。 ↩︎