简介
USART英文名字: Universal Synchronous/Asynehronous Receive/Transmitter.
最少需要3根线, GND, RX(接收), TX(发送). 当只需单向数据传输时, 可以只用一根通信线, 当电平标准不一致时, 需要加电平转换芯片.
全双工(发时收,收时发), 异步(不需要时钟线), 单端(必须共地), 点对点(1对1)通信.
通信协议: 制定通信的规则, 通信双方按照协议规则进行数据收发.
串口参数
- 波特率: 通信的速率.
- 起始位: 标志一个数据帧的开始, 固定为低电平.
- 数据位: 数据帧的有效载荷, 1为高电平, 0为低电平, 低位现行.
- 校验位: 用于数据验证, 根据数据位计算得来, 奇校验, 偶校验.
- 停止位: 用于数据帧间隔, 固定为高电平
- 空闲状态: 为高电平
一帧数据有10/11位.
低位现行: 从最低位开始发送.
串口常用电平标准
- TTL电平: +3.3v或+5v为1, 0v为0.
- RS232电平: -3v到-15v为1, +3v到+15v为0.
- RS485电平: 差分信号, 两线压差+2到+6v为1, -2到-6v为0.
波特率和比特率的区别
波特率: 单位: 码元/s, 波特.
比特率: 每秒传输的比特数, 单位: bit/s, bps.
二进制中一个码元等于1bit, 所以波特率等于比特率, 其他进制中不同.
USART外设
USART是STM32内部集成的硬件外设, 可根据数据寄存器的一个字节数据自动生成数据帧时序, 从TX发送, RX自动接收, 并拼接为一个字节数据, 存放在数据寄存器里.
发送/读取数据位, 是电路自动完成的, 也可以用GPIO软件模拟收发, 但是大大增加CPU负担, 这里只研究硬件电路.
stm32自带波特率发生器, 最高达4.5Mbit/s
可配置数据位长度:8/9
停止位长: 0.5/1/1.5/2
支持同步模式, 硬件流控制(多一根线, 防止发送快处理慢, 导致数据丢失, 反馈信号线, RTS,CTS), DMA(大量数据收发), 智能卡, LrDA, LIN.
输入数据采样, 在时钟中间位置, 选3个数据位, 全为1 = 1, 全为0 = 0. 如果2个1, 1个0 = 0, 但是噪声标志位EA = 1.
接收数据两种方式
- 查询: 不用配置中断, 在主循环中不断查询RXNE寄存器, 浪费资源还容易反应迟钝.
- 中断: RXNE中断
说明很多外设都可以不中断使用查询标志位方式, 但紧急处理还是要使用中断.
程序逻辑
- RCC使能GPIO, USART
- GPIO初始化, TX复用输出, RX上拉/浮空输入
- USART初始化
- *中断配置ITConfig
- *配置NVIC
- 开启USART
如果只要发, 不用接收, 不用配置带星号的.
参考代码
头文件
#ifndef __USART_H
#include "stm32f10x.h" // Device header
#include <stdio.h>
#include <stdarg.h>
#define GPIO_TX GPIO_Pin_9
#define GPIO_RX GPIO_Pin_10extern uint16_t receiveData;//接收数据
extern uint16_t receiveFlag;//接收标志void USART_Config(void);//USART配置
void sendByte(uint16_t Data);//发送1字节
void sendNum(uint32_t num);//发送数字
void sendString(char * str);//发送字符串
void serialPrintf(char* format, ...);//类似printf函数#endif
源文件
#include "USART.h"
uint16_t receiveData = 0;
uint16_t receiveFlag = 0;void USART_Config(void)
{
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Pin = GPIO_TX;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStruct.GPIO_Pin = GPIO_RX;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStruct);
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 9600;
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;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init(USART1, &USART_InitStruct);
USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn;
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 1;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStruct);
USART_Cmd(USART1, ENABLE);
}void sendByte(uint16_t Data)
{
USART_SendData(USART1, Data);
while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET); //等待发送完成
}void sendString(char * str)
{
uint16_t i = 0;
while(str[i] != '\0')
{
sendByte(str[i++]);
}
}static uint16_t numOfDigital(uint32_t num) //输出数字的位数
{
uint16_t sz = 0;
while(num != 0)
{
num /= 10;
sz++;
}
return sz;
}static uint32_t numPow(uint32_t num, uint16_t sz)
{
uint32_t Result = 1;
while (sz --)
{
Result *= num;
}
return Result;
}void sendNum(uint32_t num)
{
uint16_t sz = numOfDigital(num);
for(uint16_t i = 0; i < sz; i++)
{
sendByte(num/numPow(10,sz-i-1)%10 + '0');
}
}void USART1_IRQHandler(void)
{
if(USART_GetITStatus(USART1, USART_IT_RXNE) == SET)
{
receiveData = USART_ReceiveData(USART1);
receiveFlag = 1;
USART_ClearITPendingBit(USART1, USART_IT_RXNE);//清除接收中断标志位
}
}int fputc(int ch, FILE* f)//重定向fputc,printf底层就是fputc,只有串口1能用
{
sendByte(ch);
return ch;
}void serialPrintf(char* format, ...)
{
char string[100];
va_list arg;
va_start(arg, format);
vsprintf(string, format, arg);
va_end(arg);
sendString(string);
}
逻辑分析仪分析信号
发送数据0x41波形图如下所示.
其他问题
发送中文字符不换行
在魔术棒 -> C/C++ -> Misc Controls 写入:
--no-multibyte-chars
中文编码字节数
UTF-8: 3字节
GBK: 2字节
sprintf()
char str[100];
sprintf(str, "hello world %d", 123); //将字符串打印到str数组中去.