基于stm32f103rct6的USART串口通信

基于stm32f103rct6的USART串口通信

硬件说明

芯片stm32f103rct6
使用串口USART1,对应引脚   TX---PA9    RX---PA10

代码部分

需要延时函数便于观察运行情况,因此导入dalay模块

“delay.h”

#ifndef __DELAY_H
#define __DELAY_H

void Delay_us(uint32_t us);
void Delay_ms(uint32_t ms);
void Delay_s(uint32_t s);

#endif

“delay.c”

#include "stm32f10x.h"

/**
  * @brief  微秒级延时
  * @param  xus 延时时长,范围:0~233015
  * @retval 无
  */
void Delay_us(uint32_t xus)
{
	SysTick->LOAD = 72 * xus;				//设置定时器重装值
	SysTick->VAL = 0x00;					//清空当前计数值
	SysTick->CTRL = 0x00000005;				//设置时钟源为HCLK,启动定时器
	while(!(SysTick->CTRL & 0x00010000));	//等待计数到0
	SysTick->CTRL = 0x00000004;				//关闭定时器
}

/**
  * @brief  毫秒级延时
  * @param  xms 延时时长,范围:0~4294967295
  * @retval 无
  */
void Delay_ms(uint32_t xms)
{
	while(xms--)
	{
		Delay_us(1000);
	}
}
 
/**
  * @brief  秒级延时
  * @param  xs 延时时长,范围:0~4294967295
  * @retval 无
  */
void Delay_s(uint32_t xs)
{
	while(xs--)
	{
		Delay_ms(1000);
	}
} 

“Serial.h”

#ifndef __SERIAL_H
#define __SERIAL_H
#include <stdio.h>

void Serial_Init(void);
void Serial_SendByte(u8 Byte);
void Serial_SendArray(u8 *Array,u16 length);
void Serial_SendString(u8 *String);
void Serial_SendNumber(u32 num,u8 length);
void Serial_Printf(char *format,...);

u8 Serial_GetRxData(void);
u8 Serial_GetRxFlag(void);

#endif

“Serial.c”

#include "stm32f10x.h"                  // Device header
#include "Serial.h"
#include <stdio.h>
#include <stdarg.h>

//设置两个变量,方便写get函数给外部访问
u8 Serial_Rx_Data,Serial_Rx_Flag;


void Serial_Init(void)
{
    //打开 USART1 和 GPIOA 时钟; USART1由APB2总线控制; USART2,USART3是由APB1控制
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1,ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
	
    //配置PA9和PA10为 复用推挽输出 和 上拉输入,后者上拉输入保证默认高电平,这样和 USART 起始位低电平相配合
	GPIO_InitTypeDef	GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	//这里直接调用不会对PA9的端口配置产生覆盖,不难发现GPIO的初始化处在局部
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;	
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;	
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
    //使用结构体,配置USART的波特率,流量控制,需要开启的端口,奇偶校验,数据长度
	USART_InitTypeDef USART_InitStruct;
	USART_InitStruct.USART_BaudRate=9600;
	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);
	
	//如果需要接收数据,一般采取中断策略,需要配置中断
	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 启动!!!
	USART_Cmd(USART1,ENABLE);	
}

u8 Serial_GetRxData(void)
{
	return Serial_Rx_Data;
}

u8 Serial_GetRxFlag(void)
{
	if(Serial_Rx_Flag==1){
		Serial_Rx_Flag=0;
		return 1;
	}
	return Serial_Rx_Flag;
}

void Serial_SendByte(u8 Byte)
{
	USART_SendData(USART1, Byte);
	while(USART_GetFlagStatus(USART1, USART_FLAG_TXE)==RESET);
}

void Serial_SendArray(u8 *Array,u16 length)
{
	u16 i=0;
	for(i=0;i<length;i++)
	{
		Serial_SendByte(Array[i]);
	}
}

void Serial_SendString(u8 *String)
{
	u8 i=0;
	for(i=0;String[i]!='\0';i++){
		Serial_SendByte(String[i]);
	}
}	

void Serial_SendNumber(u32 num,u8 length)
{
	u8 str[length+1];
	for(int i=length-1;i>=0;i--){
		str[i]='0'+num%10;
		num/=10;
	}
	Serial_SendString(str);
}

int fputc(int ch,FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}

void Serial_Printf(char *format,...)
{
	char string[100];
	va_list arg;
	va_start(arg,format);
	vsprintf(string,format,arg);
	va_end(arg);
	Serial_SendString(string);
}

void USART1_IRQHandler(void)
{
	if(USART_GetITStatus(USART1, USART_IT_RXNE)==SET){
		Serial_Rx_Data=USART_ReceiveData(USART1);
		Serial_Rx_Flag=1;
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}

“main.c”

#include "stm32f10x.h"                  // Device header
#include "Delay.h"
#include "Serial.h"

int main(void)
{		
	Serial_Init();	
	while(1){
		u8 RxFlag=Serial_GetRxFlag();
		if(RxFlag){
			u8 RxData[1];
            //接受数据
			RxData[0]=Serial_GetRxData();
			//收到并回复给发送方
			Serial_SendString(RxData);
		}
        //等待1s方便观察
		Delay_ms(1000);
	}
}

将printf()函数映射到串口

如果想在开发中直接使用c语言std库中的printf()函数,可以进行如下设置:

首先需要在 Keil5 点击“魔术棒”,勾选 Use MicroLIB 选项。

方法一:

//在 Serial.c 和 Serial.h 的头文件中都加入
#include<stdio.h>

//然后在 Serial.c 文件中添加上
int fputc(int ch,FILE *f)
{
	Serial_SendByte(ch);
	return ch;
}
//这样就可以直接调用printf()

方法二:

//在 Serial.c 和 Serial.h 的头文件中都加入
#include<stdio.h>
int main()
{
	char string[100];
    int num;
    //将格式化后的内容保存到字符串string中
    sprintf(string,"你好,我的编号是 %d \r\d",num);
    //调用封装好的串口发送函数
    Serial.SendString(string);
}

方法三

//在 Serial.c 和 Serial.h 的头文件中都加入
#include<stdio.h>
#include<stdarg.h>

//直接在Serial.c中封装一个函数,用法与printf()一致
void Serial_Printf(char *format,...)
{
	char string[100];
	va_list arg;
	va_start(arg,format);
	vsprintf(string,format,arg);
	va_end(arg);
	Serial_SendString(string);
}


基于状态机模型,接收USART串口数据包

以一种协议为例,要求数据以 Oxa5作为起始字节,下一个字节为 0x01,表示数据长度为1,下一个字节为 数据内容0x01,最后一个字节为校验码,即数据长度与数据内容累加得到 0x02,即所发的数据包为

0xa5,0x01,0x01,0x02

定义两个状态:
状态0------等待起始字节,对每一个收到的字节判断是否为 0xa5,如果是则变为状态1 ;

​状态1-----收到数据并存入GetData[pRx++],然后按照 数据长度 + 1位校验位 往后接收数据,接收完成后则变成状态0;

//定义好缓冲区用于接受数据
u8 GetData[256];
void USART1_IRQHandler(void)
{	
	/* 利用状态机解决接受hex数据包的问题*/
	//static变量,只初始化一次,但是可以反复使用
   	
	static u8 RxState=0;			//状态机状态标志
	static u8 pRx=0;				//类似于缓冲区指针,初始为0
	if(USART_GetITStatus(USART1, USART_IT_RXNE)==SET){
		//先用一个变量接收数据
		u8 Serial_Rx_Data=USART_ReceiveData(USART1);
		//判断状态机状态,并进行相应操作
		if(RxState==0){
			RxFlag=0;		//用于与外界进行信息交互的变量,按需保留
			if(Serial_Rx_Data==0xa5){
				GetData[pRx]=Serial_Rx_Data;
				pRx++;			//更新指针,提前指向下一个空闲位置
				RxState=1;		//改变状态
			}	
		}else if(X8_RxState==1){
			GetData[pRx]=Serial_Rx_Data;
			pRx++;					//更新指针,提前指向下一个空闲位置
			GetData_length=pRx;		//更新数据规模
			if(pRx==GetData[1]+3){
				RxState=0;	//改变状态
				pRx=0;		//更新指针
				
				//RxFlag=1;	//用于与外界进行信息交互的变量,按需保留
				//this_task=1;	//用于与外界进行信息交互的变量,按需保留
			}
		}
		USART_ClearITPendingBit(USART1, USART_IT_RXNE);
	}
}
基于引用和引用,stm32f103rct6串口例程是基于STM32F103RCT6这款MCU实现串口中断发送的例程。它使用了非阻塞方式发送串口数据,并且使用了环形队列作为串口发送的缓冲区。该例程提供了硬件平台、软件平台、调试工具以及详细的代码文件。硬件平台是基于STM32F103RCT6的开发板,使用8MHz外部晶振和3.3V供电。软件平台是keil uvision5。下载工具是串口线和stc-isp-15xx-v6.87E。调试工具是XCOM V2.0。该例程的详细代码和相关图片可以在引用和引用中找到。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *3* [基于STM32F103RCT6实现串口中断发送,使用环形队列](https://blog.csdn.net/qp_12/article/details/125011018)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* [STM32(六)库函数串口发送与接收实例(中断)](https://blog.csdn.net/weixin_51218153/article/details/121897929)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值