UART串口通讯---STM32
描述:STM32F103C8T6单片机与目标设备使用串口通讯;下面工程代码可以直接复制使用;
一,简介
串口通讯(Serial Communication)是一种设备间非常常用的串行通讯方式,因为它简单便捷,因此大部分电子设备都支持该通讯方式,其通讯协议可分层为协议层和物理层。物理层规定通信协议中具有机械、电子功能的特性,从而确保原始数据在物理媒体的传播;协议层主要规定通讯逻辑,统一双方的数据打包、解包标准。通俗的讲物理层规定我们用嘴巴还是肢体交流,协议层规定我们用中文还是英文交流;
STM32的UART特点:
1)全双工异步通信;
2)分数波特率发生器系统,提供精确的波特率。发送和接受共用的可编程波特率,最高可达4.5Mbits/s;
3)可编程的数据字长度(8位或者9位);
4)可配置的停止位(支持1或者2位停止位);
5)可配置的使用DMA多缓冲器通信;
6)单独的发送器和接收器使能位;
7)检测标志:① 接受缓冲器 ②发送缓冲器空 ③传输结束标志;
8)多个带标志的中断源,触发中断;
9)其他:校验控制,四个错误检测标志
STM32串口通信基础:
STM32的串口通信接口有两种,分别是:UART(通用异步收发器)、USART(通用同步异步收发器)。而对于大容量STM32F10x系列芯片,分别有3个USART和2个UART。
UART引脚连接方法:RXD-----数据输入引脚,数据接受;TXD-----数据发送引脚,数据发送。
对于两个芯片之间的连接,两个芯片GND共地,同时TXD和RXD交叉连接。这里的交叉连接的意思就是,芯片1的RxD连接芯片2的TXD,芯片2的RXD连接芯片1的TXD。这样,两个芯片之间就可以进行TTL电平通信了。
串口通讯的过程:
二、工程代码
串口操作的一般步骤:
1)GPIO时钟使能,串口时钟使能。调用函数:RCC_APB2PeriphClockCmd();
2)串口复位(这一步不是必须的)。调用函数:USART_DeInit();
3)GPIO外设功能下的端口模式设置。调用函数:GPIO_Init();Tx(发送引脚)配置为复用推挽输出(GPIO_Mode_AF_PP)用来发送数据,Rx(接收引脚)配置为浮空输入(GPIO_Mode_IN_FLOATING)用来接收数据。
4)串口参数初始化。调用函数:USART_Init();
5)开启中断并且初始化NVIC(如果需要开启中断才需要这个步骤)。调用函数:NVIC_Init();USART_ITConfig();
6)使能串口。调用函数:USART_Cmd();
7)编写中断处理函数。调用函数:USARTx_IRQHandler();
8)串口数据收发。调用函数:USART_SendData();USART_ReceiveData();
9)串口传输状态获取。调用函数:USART_GetFlagStatus();USART_ClearITPendingBit()
代码:
uart.c
#include "stm32f10x.h"
#include "usart2.h"
#include "string.h"
/*
A2 ---> TX
A3 ---> RX
*/
u8 USART2_RX_BUF[USART2_REC_LEN];
u16 USART2_RX_STA = 0;
void USART2_Init(void){
//GPIO端口设置
GPIO_InitTypeDef GPIO_InitStructure;
USART_InitTypeDef USART_InitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能USART1,GPIOA时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
//USART1_TX GPIOA.2
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2; //PA2接串口输出(Tx)脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIO
//USART1_RX GPIOA.3初始化
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3; //PA3接串口输入(Rx)脚
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStructure); //初始化GPIO
//Usart1 NVIC 配置
NVIC_InitStructure.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=1; //抢占优先级1
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //子优先级1
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure); //根据指定的参数初始化VIC寄存器
//USART 初始化设置
USART_InitStructure.USART_BaudRate = 115200; //串口波特率
USART_InitStructure.USART_WordLength = USART_WordLength_8b; //字长为8位数据格式
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(USART2, &USART_InitStructure); //初始化串口1
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //开启串口接受中断
USART_Cmd(USART2, ENABLE); //使能串口1
}
//
/
void USART2_IRQHandler(void) //串口1中断服务程序
{
u8 Res;
if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断(接收到的数据必须是0x0d 0x0a结尾)
{
Res =USART_ReceiveData(USART2); //读取接收到的数据
if((USART2_RX_STA&0x8000)==0) //接收未完成
{
if(USART2_RX_STA&0x4000) //接收到了0x0d
{
if(Res!=0x0a)USART2_RX_STA=0; //接收错误,重新开始
else USART2_RX_STA|=0x8000; //接收完成了
}
else //还没收到0X0D
{
if(Res==0x0d)USART2_RX_STA|=0x4000;
else
{
USART2_RX_BUF[USART2_RX_STA&0X3FFF]=Res ;
USART2_RX_STA++;
if(USART2_RX_STA>(USART2_REC_LEN-1))USART2_RX_STA=0; //接收数据错误,重新开始接收
}
}
}
}
}
/**
* 函 数:串口发送一个字节
* 参 数:Byte 要发送的一个字节
* 返 回 值:无
*/
void USART2_SendChar(char ch) {
// 发送字符数据 ch 到 USART3
USART_SendData(USART2, (u8) ch);//将字节数据写入数据寄存器,写入后USART自动生成时序波形
// 等待发送完成
while (USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); //等待发送完成
/*下次写入数据寄存器会自动清除发送完成标志位,故此循环后,无需清除标志位*/
}
/**
* 函 数:串口发送一个数组
* 参 数:Array 要发送数组的首地址
* 参 数:Length 要发送数组的长度
* 返 回 值:无
*/
void USART2_SendArray(u8 *Array, u16 Length)
{
u16 i;
for (i = 0; i < Length; i ++) //遍历数组
{
USART2_SendChar(Array[i]); //依次调用Serial_SendByte发送每个字节数据
}
}
/**
* 函 数:串口发送一个字符串
* 参 数:str 要发送的字符串
* 返 回 值:无
*/
void USART2_SendString(const char *str) {
while (*str) {
USART2_SendChar(*str); // 发送当前字符
str++; // 移动到下一个字符
}
// 发送换行和回车符
USART2_SendChar('\r'); // 发送回车符
USART2_SendChar('\n'); // 发送换行符
}
/***函数:串口接收函数**/
u16 CheckReceive(void)
{
u8 data; // 定义一个无符号 8 位整数型变量 data,用于存储接收到的数据
char buffer[8]; // 定义一个长度为 8 的字符数组 buffer,用于存储接收到的数据
int i = 0; // 定义一个整型变量 i,用于索引 buffer 数组
while (i < 7) // 数据发送完成会返回一个“OK”进入一个循环,循环条件是 i 小于 7
{
if (USART_GetFlagStatus(USART2, USART_FLAG_RXNE) != RESET) // 检查 USART2 接收缓冲区是否非空,即是否有数据可读
{
data = (u8)USART_ReceiveData(USART2); // 这个函数会接受最近串口收到的消息,并返回;如果接收缓冲区非空,则读取接收到的数据并存储到 data 变量中
buffer[i++] = data; // 将读取到的数据存储到 buffer 数组中,并将索引 i 自增
}
}
buffer[i] = '\0'; // 在 buffer 数组末尾添加字符串结束标志 '\0'
if (strcmp(buffer, "OK") == 0) return 0;
else return 1;
}
uart.h
#ifndef __USART2_H
#define __USART2_H
#include "stdio.h"
#include "sys.h"
//技术支持:
#define USART2_REC_LEN 200 //定义最大接收字节数 200
#define EN_USART2_RX 1 //使能(1)/禁止(0)串口1接收
extern char commands[][100]; // 声明数组 commands
extern u8 USART2_RX_BUF[USART2_REC_LEN]; //接收缓冲,最大USART_REC_LEN个字节.末字节为换行符
extern u16 USART2_RX_STA; //接收状态标记
void USART2_Init(void);
void USART2_SendChar(char ch);
void USART2_SendArray(u8 *Array, u16 Length);
void USART2_SendString(const char *str);
u16 CheckReceive(void);
#endif