1.通信接口相关知识:
(1)处理器与外界设备通信的两种方式:
1)并行通信:
-
传输原理:数据各个位同时传输;
-
优点:速度快;
-
缺点:占用引脚资源多;
2)串行通信:
- 传输原理:数据按位顺序传输;
- 优点:占用引脚资源少;
- 缺点:速度相对较慢;
(2)按照数据传送方向分为:
- 单工:数据传输只支持数据在一个方向上传输;
- 半双工:允许数据在两个方向上传输,但在某一时刻,只允许数据在一个方向上传输,实际是一种切换方向的单工通信;
- 全双工:允许数据同时在两个方向上传输,所以全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力;
2.串行通信的通信方式:
- 同步通信:带时钟同步信号传输;如SPI、IIC;
- 异步通信:不带时钟同步信号;如UART(通用异步收发器)、单总线;
3.常见的串行通信接口:
4.STM32F103串行通信:
(1)STM32的串行通信接口:
- UART:通用异步收发器;
- USART:通用同步异步收发器;
(2)UART异步通信方式引脚连接方法:
- RED:数据输入引脚。数据接收;
- TXD:数据发送引脚。数据发送;
(3)UART异步通信方式特点:
- 全双工异步通信;
- 分数波特率发生器系统,提供精确的波特率;发送和接收共用的可编程波特率,最高可达4.5Mbit/s;
- 可编程额数据字节长度(8位或9位);
- 可配置的停止位(支持1位或者2位停止位);
- 可配置的使用DMA多缓冲器通信;
- 单独的发送器和接收器使能位;
- 检测标志:1.接收缓冲器、2.发送缓冲器、3.传输结束标志;
- 多个带标志的中断源,触发中断;
- 其他:校验控制,四个错误检测标志;
(4)串口通信过程:
(5)STM32串口异步通信需要定义的参数:
- 起始位;
- 数据位(8位或9位);
- 奇偶校验位(第九位);
- 停止位(1,15,2位);
- 波特率设置;
5.库函数实现串口通信:
(1)串口配置的步骤:
- 串口时钟使能,GPIO时钟使能;
- 串口复位(不是必须);
- GPIO端口模式设置;
- 开启中断并且初始化NVIC(如果需要开启中断才需要该不步骤);
- 使能串口;
- 编写中断处理函数;
- 串口数据收发;
- 串口传输状态获取;
(2)串口相关原理图:
(3)实现结果:
STM32通过USART1实现与PC对话,STM32的USART1收到PC发来的数据后原封不动的返回给PC显示。同时使用LED0指示灯不断闪烁提示系统正常运行。
1)主函数:
#include "delay.h"
#include "led.h"
#include "usart1.h"
int main(){
u8 i=0;
u16 length=0;
u16 temp=0;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断分组
delay_init(); //延时初始化
LED_Init(); //LED初始化
usart1_Init(9600); //串口初始化
while(1){
if(USART1_RX_STA&0x8000) //判断是否接收完成->这里表示接收完成
{
length=USART1_RX_STA&0x3fff; //接收到的数据长度
for(temp=0;temp<length;temp++)
{
USART_SendData(USART1,USART1_RX_BUF[temp]); //将接收到的数据,发送出去
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)!=SET); //判断是否已经发送完成
}
USART1_RX_STA=0; //发送完成之后,对USART1_RX_STA进行清零
}
i++;
if(i%20==0)
{
LED0=!LED0;
}
delay_ms(10);
}
}
2)头文件:
#ifndef __USART1_H
#define __USART1_H
#define USART1_REC_LEN 200
typedef unsigned char u8;
typedef unsigned short u16;
typedef unsigned int u32;
extern u8 USART1_RX_BUF[USART1_REC_LEN]; //接收缓存的数据容器
extern u16 USART1_RX_STA; //标志位
void usart1_Init(u32 bund); //串口1初始化
#endif
3)串口1初始化函数:
#include "usart1.h"
#include "stm32f10x.h"
/*
功能:串口1的初始化
变量:bund:波特率
返回值:无
*/
void usart1_Init(u32 bund)
{
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
//1.GPIO时钟使能和端口复用时钟使能
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_USART1,ENABLE);
//2.配置GPIO->TX-PA9
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9; //PA9
GPIO_InitStruct.GPIO_Speed=GPIO_Speed_50MHz; //输出速度
GPIO_Init(GPIOA,&GPIO_InitStruct);
//配置GPIO->RX-PA10
GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_InitStruct.GPIO_Pin=GPIO_Pin_10; //PA9
GPIO_Init(GPIOA,&GPIO_InitStruct);
//3.初始化串口参数
USART_InitStruct.USART_BaudRate=bund; //设置波特率
USART_InitStruct.USART_HardwareFlowControl=USART_HardwareFlowControl_None; //不使用硬件流控制
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);
//4.设置串口中断优先级,使能串口中断通道
NVIC_InitStruct.NVIC_IRQChannel=USART1_IRQn; //设置串口1通道
NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE; //使能中断
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority=2; //子优先级
NVIC_Init(&NVIC_InitStruct);
//5.设置串口中断类型
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //串口1,接收中断,使能
//6.使能串口
USART_Cmd(USART1,ENABLE);
}
/*
功能:串口1中断服务函数
变量:无
返回值:无
设置通信方式-注意:
1.0x0A(换行),0x0D(回车);
2.识别累计接收的数据长度以及是否接收到0x0D和0x0A;当连续接收到0x0D和0x0A才算接收完成;
3.USART1_RX_STA的15位为1,则为接收完成的标志;USART1_RX_STA的14位为1,则接收到0x0D;
4.0~13位,表示接收的有效数据字节;
*/
u8 USART1_RX_BUF[USART1_REC_LEN]; //接收缓存的数据容器
u16 USART1_RX_STA=0; //标志位
void USART1_IRQHandler(void)
{
u8 data=0;
if(USART_GetITStatus(USART1,USART_IT_RXNE)) //获取串口接收中断标志
{
data=USART_ReceiveData(USART1); //将串口1接收到的数据,存储到data中;
if((USART1_RX_STA&0x8000)==0) //判断是否接收完成->这里是接收未完成
{
if((USART1_RX_STA&0x4000)) //判断是否接收到0x0D->这里是接收到了0x0D
{
if(data==0x0A) //判断接收到0X0D后,下一次接收的是否是0X0A->这里是已经接收到0x0A
{
USART1_RX_STA|=0x8000; //将USART1_RX_STA最高位设置为1,表示接收完成
}
else //未接收到0x0A
{
USART1_RX_STA=0; //接收错误,置0
}
}
else //未接收到0x0D
{
if(data==0x0D) //下一次接收到0x0D时
{
USART1_RX_STA|=0x4000; //将USART1_RX_STA的第14位设置为1
}
else //下一次并未接收到0X0D时
{
USART1_RX_BUF[USART1_RX_STA&0x3fff]=data; //将data内的数据,放入USART1_RX_BUF中
USART1_RX_STA++;
if(USART1_RX_STA>(USART1_REC_LEN-1)) //判断累计的次数是否大于数组极限容量值->这里表示超过
{
USART1_RX_STA=0; //接收错误,置0
}
}
}
}
}
}
6.串口通信printf
(1)使用结果:
在上诉实验中进行改进,对printf进行重定向,再通过printf函数将信息打印到串口调试助手上显示,同时LED0指示灯不断闪烁,表示系统正常运行。
(2)库函数实现:
1)主函数:在主函数中添加printf打印;
2)头文件:在头文件中添加#include "stdio.h";
3)串口初始化函数:对printf进行重定向;
注意:
在串口通信中需要使用printf打印时,需要勾选Use Micro LIB否则将无法打印出信息;