通讯的基本概念
通讯按数据传送的方式分为串行通讯与并行通讯
串行通讯:设备之间通过少量数据信号线,地线及控制信号线,按数据位形式一位一位地传输数据
并行通讯:使用8、6、32及64根或更多的数据线进行传输的通讯方式,可以同时传输多个数据位的数据
通讯的方向分为全双工、半双工及单工,以信道的方式来区分
通讯按数据同步方式分为同步和异步,根据通讯过程中是否有使用到时钟信号进行简单的区分
同步通讯:收发设备双方会使用一根信号线表示时钟信号,在时钟信号的驱动下双方进行协调,通讯中双方会同意规定在时钟信号的上升沿或者下降沿对数据线进行采样
异步通讯:异步通讯中不使用时钟信号进行数据同步,它们在数据信号中穿插一些同步的信号位,或者把主体数据进行打包,以数据帧的格式传输数据,有些通讯中还需要双方约定数据的传输速率
通讯速率:衡量通讯性能的重要参数,以比特率表示,即每秒钟传输的二进制位数,单位为bit/s,不要跟波特率混淆了,波特率是每秒钟传输了多少个码元
通讯协议以分层的方式最基本的分为物理层和协议层,简单理解,物理层规定我们用什么形式交流,嘴巴还是肢体,协议层规定的是语言类型,我们用中文还是英文
本文讲解的是串口异步通讯,串口通讯的数据包是由启始位、主体数据、校验位及停止位组成。一个数据包从起始信号开始,直到停止信号结束
校验方法有奇校验、偶校验、0校验、1校验
奇校验:要求有效数据和校验位中1的个数为奇数,偶校验相反,要求个数为偶数
0校验不管有效数据内容,校验位总为0,1校验总为1
串行通信一般是以桢格式传输数据
发送器
USART有专门控制发送的发送器、控制接收的接收器,在使用USART之前需要向USART_CR1寄存器的UE位置1使能USART,UE位用来开启供给串口的时钟。发送或者接收的数据字长可选8位或者9位,有USART_CR1的M位控制
在发送数据中,编程中重要的标志位
TE-----发送使能
TXE-------发送寄存器为空,发送单个字节的时候使用
TC----------发送完成,发送多个字节数据的时候使用
TXIE--------发送完成中断使能
接收器
USART_CR1寄存器的RE位置1,能使USART接收
RE---------接收使能
RXNE------读数据寄存器非空
RXNEIE----发送完成中断使能
波特率的计算公式
USART初始化结构体
标准库函数对每个外设都建立一个初始化结构体,结构体成员用于设置外设工作参数,并有外设初始化配置函数,设定参数将会设置外设相应的寄存器,达到配置外设工作环境的目的。
//USART初始化结构体
typedef struct {
uint32_t USART_BaudRate;//波特率
uint16_t USART_WordLength; // 字长
uint16_t USART_StopBits; // 停止位
uint16_t USART_Parity; // 校验位
uint16_t USART_Mode; // USART 模式
uint16_t USART_HardwareFlowControl; // 硬件流控制
} USART_InitTypeDef;
参数说明:
USART_BaudRate: 波特率设置
USART_WordLength:数据帧字长,可选 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_HardwareFlowControl: 硬件流控制选择,只有在硬件流控制模式才有效,可选有使能 RTS、使能 CTS、同时使能 RTS 和 CTS、不使能硬件流。
//USART 时钟初始化结构体
typedef struct {
uint16_t USART_Clock; // 时钟使能控制
uint16_t USART_CPOL; // 时钟极性
uint16_t USART_CPHA; // 时钟相位
uint16_t USART_LastBit; // 最尾位时钟脉冲
} USART_ClockInitTypeDef;
USART_Clock:同步模式下 SCLK 引脚上时钟输出使能控制,可选禁止时钟输出
(USART_Clock_Disable)或开启时钟输出(USART_Clock_Enable);如果使用同步模式发送,一般都需要开启时钟。它设定 USART_CR2 寄存器的 CLKEN 位的值。
USART_CPOL:同步模式下 SCLK 引脚上输出时钟极性设置,可设置在空闲时
SCLK 引脚为低电平(USART_CPOL_Low)或高电平(USART_CPOL_High)。它设
定 USART_CR2 寄存器的 CPOL 位的值。
USART_CPHA:同步模式下 SCLK 引脚上输出时钟相位设置,可设置在时钟第一个变化沿捕获数据(USART_CPHA_1Edge)或在时钟第二个变化沿捕获数据。它设定 USART_CR2 寄存器的 CPHA 位的值。USART_CPHA 与 USART_CPOL 配合使用可以获得多种模式时钟关系。
USART_LastBit:选择在发送最后一个数据位的时候时钟脉冲是否在 SCLK 引脚
输出,可以是不输出脉冲 (USART_LastBit_Disable) 、输出脉冲
(USART_LastBit_Enable)。
编程思路
这边使用了printf直接打印到串口,这边我们要改一下配置,fputc()是printf()的底层函数,参考下面的博文
STM32的UART读写及printf打印:https://blog.csdn.net/qq_26904271/article/details/80113740
完整代码
UART.h
#ifndef _UART_H_
#define _UART_H_
void USART_config(void);
#endif
UART.c
#include "UART.h"
#include "stm32f10x.h"
#include "stdio.h"
static void NVIC_Configuration(void){ //中断控制器
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel=USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1;
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void init_gpio(void){
GPIO_InitTypeDef GPIO_InitStructure;
//打开串口GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
//将RX的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_9;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
//将TX的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
void USART_init(void){
USART_InitTypeDef USART_InitStructure;
//打开串口外设时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//配置串口的工作参数
USART_InitStructure.USART_BaudRate=115200;//波特率为115200
USART_InitStructure.USART_WordLength=USART_WordLength_8b;//数据长度为1
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(USART1,&USART_InitStructure);
//串口中断优先级配置
NVIC_Configuration();
//接收数据
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
//打开串口
USART_Cmd(USART1,ENABLE);
}
void Sendbyte(USART_TypeDef *pUSARTx,uint8_t ch){
USART_SendData(pUSARTx,ch);//·发送一个字节数据到USART
//j等待发送数据寄存器为空
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET);
}
void SendString(USART_TypeDef *pUSARTx,char *str){
while(*str!='\0'){
Sendbyte(pUSARTx,*str++);
}
//等待发送完成
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET){
}
}
void USART_config(){ //初始化串口和GPIO
USART_init();
init_gpio();
}
int fputc(int ch,FILE *f){
Sendbyte(USART1, ch);
return ch;
}
//禁用半主机模式
#pragma import(__use_no_semihosting)
struct __FILE
{
int a;
};
FILE __stdout;
void _sys_exit(int x)
{
}
main.c
#include "UART.h"
#include "stdio.h"
void delay(){
int i;
int j;
for(i=0;i<300;i++)
for(j=0;j<100;j++);
}
int main()
{
USART_config();
while(1){
delay();
printf("hello\n");
}
}
资料内容来源:野火 零死角玩转stm32–F10指南