蓝桥杯电子类嵌入式(STM32G431)备赛学习记录(六)——I2C、串口通信

备赛之前,区区有学过I2C和串口相关的教程,这里不对原理展开具体的讲解,可以参考火哥的教程

I2C

将上一个博客的例程复制命名为Ag_05,比赛的时候给参赛选手的数据包里会有I2C的文件,所以我们这里直接将底层驱动代码参考文件夹中的i2c_hal.c和i2c_hal.h两个文件复制到bsp文件夹中。
打开.ioc文件,根据数据手册配置PB6和PB7为Output。点击生成代码,打开工程文件。
在这里插入图片描述
在这里插入图片描述
把i2c_hal.c添加到工程。可以看到已经封装好了很多i2c的函数,我们只需要去调用完成相应功能就行。这里我们假定要将之前例程中得到的频率值存储到EEPROM中。在i2c_hal.c中加入:

void EEP_Write(uchar addr,ucahr dat)			//写函数
{
	I2CStart();									//调用上面封装好的函数开始I2C通讯
	I2CSendByte(0xa0);					//根据芯片手册发送指令,接下来要写操作
	I2CWaitAck();							//发送了数据,要等待
	I2CSendByte(addr);				//发送要写入的地址
	I2CWaitAck();							//等待
	I2CSendByte(dat);					//发送要写入的数据
	I2CWaitAck();							//等待
	I2CStop();							//结束I2c通讯
}

uchar EEP_Read(uchar addr)					//读函数
{
	uchar dat;								//定义一个变量来储存读取的数据
	I2CStart();								//开始通讯
	I2CSendByte(0xa0);				//根据芯片手册发送写入指令,因为要先发送地址数据写入到EEPROM
	I2CWaitAck();							//等待
	I2CSendByte(addr);				//发送要读取的地址
	I2CWaitAck();
	I2CStop();								
	
	I2CStart();
	I2CSendByte(0xa1);				//发送读取指令
	I2CWaitAck();							//等待
	dat = I2CReceiveByte();		//将对应地址的数据接收到这个变量
	I2CWaitAck();							//等待
	I2CStop();	
	return dat;
}

注意,写命令和读命令是根据芯片手册得到的。
在这里插入图片描述
现在,我们有了写数据和读数据两个函数,将之在.h文件中声明后,在main.c中实现相关功能即可。例如,我们按下B3按键,将频率1存入EEPROM。在按键函数key_pro(void)中加入

if(key_state[2].short_flag==1)		//判断如果为按下B3
	{
		uchar frq_h;						//因为frq_1是16位数据类型,而定义的写,读函数的形参都是8位
		uchar frq_l;						//所以要用两个变量分别去存高8位和低8位数据
		frq_h = frq_1 >> 8;
		frq_l = frq_1 & 0xff;
		EEP_Write(1,frq_h);			//在地址为1的空间写入高八位数据
		I2CWaitAck();						//等待
		EEP_Write(2,frq_l);			//在地址为2的空间写入低八位
		key_state[1].short_flag=0;
	}

在显示函数dis_pro()中加入:

uint eep_dat = (EEP_Read(1)<<8)+EEP_Read(2);  //定义一个变量来存读取的数据
sprintf(dis_2,"     EEP_I2C:%d",eep_dat);
LCD_DisplayStringLine(Line3,(unsigned char *)dis_2);

编译无误,下载完成

串口通信

根据题目要求,我们需要开发板通过串口通信接受一个字符串,按要求处理数据并打印到LCD屏上,如果接受出错则打印Error。
在cube中打开并配置USART1相关参数。将模式设置为异步通信,波特率设置为9600(根据本例程参考的题目所给要求),使能中断然后生成代码。
在这里插入图片描述
我们这里还是用中断的方式,先在interrupt.c文件中编写回调函数,当通过串口接受到数据,先将数据存储到预先定义好的数组中以待后面显示。

char Re_Dat[30];		//用来存储接受到的字符串
uint8_t rxdat;			//用来存当前接受到的单个的字符
uint8_t re_pointer;				//用来指向数组存储位置的光标
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)		//串口接受中断回调函数
{
	Re_Dat[re_pointer++] = rxdat;						//将缓存的数据放入数组,光标往后移动一位
	HAL_UART_Receive_IT(&huart1,&rxdat,1);				//开启中断,为接受下一个数据作准备。rxdat缓存接收到的数据,1为接受数据进入中断的阈值。
}

然后,将上面定义的变量和数组在main.c中作外部调用。首先用HAL_UART_Receive_IT(&huart1,&rxdat,1);打开串口接受中断定义一个处理接收到的数据的函数,这里按照题目要求来编写,如要将22位的字符串分为4、4、12的格式在LCD中显示。

char car_type[5];				//将22位字符串分为三个部分分别存储
char car_time[13];
char car_dat[5];

void Re_Usart_Pro()					//定义处理接收到的数据的函数
{
	if(re_pointer>0)
	{
		if(re_pointer==22)
		{
			sscanf(Re_Dat,"%4s:%4s:%12s",car_type,car_dat,car_time);		//将接受到的数据按照既定格式分别放入三个数组中
		}
		else
		{
			char temp[20];
			sprintf(temp,"Error!");				//如果接受的数据不够则打印Error
			HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);		//库函数发送,最后一个是发送的最大时间,如果超过就退出发送,注意要设置大一些
		}
		re_pointer=0;							//将光标返回置开头
		memset(Re_Dat,0,30);			//将接受数据的数组全部置零
	}
}

接下来只需要在显示函数dis_pro中将接受的数据进行显示即可,这里可以设置一个变量view来表示多个界面,将之前所有例程的显示用0来表示,这里的串口通信数据用1来表示。
dis_pro:

void dis_pro(void)
{

	if(view == 0)
	{		
		char dis[20];    //定义一个内存为20的数组
		char dis_1[20];
		char dis_2[20];
		sprintf(dis,"     PWM:");     //将字符串传到指定数组中
		LCD_DisplayStringLine(Line1,(unsigned char *)dis); 	//在第一行显示
	
		sprintf(dis_1,"     PWM:%d",frq_1);
		LCD_DisplayStringLine(Line5,(unsigned char *)dis_1);
	
		sprintf(dis_2,"     PWM:%d",frq_2);
		LCD_DisplayStringLine(Line6,(unsigned char *)dis_2);
	
	
		sprintf(dis_2,"     V:%.2f",Get_Adc(&hadc1));
		LCD_DisplayStringLine(Line8,(unsigned char *)dis_2);
		sprintf(dis_2,"     V:%.2f",Get_Adc(&hadc2));
		LCD_DisplayStringLine(Line9,(unsigned char *)dis_2);
	
		uint eep_dat = (EEP_Read(3)<<8)+EEP_Read(4);  //定义一个变量来存读取的数据
		sprintf(dis_2," EEP_I2C:%d",eep_dat);
		LCD_DisplayStringLine(Line3,(unsigned char *)dis_2);
	}
	//上面的代码在之前例程有,这里只是连贯起来
	if(view == 1)
	{
		char arr[30];
		sprintf(arr,"  UART:");
		LCD_DisplayStringLine(Line1,(unsigned char *)arr);
		sprintf(arr,"car_type:%s",car_type);
		LCD_DisplayStringLine(Line4,(unsigned char *)arr);
		sprintf(arr,"car_dat:%s",car_dat);
		LCD_DisplayStringLine(Line5,(unsigned char *)arr);
		sprintf(arr,"car_time:%s",car_time);
		LCD_DisplayStringLine(Line6,(unsigned char *)arr);
	}
}

在按键函数key_pro中,将按键B4设置为界面切换按钮。在函数中加入:

		if(key_state[3].short_flag == 1)
		{
			view++;
			if(view == 2)
				view = 0;
			LCD_Clear(Black);					//清屏
			key_state[3].short_flag = 0;
		}

Re_Usart_Pro()函数放入主函数while中,编译无误,下载完成。

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值