通信接口背景知识
分为同步通信和异步通信,
同步带时钟同步信息传输
异步不带时钟同步信号
STM32F103ZET6一共有5个串口引脚
其中有三个USART有同步异步通信功能,两个UART只有异步通信功能
串口通信一般是使用异步通信,这样节省资源。
只需要一个RXD,一个TXD,一个GND。
STM32串口异步通信需要定义的参数
1.起始位
2.数据位(8位或9位)
3.奇偶校验位(第9位)
4.停止位(1,1.5,2位)
5.波特率设置 收发两端要一致!
串口相关的寄存器
USART_SR状态寄存器用到表中事件,
两个数据包之间间隔十毫秒,那就是两个不同时刻发出的数据,这个数据就叫做帧,一帧的数据。帧的长度由这个数据到下一个数据间隔多长判断。
USART_DR数据寄存器 32位只应用了9位。即DR[8:0] 。
USART_BRR波特率寄存器,要保证接收方波特率和发送方一致
串口通信的相关函数
初始化函数 bsp_InitUart
第一步:配置GPIO:1.打开GPIO时钟2.打开UART时钟3.配置USART Tx为复用功能4.配置USART Rx为复用功能
第二步:配置串口硬件参数
第三步:Usart1 NVIC配置
第四步:使能串口1
中断服务函数:USART1_IRQHandler
状态清零函数:Uart0_STA_CIr
打印输出函数:fput
数据发送函数:USART1_Send_Data
USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)
中断的配置函数,第一个是哪一个串口,第二个代表的是哪一个中断(不知道取值范围的就GOTO看一下,方法之前都提到过)第三个代表是否使能中断。
现在我们开始操作一下串口1的示例工程。
首先建立bsp_uart.c和bsp_uart.h文件,在bsp.h里加入bsp_uart.h.
然后分别在.c和.h加入源文件模板和头文件模板并在头文件包含区加入#include "bsp.h".
然后我们需要在.h里先宏定义一个缓存空间和使能接收
#define USART_REC_LEN 1024
#define EN_USART1_RX 1
然后回到.c文件里定义变量缓存空间和接收长度,接收状态
uint8_t USART_RX_BUF[USART_REC_LEN] = {0}; //缓存空间
uint16_t RxCounter = 0; //接收长度
uint8_t ReceiveState = 0; //接收状态,是否接收到一帧数据
因为可能会外部调用这些变量,所以我们在.h里要进行一下外部声明:
extern uint8_t USART_RX_BUF[USART_REC_LEN]; //缓存空间
extern uint16_t RxCounter; //接收长度
extern uint8_t ReceiveState; //接收状态,是否接收到一帧数据
接下来开始.c里书写函数实体
串口1的初始化:
首先是GPIO和USART,NVIC配置,先定义结构体
GPIO_InitTypeDef GPIO_InitStruct;
USART_InitTypeDef USART_InitStruct;
NVIC_InitTypeDef NVIC_InitStruct;
然后打开对应时钟(NVIC不用时钟配置)
RCC_APB2PeriphResetCmd(RCC_APB2Periph_GPIOA, ENABLE);
RCC_APB2PeriphResetCmd(RCC_APB2Periph_USART1, ENABLE);
接着配置GPIO参数
//配置GPIO PA9 TX和PA10 RX
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; //RX设置为浮空输入 输入不用写速度
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_Init(GPIOA,&GPIO_InitStruct);
然后是配置串口1
//3.设置串口 数据位8位,停止位1位,无奇偶校验,无硬件流控,收发模式
USART_InitStruct.USART_BaudRate =baud; //波特率
USART_InitStruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
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);
再配置NVIC中断
//4.设置中断NVIC
NVIC_InitStruct.NVIC_IRQChannel = USART1_IRQn; //通道号在stm32f10x_h里找
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 3;
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;
NVIC_Init(&NVIC_InitStruct);
然后使能串口1
//开启中断
USART_ITConfig(USART1,USART_IT_RXNE,ENABLE); //USART_IT_RXNE是接收中断
USART_ITConfig(USART1,USART_IT_IDLE,ENABLE); //USART_IT_IDLE是空闲中断
USART_Cmd(USART1,ENABLE);
这时候初始化函数才刚写完,然后我们写服务函数(hd.s里找)和对应的清除状态函数
*********************************************************************************************************
* 函 数 名: USART1_IRQHandler
* 功能说明: 串口1的中断服务函数
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void USART1_IRQHandler(void)
{
uint8_t Res = Res; //定义变量读取相关寄存器,方便清除相关中断
if(USART_GetITStatus(USART1,USART_IT_RXNE) != RESET)//查询状态(串口1的接收中断)
{
USART_RX_BUF[RxCounter++] = USART1->DR ; //DR是数据寄存器,把接收到的字节放到缓存空间里
}
if(USART_GetITStatus(USART1,USART_IT_IDLE ) != RESET)//查询状态(串口1的空闲中断)
{
Res = USART1->SR; //读取状态寄存器
Res = USART1->DR; //读取数据寄存器(先读SR再读DR是为了清除空闲中断)
ReceiveState = 1; //标志接收到1帧数据
}
}
/*
*********************************************************************************************************
* 函 数 名: USART1_Clr(void)
* 功能说明: 串口1清除函数
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
void USART1_Clr(void)
{
RxCounter = 0;//数据长度清零
ReceiveState = 0; //清除接收状态
}
学过C语言的都知道格式化打印输出函数printf。在stm32里使用printf就必须要进行一些操作这是我们是使用这个代码去使用,原理不用细究,可以直接拿来使用,放到函数实体区域的最上面
//支持使用Printf
#if 1
#pragma import(__use_no_semihosting)
struct __FILE
{
int handle;
};
FILE __stdout;
void _sys_exit(int x) //避免半主机模式
{
x = x;
}
/*
*********************************************************************************************************
* 函 数 名: fputc
* 功能说明: 重定义putc函数,串口1和printf做关联
* 形 参:无
* 返 回 值: 无
*********************************************************************************************************
*/
int fputc(int ch,FILE *f)
{
while((USART1->SR &0x40 ) == 0) {}; //等待发送完成
USART1->DR = (uint8_t)ch;
return ch;
}
#endif
最后我们写一下发送的函数
/*
*********************************************************************************************************
* 函 数 名: USART1_Send_Data
* 功能说明: USART1 发送len个字
* 形 参:buf:发送区首地址
* len:发送的字节数 0~255
* 返 回 值: 无
*********************************************************************************************************
*/
void USART1_Send_Data(uint8_t *buf,uint8_t len)
{
uint16_t t;
for(t = 0;t<len;t++)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
USART_SendData(USART1,buf[t]);
}
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
}
然后在bsp.c的初始化里加入
bsp_InitUart1(115200); /*配置串口1的波特率为115200bps*/
在bsp_uart.h里加入.c里写入的函数的声明,该工程就完成了,接下来我在main.c里写一下实现功能实例就结束了
main.c:
#include "bsp.h" /* 底层硬件驱动 */
/*
*********************************************************************************************************
* 函 数 名: main
* 功能说明: c程序入口
* 形 参:无
* 返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{
uint8_t ucKeyCode;
bsp_Init(); /* 硬件初始化 */
printf("串口1的实例/r/n");
while(1)
{ bsp_Idle();
/* 处理按键事件*/
ucKeyCode = bsp_GetKey();
switch(ucKeyCode)
{
case WKUP_DOWN:
{
bsp_BEEPToggle();
printf("按键WKUP按下/r/n");
}
}
}
}