寄存器与固件库的编程差异&STM32的USART窗口通讯

寄存器与固件库的编程差异&STM32的USART窗口通讯



前言

1.基于寄存器与基于固件库的stm32 LED流水灯例子的编程方式的差异;
2. 学习和阅读“零死角玩转STM32F103–指南者”文档中的第20、21章内容,完成STM32的USART窗口通讯程序,要求:
1)设置波特率为115200,1位停止位,无校验位。
2)STM32系统给上位机(win10)连续发送“hello windows!”,上位机接收程序可以使用“串口调试助手“,也可自己编程。
3)当上位机给stm32发送“Stop,stm32”后,stm32停止发送。


一、寄存器与固件库的编程差异

使用固件库,目前比较多的例程是使用固件库编写的。官方的例子也都采用固件库方式。特点就是简单,易于理解,资料多。如果你没有 CortexM 系列内核的开发基础,建议从固件库开始玩起。等有一定基础,或是特别需要时再用寄存器,本篇文章采用的就是固件库。
使用寄存器,想要深入理解 CortexM3 内核或是需要为了获得更好的可移植性,学习寄存器编程会比较有帮助。但是从专业的角度上看,寄存器更贴近底层,对外设的工作原理和运行机理会有更深的理解。

二、串口通讯的认识

1.了解通讯

串行通讯与并行通讯
串行通讯:设备之间通过少量数据信号线,地线以及控制信号线,按数据形式一位一位地传输数据。
并行通讯:设备之间通过信号线,同时传输多个数据位的数据。

串行通讯的通讯距离和抗干扰能力要优于并行通讯,并且成本更低,而并行通讯的传输速率要优于串行通讯

全双工,半双工和单工通讯
全双工通讯:设备之间可以同时收发数据。
半双工通讯:设备之间可以收发数据,但是不能够同时进行。
单工通讯:单方向的进行数据的发送和接收,即一个设备要么作为发送设备,要么作为接收设备。
同步通讯和异步通讯
同步通讯:收发双方使用同一个信号线作为时钟信号,在时钟信号的驱动下双方进行协调,同步数据。
异步通讯:不采用时钟信号进行数据同步,而是在数据信号中穿插一些同步用的信号位来实现同步。

2.相关介绍

两种电平标准
TTL标准:当电平处于2.4~5V之间时,表示逻辑1;当电平处于 0 ~0.5V时,表示逻辑0。
RS-232标准:当电平处于-15~-3V之间时,表示逻辑1;当电平处于3 ~15V时,表示逻辑0。
在这里插入图片描述
USB转串口通讯
USB转串口主要是设备跟电脑通信,该过程需要电平转换芯片来实现,常用的芯片有CH340,PL2303,CP2102,FT232。使用的时候需要安装电平转换芯片的驱动。
原生的串口到串口
主要是控制器跟串口设备或者传感器通信,不需要电平转换芯片来转换电平,直接使用TTL电平通信。例如GPS模块。
波特率与比特率
波特率即每秒钟传输的码元个数,便于对信号进行解码。常用的波特率4800,9600,115200。比特率即每秒钟传输的二进制位数。
通讯的起始和停止信号
起始信号由逻辑0的数据位表示,停止信号可由0.5,1.5,1或2个1的数据位来表示。双方自行约定。
校验
通过校验码来避免数据在传输过程中,受到外部干扰而发生偏差。常采用奇偶校验,只能检测出发生偏差位的1位。

3.通讯过程

串口设备间常见的通讯结构,如下图所示
在这里插入图片描述
两个通讯设备的 “ DB9 接口 ” 之间通过串口信号线建立起连接,串口信号线中使用 “ RS-232 标准 ” 传输数据信号。由于 RS-232 电频标准的信号不能直接被控制器直接识别,所以这些信号会经过一个 “ 电平转换芯片 ” 转换成控制器能识别的 “ TTL 标准 ” 的电平信号,才能实现通讯。

三、USART窗口通讯

①将使用已经建好的一个文件(使用固件库),将其备份成一个新文件
②在新文件中的User文件下,新建一个bsp_uart.h,bsp_uart.c,main.c文件
③使用Keil打开文件,将新建文件添加进来
在这里插入图片描述
④查看相关文件
在这里插入图片描述
相关数据的介绍
①USART初始化结构体

typedef struct
{
    
    
  uint32_t USART_BaudRate;//波特率BRR            
  uint16_t USART_WordLength;//字长CR1_M          	
  uint16_t USART_StopBits;//停止位CR2_STOP           
  uint16_t USART_Parity;//校验控制CR1_PCE,CR1_PS             
  uint16_t USART_Mode;//模式选择CR1_TE,CR1_RE(发送和接收)               
  uint16_t USART_HardwareFlowControl;//硬件流选择CR3_CTSE,CR3_RTSE 
} USART_InitTypeDef;

②同步时钟初始化结构体

typedef struct
{
    
    

  uint16_t USART_Clock;//同步时钟CR2_CLKEN(时钟使能)
  uint16_t USART_CPOL;//极性CR2_CPOL
  uint16_t USART_CPHA;//相位CR2_CPRA
  uint16_t USART_LastBit;//最后一个位的时钟脉冲CR2_LBC
} USART_ClockInitTypeDef;

相关固件函数介绍
①串口初始化函数

void USART_Init(USART_TypeDef* USARTx, USART_InitTypeDef* USART_InitStruct)

②中断配置函数

void USART_ITConfig(USART_TypeDef* USARTx, uint16_t USART_IT, FunctionalState NewState)

③串口使能函数

void USART_Cmd(USART_TypeDef* USARTx, FunctionalState NewState)

④数据发送函数

void USART_SendData(USART_TypeDef* USARTx, uint16_t Data)

⑤数据接收函数

uint16_t USART_ReceiveData(USART_TypeDef* USARTx)

⑥中断状态位获取函数

ITStatus USART_GetITStatus(USART_TypeDef* USARTx, uint16_t USART_IT)

代码编写
①一个简单的串口发送数据
bsp_uart.h

#ifndef _BSP_UART_H
#define _BSP_UART_H

#include "stm32f10x.h"
//串口1-USART1
#define DEBUG_UARTx										USART1;
#define DEBUG_UART_CLK      					RCC_APB2Periph_USART1
#define DEBUG_UART_APBxClkCmd					RCC_APB2PeriphClockCmd
#define DEBUG_UART_BAUDRATE						115200

//USART GPIO 引脚宏定义
#define DEBUG_USART_GPIO_CLK				  (RCC_APB2Periph_GPIOA)
#define DEBUG_USART_GPIO_APBxClkCmd		RCC_APB2PeriphClockCmd
#define DEBUG_USART_TX_GPIO_PORT			GPIOA
#define DEBUG_USART_TX_GPIO_PIN       GPIO_Pin_9
#define DEBUG_USART_RX_GPIO_PORT			GPIOA
#define DEBUG_USART_RX_GPIO_PIN				GPIO_Pin_10

#define DEBUG_USART_IRQ								USART1_IRQn
#define DEBUG_USART_IRQHandler				USART1_IRQHandler
void DEBUG_UART_Config(void);

#endif /*_BSP_UART_H*/

bsp_uart.c

#include "./uart/bsp_uart.h"

void DEBUG_UART_Config(void)
{
    
    
	/*第一步:初始化GPIO*/
	//定义GPIO对象
	GPIO_InitTypeDef GPIO_InitStructure;
	//定义串口对象
	USART_InitTypeDef USART_InitStructure;
	//打开串口GPIO的时钟
	DEBUG_USART_GPIO_APBxClkCmd(DEBUG_USART_GPIO_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);
	
	/*第二步:配置串口的初始化结构体*/
	//打开串口外设的时钟
	DEBUG_USART_APBxClkCmd(DEBUG_USART_CLK,ENABLE);
	//配置波特率
	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);
	/*第三步:使能串口*/
	USART_Cmd(DEBUG_USARTx,ENABLE);
}

main.c

#include "stm32f10x.h"
#include "./uart/bsp_uart.h"
int main(void)
{
    
    	
	DEBUG_UART_Config();
	USART_SendData(DEBUG_USARTx,0xaa);
	while(1);
}

编译
在这里插入图片描述
烧录程序的设置
在这里插入图片描述
选择对应的下载器
在这里插入图片描述
端口选择SW
在这里插入图片描述
将图中内容打上勾
在这里插入图片描述
编译程序,再次烧录
在这里插入图片描述
安装CH34驱动程序
链接: http://www.wch.cn/search?t=all&q=CH341A.
结果显示
在这里插入图片描述
串口的复杂通信
bsp_uart.h增添如下代码

void Usart_SendByte(USART_TypeDef * pUSARTx,uint8_t ch);
void USART_SendString(USART_TypeDef * pUSARTx,char *str);
void delay_ms(uint16_t delay_ms);

bsp_uart.c增添如下代码

//发送一个字节
void Usart_SendByte(USART_TypeDef * pUSARTx,uint8_t ch)
{
    
    
	USART_SendData(pUSARTx,ch);// 发送一个字节数据到USART
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TXE)==RESET);// 等待发送数据寄存器为空
}
//发送字符串
void USART_SendString(USART_TypeDef * pUSARTx,char *str)
{
    
    
	unsigned int k=0;
	do
	{
    
    
		Usart_SendByte(pUSARTx,*(str+k));
		k++;
	}while(*(str+k)!='\0');
	// 等待发送完成
	while(USART_GetFlagStatus(pUSARTx,USART_FLAG_TC)==RESET);
}
//微秒级的延时
void delay_us(uint32_t delay_s)
{
    
        
  volatile unsigned int i;
  volatile unsigned int t;
  for (i = 0; i < delay_s; i++)
  {
    
    
    t = 11;
    while (t != 0)
    {
    
    
      t--;
    }
  }
}
//毫秒级的延时函数
void delay_ms(uint16_t delay_ms)
{
    
        
  volatile unsigned int num;
  for (num = 0; num < delay_ms; num++)
  {
    
    
    delay_us(1000);
  }
}
//配置嵌套向量中断控制器NVIC
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);
}

//并在DEBUG_USART_Config函数第三步前面添加
//串口中断优先级配置
NVIC_Configuration();
//使能串口接收中断
USART_ITConfig(DEBUG_USARTx,USART_IT_RXNE,ENABLE);

main.c

#include "stm32f10x.h"
#include "./uart/bsp_usart.h"


// 接收缓冲,最大100个字节
uint8_t USART_RX_BUF[100];
// 接收状态标记位
uint16_t USART_RX_FLAG=0;

//串口中断服务函数
void DEBUG_USART_IRQHandler(void)
{
    
    
	uint8_t temp;
	//接收中断
	if(USART_GetFlagStatus(DEBUG_USARTx, USART_IT_RXNE) != RESET)
	{
    
    
		// 读取接收的数据
		temp = USART_ReceiveData(DEBUG_USARTx);
		//接收未完成
		if((USART_RX_FLAG & 0x8000)==0)
		{
    
    
			//接收到了0x0d
			if(USART_RX_FLAG & 0x4000)
			{
    
    
				// 接收错误,重新开始
				if(temp != 0x0a) USART_RX_FLAG=0;
				// 接收完成
				else USART_RX_FLAG |= 0x8000;
			}
			// 还未接收到0x0d
			else
			{
    
    
				if(temp == 0x0d)
				{
    
    
					USART_RX_FLAG |= 0x4000;
				}
				else
				{
    
    
					USART_RX_BUF[USART_RX_FLAG & 0x3FFF]=temp;
					USART_RX_FLAG++;
					//接收数据错误,重新开始接收
					if(USART_RX_FLAG > 99) USART_RX_FLAG=0;
				}
			}
		}
	}
}


int main(void)
{
    
    
	uint8_t len=0;
	uint8_t i=0;
	// USART初始化
	USART_Config();
	while(1)
	{
    
    
		if(USART_RX_FLAG & 0x8000)
		{
    
    
			// 获取接收到的数据长度
			len = USART_RX_FLAG & 0x3FFF;
			USART_SendString(DEBUG_USARTx, "发送消息:\n");
			for(i=0; i<len;i++)
			{
    
    
				// 向串口发送数据
				USART_SendData(DEBUG_USARTx, USART_RX_BUF[i]);
				//等待发送结束
				while(USART_GetFlagStatus(DEBUG_USARTx, USART_FLAG_TC)!=SET);
			}
			USART_SendString(DEBUG_USARTx, "\n\n");
			if(strcmp((char *)USART_RX_BUF,"Stop,stm32!")==0)
			{
    
    
				USART_SendString(DEBUG_USARTx, "stm32已停止发送!");
				break;
			}
			USART_RX_FLAG=0;
			memset(USART_RX_BUF,0,sizeof(USART_RX_BUF));
		}
		else
		{
    
    
			USART_SendString(DEBUG_USARTx, "hello windows!\n");
			delay_ms(800);
		}
	}
}

烧录编译,结果显示;
在这里插入图片描述


四、总结与参考资料

1.总结

对串口通讯有个简单了解,理解相关代码。不懂得需要多查阅资料。

2.参考资料

1.野火F103-MINI视频学习.
2. ST-LINK连接方式.

猜你喜欢

转载自blog.csdn.net/QWERTYzxw/article/details/110726965