UART

处理器与外部设备通信的两种方式:
并行通信
串行通信
按照数据传送方向,分为:
数据传输只支持数据在一个方向上传输
允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信;
允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。

同步通信:带时钟同步信号传输。
**异步通信:**不带时钟同步信号。

UART:通用异步收发器
USART:通用同步异步收发器
uart和usart的区别
UART与USART都是单片机上的串口通信,他们之间的区别如下:
首先从名字上看:
UART:universal asynchronous receiver and transmitter通用异步收/发器
USART:universal synchronous asynchronous receiver and transmitter通用同步/异步收/发器
从名字上可以看出,USART在UART基础上增加了同步功能,即USART是UART的增强型,事实也确实是这样。但是具体增强到了什么地方呢?
其实当我们使用USART在异步通信的时候,它与UART没有什么区别,但是用在同步通信的时候,
区别就很明显了:大家都知道同步通信需要时钟来触发数据传输,也就是说USART相对UART的区别之一就是能提供主动时钟
UART异步通信方式引脚连接方法:
-RXD:数据输入引脚。数据接受。
-TXD:数据发送引脚。数据发送。

全双工异步通信。
小数波特率发生器系统,提供精确的波特率。
可配置的16倍过采样或8倍过采样,因而为速度容差与时钟容差的灵活配置提供了可能。
可编程的数据字长度(8位或者9位);
可配置的停止位(支持1或者2位停止位);
可配置的使用DMA多缓冲器通信。
单独的发送器和接收器使能位。
检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志
多个带标志的中断源。触发中断。
其他:校验控制,四个错误检测标志。


数据在两个串口之间进行通讯,常常会出现丢失数据的现象,比如当接收端数据缓冲区满了,而发送端还有数据发送过来,本质原因是速度不匹配,处理能力不匹配。
流控就是为了解决这个速度匹配的问题,它的含义非常简单,当接收端处理数据处理不过来时,就向发送端发送不在接收信息,当发送端接收到这个信号之后,就会停止发送,直到收到可以继续发送信号在继续发送。
流控的方式有两种,一种是硬件流控,一种是软件流控
1、很多模块默认出厂设置硬件参数配置如下:
配置串口,遵循“9600, N, 8,1”
9600,波特率
N,无校验位
8,数据位
1,停止位
对于ARM的高速芯片,波特率默认上浮到115200bps
使能端口A的硬件时钟
使能串口1的硬件时钟
端口A引脚配置为复用功能模式
引脚连接到串口1硬件
配置串口相关的参数:波特率、校验位、数据位、停止位
配置串口中断
编写中断服务函数(用来接收数据)
串口1发送数据、接收数据
注意:
乱码情况,文本的格式可能不一样,PLL参数是否正确

#include "usart.h"
#include "stm32f4xx.h"
#include
//usart a9t a10r
static uint8_t USART1_buf[64];//接收缓存区
void USART1_init(uint32_t bps)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
// GPIOA时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
// 使能串口1的硬件时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
// 引脚连接到串口1硬件
GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);
// 端口A引脚A9、A10配置为复用功能模式
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10; //第9号跟10号引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用模式
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz; //高速
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP; //推挽输出,增加输出电流能力
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL; //没有上拉下拉电阻
GPIO_Init(GPIOA, &GPIO_InitStructure);
// 配置串口相关的参数:波特率、校验位、数据位、停止位
USART_InitStructure.USART_BaudRate = bps; //波特率设置
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //数据位长度8位
USART_InitStructure.USART_StopBits = USART_StopBits_1; //停止位1位
/* When using Parity the word length must be configured to 9 bits */
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);
// 配置串口1的中断优先级
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 配置串口1中断触发方式,接收一个字节触发中断
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
// 使能串口1工作
USART_Cmd(USART1, ENABLE);
}
//串口1中断服务函数 接收数据
void USART1_IRQHandler(void)
{
uint16_t d;
static uint16_t cnt = 0;
if(USART_GetITStatus(USART1,USART_IT_RXNE) == SET)//检测中断标志
{
if(cnt < 63)//接收数据
{
USART1_buf[cnt] = USART_ReceiveData(USART1);
}
else
{
cnt = 0;
}
//将接收到的数据,返发给PC
USART_SendData(USART1, USART1_buf[cnt]);
cnt ++;
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
//清空标准位
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
}
void USART1_SendChar(uint16_t c)
{
USART_SendData(USART1,c);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
}
void USART1_SendData(char *data)
{
while(*data!='\0')
{
USART1_SendChar(*data);
data++;
}
}
//buf:接收缓存首地址
//len:读到的数据长度
void MyUSART1_RcvData(char *buf,uint8_t *len)
{
uint8_t i=0;
while(USART1_buf[i]!='\0')
{
buf[i] = USART1_buf[i];
i++;
}
if(i==0)
*len = 0;
else
*len = i-1;
}
int main(void)
{
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
init_led_beep();
USART1_init(9600);
USART1_SendData("hello world\r\n");
char buf[64]={0};
uint8_t len;
while(1)
{
memset(buf,0,64);
delay_xms(40);
MyUSART1_RcvData(buf,&len);
if(len == 0)
continue;
if(strstr(buf,"open beep")!= NULL)
BEEP = 1;
if(strstr(buf,"close beep")!= NULL)
BEEP = 0;
if(strstr(buf,"open led")!= NULL)
Led_All_On2();
if(strstr(buf,"close led")!= NULL)
Led_All_off2();
}
}
《CortexM3与M4权威指南.pdf》583页 18.1章节
A common task for beginners is to generate a simple output message of“Hello world!” In C language, this is commonly handled with a “printf” state-ment. Under the hood, the message output can be redirected to different formsof communication interfaces. Typically this is known as re-targeting. Forexample, it is very common to retarget printf to a UART during embedded soft-ware development
《CortexM3与M4权威指南.pdf》584页 18.2.2章节
In Keil ? MDK-ARM (or other ARM ? toolchains such as DS-5 ? Professional), thefunction that needs to be implemented to support printf is “fputc.”

/* Short version of retarget.c - Minimum code to support simple printf in Keil MDK-ARM */
/**************************************************************/
/* Minimum retarget functions for ARM DS-5 Professional / Keil MDK */
/**************************************************************/
#pragma import(__use_no_semihosting_swi)
#include "stm32f4xx.h"
#include
struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;
FILE __stdin;
int fputc(int ch, FILE *f) {
return (ITM_SendChar(ch));
}
void _sys_exit(int return_code) {
label: goto label; /* endless loop */
}
Pragma用于指示编译器完成一些特定的动作
在嵌入式程序编译时如果出现printf、fopen、fclose等文件操作,因程序中并没有对这些函数的底层实现,使得设备运行时会出现软件中断BAEB处,这时就需要
__use_no_semihosting_swi这个声明,使程序遇到这些文件操作函数时不停在此中断处
应用,指纹锁、手机无线传输模块,如WiFi模块,蓝牙模块,GPRS模块,GPS模块,4G模块、串口屏
test
struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;
FILE __stdin;
static uint8_t USART1_buf[64];//接收缓存区
//重定向printf,实际上是重定向这个fputc
int fputc(int ch, FILE *f)
{
USART_SendData(USART1, ch);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
return ch;
}
#include "myusart.h"
#include "stdio.h"
struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;
FILE __stdin;
//重定向printf
int fputc(int ch,FILE *f)
{
USART_SendData(USART1,ch);
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE) == RESET);
return ch;
}
void MYUSAERT_Init(uint32_t bps)
{
USART_InitTypeDef USART_InitStructure;
GPIO_InitTypeDef GPIO_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
//串口时钟使能,GPIO时钟使能
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
//引脚复用
GPIO_PinAFConfig(GPIOA,GPIO_PinSource9,GPIO_AF_USART1);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource10,GPIO_AF_USART1);
//GPIO端口模式设置
/* Configure PG6 and PG8 in output pushpull mode */
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9 | GPIO_Pin_10;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
GPIO_Init(GPIOA, &GPIO_InitStructure);
//串口初始化
/* USARTx configured as follows:
- BaudRate = bps baud
- Word Length = 8 Bits
- One Stop Bit
- No parity
- Hardware flow control disabled (RTS and CTS signals)
- Receive and transmit enabled
*/
USART_InitStructure.USART_BaudRate = bps;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
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
NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE);
//使能串口
USART_Cmd(USART1,ENABLE);
}
//中断处理函数
void USART1_IRQHandler(void)
{
//串口数据收发
//串口传输动态获取
if(USART_GetITStatus(USART1,USART_IT_RXNE)== SET)//检测中断标志,如果中断被触发,接收到一个字节的数据
{
uint16_t d = USART_ReceiveData(USART1); //从USART1获取数据
USART_SendData(USART1,d); //将接收到的数据返回给PC
while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)== RESET);
USART_ClearITPendingBit(USART1,USART_IT_RXNE);//清空标志位
}
}
int main(void)
{
/* Configure two bits for preemption priority */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
MYUSAERT_Init(9600);
printf("hello world");
while(1);
}