(1)关于RS-232
在串口通信中RS-232 电平标准的信号不能直接
被控制器直接识别,所以这些信号会经过一个“电平转换芯片”转换成控制器能识别的
“TTL 校准”的电平信号,才能实现通讯
控制器一般使用 TTL 电平标准,所以常常会使用 MA3232 芯片对 TTL 及 RS-232
电平的信号进行互相转换。
TTL标准:逻辑1:2.4V到5V
逻辑0:0到0.5V
RS-232 :逻辑1:-15V到-3V
逻辑0:+3v~+15v
在旧式的台式计算机中一般会有 RS-232 标准的 COM 口(也称 DB9 接口),接线口以针式引出信号线的称为公头,以孔式引出信号线的称为母头.
接收数据 RXD DTE-DCE Receive Data,数据接收信号,即输入。
发送数据 TXD DTE-DCE Transmit Data,数据发送信号,即输出。两个
设备之间的 TXD 与 RXD 应交叉相连。
数据设备
(DCE) 就绪DSR DT-DCE Data Set Ready,数据发送就绪,用于 DCE 告知对方本机是否处于待命状态
公头5GND对应母头5GND
(2)串口异步和同步区别
stm32中将sclk(时钟)关闭就为异步通信,开启时为同步传输。异步通信电平间隔不固定,同步固定。
(3)波特率
以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码,常见的波特率为
4800、9600、115200 等
(4)起始和停止信号
串口通讯的一个数据包从起始信号开始,直到停止信号结束。数据包的起始信号由一个逻辑 0 的数据位表示。
数据包的停止信号可由 0.5、1、1.5 或 2 个逻辑 1 的数据位表示,只要双方约定一致即可。
(5)有效数据
在数据包的起始位之后紧接着的就是要传输的主体数据内容,也称为有效数据,有效数据的长度常被约定为 5、6、7 或 8 位长。
(6)数据校验
在有效数据之后,有一个可选的数据校验位。校验方法有奇校验(odd)、偶校验(even)、0 校验(space)、1 校验(mark)以及无校验(noparity)。
奇校验要求有效数据和校验位中“1”的个数为奇数。
注:0 校验是不管有效数据中的内容是什么,校验位总为“0”,1 校验是校验位总为
“1”
在无校验的情况下,数据包中不包含校验位
(7)USART
(1)USART 满足外部设备对工业标准 NRZ 异步串行数据格式的要求,并且使用了小数波特率发生器,可以提供多种波特率,使得它的应用更加广泛。USART 支持同步单向通信和半双工单线通信;USART 支持使用 DMA,可实现高速数据通信。
(2)应用:
USART 在 STM32 应用最多莫过于“打印”程序信息,一般在硬件设计时都会预留一
个 USART 通信接口连接电脑,用于在调试程序是可以把一些调试信息“打印”在电脑端
的串口调试助手工具上,从而了解程序运行是否正确、指出运行出错位置等等
ps:STM32 的 USART 输出的是 TTL 电平信号,若需要 RS-232 标准的信号可使用
MAX3232 芯片进行转换。
(3)相关寄存器
TX:发送数据输出引脚。
RX:接收数据输入引脚。
SCLK:发送器时钟输出引脚。这个引脚仅适用于同步模式。
数据寄存器:USART 数据寄存器(USART_DR):只有低 9 位有效,并且第 9 位数据是否有效要取决于
USART 控制寄存器 1(USART_CR1)的 M 位设置,当 M 位为 0 时表示 8 位数据字长,当 M
位为 1 表示 9 位数据字长,我们一般使用 8 位数据字长。
USART_DR 实际是包含了两个
寄存器,一个专门用于发送的可写 TDR,一个专门用于接收的可读 RDR。往 USART_DR 写入数据会自动存储在 TDR 内;当进行读取操作时,向 USART_DR读取数据会自动提取 RDR 数据。
TDR 和 RDR 都是介于系统总线和移位寄存器之间。串行通信是一个位一个位传输的,
发送时把 TDR 内容转移到发送移位寄存器,然后把移位寄存器数据每一位发送出去,接收
时把接收到的每一位顺序保存在接收移位寄存器内然后才转移到 RDR。
(4)一个字符帧发送需要三个部分:起始位+数据帧+停止位,起始位是一个位周期的低电平,数据帧就是我们要发送的 8 位或 9 位数据,停止位是一定时间周期的高电平,可选 0.5 个、1 个、1.5 个和 2 个停止位。
(5)标志位:
TE 发送使能
TXE 发送寄存器为空,发送单个字节的时候使用
TC 发送完成,发送多个字节数据的时候使用
TXIE 发送完成中断使能
TE 置 1 之后,发送器开始会先发送一个空闲帧(一个数据帧长度的高电平),接下来就可以往 USART_DR 寄存器写入要发送的数据
写入最后一个数据后,需要等待 USART 状态寄存器(USART_SR)的 TC 位为 1,表示数据传输完成。
USART_CR1 寄存器的 TCIE 位置 1,将产生中断。
RX:接受使能:USART_CR1 寄存器的 RE 位置 1,使能 USART 接收,接收器在 RX 线开始搜索起始位
RXNE:读数据寄存器非空//数据接收完成后就把接收移位寄存器数据移到 RDR 内,并把 USART_SR 寄存器的 RXNE 位置1,
RXNEIE:发送完成中断使能
ps:
OVER8 位设置为 1 采用 8 倍过采样,
即用 8 个采样信号采样一位数据;如果 OVER8 位设置为 0 采用 16 倍过采样,即用 16 个采
样信号采样一位数据,8倍比16倍更快,16倍比8倍更精准。
接收数据时如果出现奇偶校验位验证失败,会见 USART_SR 寄存
器的 PE 位置 1,并可以产生奇偶校验中断
(6)串口中断请求:
发送数据寄存器为空 中断标志TXE 中断使能TXEIE
发送完成 TC TCIE
准备好读取接收到的数据 RXNE RXNEIE
奇偶校验错误 PE PEIE
USART 初始化结构体
1 typedef struct {
2 uint32_t USART_BaudRate; // 波特率
3 uint16_t USART_WordLength; // 字长
4 uint16_t USART_StopBits; // 停止位
5 uint16_t USART_Parity; // 校验位
6 uint16_t USART_Mode; // USART 模式
7 uint16_t USART_HardwareFlowControl; // 硬件流控制
8 } USART_InitTypeDef;
USART_BaudRate:波特率设置。一般设置为 2400、9600、19200、115200。
USART_WordLength:数据帧字长,可选 8 位或 9 位。它设定 USART_CR1 寄存
器的 M 位的值。如果没有使能奇偶校验控制,一般使用 8 数据位;如果使能了奇
偶校验则一般设置为 9 数据位。
USART_StopBits:停止位设置,可选 0.5 个、1 个、1.5 个和 2 个停止位,
USART_Parity : 奇 偶 校 验 控 制 选 择 , 可 选 USART_Parity_No( 无校验 ) 、
USART_Parity_Even( 偶校验 ) 以 及 USART_Parity_Odd( 奇校验 )。
USART_Mode:USART 模式选择,有 USART_Mode_Rx 和 USART_Mode_Tx,
允许使用逻辑或运算选择两个,它设定 USART_CR1 寄存器的 RE 位和 TE 位。
利用 USART 实现开发板与电脑通信,需要用到一个 USB 转 USART 的 IC,我们选
择 CH340G 芯片来实现这个功能。
二:程序设计
bsp_USART.h
#ifndef BSP_USART_H
#define BSP_USART_H
#include "stm32f4xx_gpio.h"
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_usart.h"
#include
#define SIZE 50000
extern void usart1_init(void);
extern uint8_t usart_receivebyte(USART_TypeDef* USARTx);
extern void usart_Sendbyte(USART_TypeDef* USARTx, uint8_t tem);
extern void tem_DM2_Config(void);
#endif
bsp_USART.c
#include "bsp_usart.h"
uint16_t SendBuff[SIZE];
void usart1_init(void)
{
//结构体声明
GPIO_InitTypeDef GPIO_InitStruct;
//结构体初始化
USART_InitTypeDef USART_InitStruct;
//void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct);
//void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)
//开启GPIOA时钟开启usart1时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//配置GPIO A9和A10复用
GPIO_InitStruct.GPIO_OType= GPIO_OType_PP;//推挽模式
GPIO_InitStruct.GPIO_PuPd= GPIO_PuPd_UP;//上拉
GPIO_InitStruct.GPIO_Speed=GPIO_Fast_Speed;//50Mhz
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;//复用模式
GPIO_PinAFConfig( GPIOA, GPIO_PinSource9,GPIO_AF_USART1);//连接 PXx 到 USARTx_Tx*
GPIO_Init(GPIOA, &GPIO_InitStruct);//初始化结构体
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
GPIO_PinAFConfig( GPIOA, GPIO_PinSource10,GPIO_AF_USART1);//连接 PXx 到 USARTx_Rx*
GPIO_Init(GPIOA, &GPIO_InitStruct);
//在配置USART1 1位停止位 8位数据位 无校验位 波特率115200
USART_InitStruct.USART_BaudRate=115200;
USART_InitStruct.USART_StopBits=USART_StopBits_1 ;
USART_InitStruct.USART_WordLength=USART_WordLength_8b;
USART_InitStruct.USART_Parity=USART_Parity_No;//无校验位
USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None;//硬件流控制:不使用硬件流
//使能发送和接受
USART_InitStruct.USART_Mode=USART_Mode_Tx|USART_Mode_Rx;
//放入结构体初始化
USART_Init(USART1, &USART_InitStruct);
//开启串口功能
USART_Cmd( USART1,ENABLE);
}
主函数:
#include "stm32f4xx.h"
#include "bsp_usart.h"
#include "stm32f4xx_usart.h"
#include "bsp_led.h"
#include
#include "bsp_exit.h"
#include "bsp_key.h"
extern uint16_t SendBuff[SIZE];
uint8_t data;
void delay_ms(unsigned int xms)
{
unsigned int j;
for(;xms>0;xms--)
for(j=500;j>0;j--);
}
int main(void)
{
uint16_t i;
LED_GPIO_Config();
usart1_init();
key_Config();
while(1)
{
USART_SendData( USART1, 'b');//串口不断发送b
//
// if(USART_ReceiveData( USART1)=='a')//电脑接受到a
{
GPIO_SetBits(GPIOC , GPIO_Pin_1);
}
if(USART_ReceiveData( USART1)=='b')
{
GPIO_SetBits(GPIOC , GPIO_Pin_2);
}
if(USART_ReceiveData( USART1)=='c')
{
GPIO_SetBits(GPIOC , GPIO_Pin_3);
}
if(USART_ReceiveData( USART1)=='d')
{
GPIO_SetBits(GPIOC , GPIO_Pin_4);//按下按键abcd 4个灯全亮
}
}
三 重定义printf和scanf
int fputc(int ch ,FILE *f)//重定义Printf到串口后才能使用printf
{
USART_SendData(USART1, (uint8_t)ch);//发送一个字节到串口
while(USART_GetFlagStatus( USARTx, USART_FLAG_TXE)==RESET);//等待发送完毕,当发送标志位为1时发送完毕,没有为0一直等待
return ch;
}
//重定义scanf getchar函数到串口
//int fgetc(FILE *f)
//{
//
// while(USART_GetITStatus( USART1, USART_IT_TXE)==RESET);//等待输入数据标志位为1结束输入
// return (int) USART_ReceiveData( USARTx);
//}
#include "stm32f4xx.h"
#include "bsp_usart.h"
#include "stm32f4xx_usart.h"
#include "bsp_led.h"
#include
#include "bsp_exit.h"
#include "bsp_key.h"
extern uint16_t SendBuff[SIZE];
uint8_t data;
void delay_ms(unsigned int xms)
{
unsigned int j;
for(;xms>0;xms--)
for(j=500;j>0;j--);
}
int main(void)
{
uint16_t i;
LED_GPIO_Config();
usart1_init();
key_Config();
ch=getchar();
printf("接收到字符:%c\n",ch);
}
四:回调实验
#include "stm32f4xx.h"
#include "bsp_usart.h"
#include "stm32f4xx_usart.h"
#include "bsp_led.h"
#include
#include "bsp_exit.h"
#include "bsp_key.h"
extern uint16_t SendBuff[SIZE];
uint8_t data;
void delay_ms(unsigned int xms)
{
unsigned int j;
for(;xms>0;xms--)
for(j=500;j>0;j--);
}
int main(void)
{
uint16_t i;
LED_GPIO_Config();
usart1_init();
key_Config();
//当按键1按下时为0发送字符a同时点亮c口led1
if(key_Scan( GPIOA,GPIO_Pin_0)==Key_OFF)//
{
USART_SendData (USART1, 'a');
GPIO_SetBits(GPIOC , GPIO_Pin_1);
}
//按下按键2发送串口1接受的数据(由串口助手接受并发送)
if(key_Scan( GPIOC,GPIO_Pin_13)==Key_OFF)
{
GPIO_SetBits(GPIOC , GPIO_Pin_2);
USART_SendData (USART1, USART_ReceiveData( USART1));//发送串口接受到的数据
}
}
///***°´按键检测*****printf***/
//
// if(key_Scan( GPIOA,GPIO_Pin_2)==Key_OFF)
// {
// printf("\r\n·串口ð\r\n");
// GPIO_SetBits(GPIOC , GPIO_Pin_3);
//
// }
五.回调实验
自定义一个串口发送和接受程序
void usart_Sendbyte(USART_TypeDef* USARTx, uint8_t tem)
{
USART_SendData(USART1, tem);
while(USART_GetFlagStatus( USART1, USART_FLAG_TXE)==RESET);//等标志位为1退出
//USART_FLAG_TXE发送缓冲区空标志:说明可以往数据寄存器写入数据了,不代表数据发送完成
}
//TXE 位由硬件置 1,它表示:
// 数据已从 TDR 移到移位寄存器中且数据发送已开始。
//TDR 寄存器为空。
///USART_DR 寄存器中可写入下一个数据,而不会覆盖前一个数据
uint8_t usart_receivebyte(USART_TypeDef* USARTx)
{
uint8_t temp = 0;
while(USART_GetFlagStatus( USART1, USART_FLAG_RXNE)==RESET);//RXNE接受数据寄存器非空标志位
//RXNE 位置 1。这表明移位寄存器的内容已传送到 RDR。也就是说,
//已接收到并可读取 数据(以及其相应的错误标志)
temp = (int) USART_ReceiveData( USARTx);//从RDR读取数据
USART_ClearFlag(USART1, USART_FLAG_RXNE);//RXNE 位必须在结束接收下一个字符前清零,以
避免发生上溢错误。
return temp;
}
//上溢错误
//如果在 RXNE 未复位时接收到字符,则会发生上溢错误。RXNE 位清零前,数据无法从移位
//寄存器传送到 RDR 寄存器。
主函数:
while(1)
{
/***********/
// //printf("\r\n·¢ËÍ'a'µÆÁÁ·¢ËÍ'b'µÆÃð\r\n");
// data=usart_receivebyte(USART1);//»Øµ÷ʵÑé
// switch(data)
// {
// case 65 : USART_SendData (USART1, 'a');//接受到'a'的ascll码
// break;
//
//
// }
注意查看参考手册串口状态寄存器章节