基本知识
串口通讯 (Serial Communication)
串口中断其实也是依靠判断寄存器的状态进行中断服务的
通信的速率
Bitrate—比特率:每秒钟传输的二进制位数,单位为比特每秒(bit/s)
Baudrate—波特率:表示每秒钟传输的码元个数
一个二进制位表示一个码元 0V —— 0
两个二进制位表示一个码元
TTL电平转USB电平
处理器与外部设备通信的两种方式
![image.png](#align=left&display=inline&height=337&margin=[object Object]&name=image.png&originHeight=674&originWidth=813&size=306771&status=done&style=none&width=406.5)
并行通信
- 传输原理:数据各个位同时传输。
- 优点:速度快
- 缺点:占用引脚资源多
串行通信
- 传输原理:数据按位顺序传输。
- 优点:占用引脚资源少
- 缺点:速度相对较慢
![image.png](#align=left&display=inline&height=101&margin=[object Object]&name=image.png&originHeight=202&originWidth=1094&size=41026&status=done&style=none&width=547)
按照数据传送方向
- 单工:数据传输只支持数据在一个方向上传输
- 半双工:允许数据在两个方向上传输,但是,在某一时刻,只允许数据在一个方向上传输,它实际上是一种切换方向的单工通信
- 全双工:允许数据同时在两个方向上传输,因此,全双工通信是两个单工通信方式的结合,它要求发送设备和接收设备都有独立的接收和发送能力。
![image.png](#align=left&display=inline&height=250&margin=[object Object]&name=image.png&originHeight=499&originWidth=616&size=116084&status=done&style=none&width=308)
串行通信的通信方式
- 同步通信:带时钟同步信号传输。SPI,IIC通信接口
- 异步通信:不带时钟同步信号。UART(通用异步收发器),单总线
| 通信标准 | 引脚说明 | 通信方式 | 通信方向 |
| — | — | — | — |
| UART
(通用异步收发器) | TXD:发送端
RXD:接受端
GND:公共地 | 异步通信 | 全双工 |
| 单总线
(1-wire) | DQ:发送/接受端 | 异步通信 | 半双工 |
| SPI | SCK:同步时钟
MISO:主机输入,从机输出
MOSI:主机输出,从机输入 | 同步通信 | 全双工 |
| I2C | SCL:同步时钟
SDA:数据输入/输出端 | 同步通信 | 半双工 |
- UART:通用异步收发器 USART:通用同步异步收发器
- RXD:数据输入引脚。数据接受。 TXD:数据发送引脚。数据发送。
![image.png](#align=left&display=inline&height=119&margin=[object Object]&name=image.png&originHeight=237&originWidth=590&size=36281&status=done&style=none&width=295)
[tr]串口号RXDTXD[/tr]
1 | PA10 | PA9 |
2 | PA3 | PA2 |
3 | PB11 | PB10 |
4 | PC11 | PC10 |
5 | PD2 | PC12 |
![image.png](#align=left&display=inline&height=86&margin=[object Object]&name=image.png&originHeight=171&originWidth=555&size=86944&status=done&style=none&width=277.5)
![image.png](#align=left&display=inline&height=164&margin=[object Object]&name=image.png&originHeight=328&originWidth=686&size=102771&status=done&style=none&width=343)
物理层
RS-232 串口标准常用于计算机、路由与调制调解器(MODEN,俗称“猫”) 之 间的通讯,在这种通讯系统中,设备被分为数据终端设备 DTE(计算机、路由) 和数据通讯设备 DCE(调制调解器)
![image.png](#align=left&display=inline&height=77&margin=[object Object]&name=image.png&originHeight=153&originWidth=663&size=69493&status=done&style=none&width=331.5)
![image.png](#align=left&display=inline&height=123&margin=[object Object]&name=image.png&originHeight=246&originWidth=425&size=24068&status=done&style=none&width=212.5)
- 公头与母头
- 使用逻辑 1 表示信号有效,逻辑 0 表示信号无效
- 一般只使用 RXD、TXD 以及 GND 三条信号线,直 接传输数据信号
![image.png](#align=left&display=inline&height=54&margin=[object Object]&name=image.png&originHeight=107&originWidth=667&size=26511&status=done&style=none&width=333.5)
![image.png](#align=left&display=inline&height=117&margin=[object Object]&name=image.png&originHeight=234&originWidth=442&size=63502&status=done&style=none&width=221)
协议层
![image.png](#align=left&display=inline&height=56&margin=[object Object]&name=image.png&originHeight=112&originWidth=649&size=53210&status=done&style=none&width=324.5)
异步通讯中由于没有时钟信号 (如前面讲解的DB9接口中是 没有时钟信号的),所以两个通讯设备之间需要约定好波特率,即每个码元的长度,以便对信号进行解码。
在stm32中需要定义的参数
- 起始位
- 数据位(8位或者9位)
- 奇偶校验位(第9位)
- 停止位(1,15,2位)
- 波特率设置
![image.png](#align=left&display=inline&height=88&margin=[object Object]&name=image.png&originHeight=175&originWidth=1012&size=29539&status=done&style=none&width=506)
虚线分开的每一格就是代表一个码元。
波特率
通讯的起始和停止信号
有效数据
数据校验:奇校验 (odd)、偶校验 (even)、0 校验 (space)、1 校验 (mark) 以及无校验 (noparity)。
![image.png](#align=left&display=inline&height=343&margin=[object Object]&name=image.png&originHeight=686&originWidth=680&size=172203&status=done&style=none&width=340)
![image.png](#align=left&display=inline&height=227&margin=[object Object]&name=image.png&originHeight=453&originWidth=552&size=66480&status=done&style=none&width=276)
5.3 节对串口有过简单的介绍
在 4.4.1 章节端口复用功能已经讲解过
8.1.11 外设的 GPIO 配置
当然要初始化 NVIC 设置中 断优先级别,最后编写中断服务函数
串口初始化程序
- 串口时钟使能,GPIO 时钟使能
- 串口复位
- GPIO端口模式设置
- 串口参数初始化
- 开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)
- 使能串口
- 编写中断处理函数,实现数据的接收和发送
USART_SR状态寄存器
USART_DR数据寄存器
USART_BRR波特率寄存器
波特率的计算
![image.png](#align=left&display=inline&height=139&margin=[object Object]&name=image.png&originHeight=277&originWidth=908&size=110979&status=done&style=none&width=454)
![image.png](#align=left&display=inline&height=142&margin=[object Object]&name=image.png&originHeight=283&originWidth=927&size=101376&status=done&style=none&width=463.5)
![image.png](#align=left&display=inline&height=166&margin=[object Object]&name=image.png&originHeight=332&originWidth=593&size=206292&status=done&style=none&width=296.5)
实现效果
1.STM32 通过串口和上位机的对话, STM32 在收到上位机发过来的字符串后,原原本本的返回给上位机。
2.电脑给单片机发命令,用于控制开发板上的灯。
相应的接口
代码理解
1.我都不知的正点原子是写了个啥玩野,还是去看看程序讲解视频吧
2.正点原子的是真**看不懂,好像有搞到寄存器中去了
main.c
main函数中没有什么,就是调用串口函数就好了
#include "stm32f10x.h"
#include "delay.h"
#include "sys.h"
#include "usart.h"
int main(void)
{
/*初始化USART 配置模式为 115200 8-N-1,中断接收*/
USART_Config();
while(1)
{
}
}
stm32f10x_it.c
配置串口中断服务函数,就是把串口中断服务函数加入其中了
#include "stm32f10x_it.h"
#include "usart.h"
// 串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
uint8_t ucTemp;
if(USART_GetITStatus(DEBUG_USARTx,USART_IT_RXNE)!=RESET)
{
ucTemp = USART_ReceiveData(DEBUG_USARTx);
USART_SendData(DEBUG_USARTx,ucTemp);
}
}
void NMI_Handler(void)
{
}
void HardFault_Handler(void)
{
/* Go to infinite loop when Hard Fault exception occurs */
while (1)
{
}
}
void MemManage_Handler(void)
{
/* Go to infinite loop when Memory Manage exception occurs */
while (1)
{
}
}
void BusFault_Handler(void)
{
/* Go to infinite loop when Bus Fault exception occurs */
while (1)
{
}
}
void UsageFault_Handler(void)
{
/* Go to infinite loop when Usage Fault exception occurs */
while (1)
{
}
}
void SVC_Handler(void)
{
}
void DebugMon_Handler(void)
{
}
void PendSV_Handler(void)
{
}
void SysTick_Handler(void)
{
}
usar.h
基本的配置函数,一个发送数据,然后一个打印到上位机中,重点是重定向
#include "sys.h"
#include "usart.h"
static void NVIC_Configuration(void)
{
NVIC_InitTypeDef NVIC_InitStructure;
/* 嵌套向量中断控制器组选择 */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
/* 配置USART为中断源 */
NVIC_InitStructure.NVIC_IRQChannel = DEBUG_USART_IRQ;
/* 抢断优先级*/
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
/* 子优先级 */
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
/* 使能中断 */
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
/* 初始化配置NVIC */
NVIC_Init(&NVIC_InitStructure);
}
/**
* @brief USART GPIO 配置,工作参数配置
* @param 无
* @retval 无
*/
void USART_Config(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
// 打开串口GPIO的时钟
DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_CLK, ENABLE);
// 打开串口外设的时钟
DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK, ENABLE);
// 将USART Tx的GPIO配置为推挽复用模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_TX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(DEBUG_USART_TX_GPIO_PORT, &GPIO_InitStructure);
// 将USART Rx的GPIO配置为浮空输入模式
GPIO_InitStructure.GPIO_Pin = DEBUG_USART_RX_GPIO_PIN;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(DEBUG_USART_RX_GPIO_PORT, &GPIO_InitStructure);
// 配置串口的工作参数
// 配置波特率
USART_InitStructure.USART_BaudRate = DEBUG_USART_BAUDRATE;
// 配置 针数据字长
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(DEBUG_USARTx, &USART_InitStructure);
// 串口中断优先级配置
NVIC_Configuration();
// 使能串口接收中断
USART_ITConfig(DEBUG_USARTx, USART_IT_RXNE, ENABLE);
// 使能串口
USART_Cmd(DEBUG_USARTx, ENABLE);
}
/***************** 发送一个字节 **********************/
void Usart_SendByte( USART_TypeDef * pUSARTx, uint8_t ch)
{
/* 发送一个字节数据到USART */
USART_SendData(pUSARTx,ch);
/* 等待发送数据寄存器为空 */
while (USART_GetFlagStatus(pUSARTx, USART_FLAG_TXE) == RESET);
}
/****************** 发送8位的数组 ************************/
void Usart_SendArray( USART_TypeDef * pUSARTx, uint8_t *array, uint16_t num)
{
uint8_t i;
for(i=0; i
{
/* 发送一个字节数据到USART */
Usart_SendByte(pUSARTx,array
);
}
/* 等待发送完成 */
while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}
/***************** 发送字符串 **********************/
void Usart_SendString( USART_TypeDef * pUSARTx, char *str)
{
unsigned int k=0;
do
{
Usart_SendByte( pUSARTx, *(str + k) );
k++;
} while(*(str + k)!='