STM32G431RB--基于HAL库(蓝桥杯嵌入式赛前梳理)

前言

明天就进行蓝桥杯的比赛了,最后一天再重新梳理一下各个模块的使用和代码的编写。 如果各个模块的MX配置是根据我之前发的来的,那么这篇文章中的代码完全适用;如不是,原理部分也是相同的,代码部分适用,可以自行判断,作为一个参考。


LED

引脚:

PC8~PC15(LED1 ~ LED8)

1.控制LED灯亮灭时需要更改PD2引脚电平(先高后低)
2.如果不锁存无法保存数据
3.操作LCD时会影响LED
4.锁存:

HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);

5.与LCD发生冲突时使用数组解决

/*引脚相关宏定义->在CubeMX中配置*/
#define LED1_Pin GPIO_PIN_8
#define LED1_GPIO_Port GPIOC
#define LED2_Pin GPIO_PIN_9
#define LED2_GPIO_Port GPIOC
#define LED3_Pin GPIO_PIN_10
#define LED3_GPIO_Port GPIOC
//...此处省略剩余LED引脚宏定义
#define LOCK_Pin GPIO_PIN_2
#define LOCK_GPIO_Port GPIOD

/*LED控制相关宏定义*/
#define LED1(a) HAL_GPIO_WritePin(LED1_GPIO_Port, LED1_Pin, a)
#define LED2(a) HAL_GPIO_WritePin(LED2_GPIO_Port, LED2_Pin, a)
#define LED3(a) HAL_GPIO_WritePin(LED3_GPIO_Port, LED3_Pin, a)
#define LED4(a) HAL_GPIO_WritePin(LED4_GPIO_Port, LED4_Pin, a)
#define LED5(a) HAL_GPIO_WritePin(LED5_GPIO_Port, LED5_Pin, a)
#define LED6(a) HAL_GPIO_WritePin(LED6_GPIO_Port, LED6_Pin, a)
#define LED7(a) HAL_GPIO_WritePin(LED7_GPIO_Port, LED7_Pin, a)
#define LED8(a) HAL_GPIO_WritePin(LED8_GPIO_Port, LED8_Pin, a)
#define LEDALL(a) HAL_GPIO_WritePin(GPIOC, GPIO_PIN_All, a)

#define LOCK_HIGH() HAL_GPIO_WritePin(LOCK_GPIO_Port, LOCK_Pin, GPIO_PIN_SET)
#define LOCK_LOW()  HAL_GPIO_WritePin(LOCK_GPIO_Port, LOCK_Pin, GPIO_PIN_RESET)

/*存储LED状态数组*/
uint8_t LED_Close[9] = {0};

/*LED控制函数*/
void LED_Contorl()
{
	LEDALL(OFF);
	
	for(uint8_t i=1;i<=1;i++)//i<?取决于要使用LED灯的数量
	{
		if(!LED_Close[i])
		{
			switch(i)
			{
				case 1:
					LED1(ON);
					break;
			}
		}
	}
	
	LOCK_HIGH();
	LOCK_LOW();
}

KEY

引脚:

PB0     ------> B0
PB1     ------> B1
PB2     ------> B2
PA0     ------> B3

1.判断按键按下HAL_GPIO_ReadPin();
2.按键消抖5~10ms即可
3.whlie检测按键松开

I2C-EEPROM

1.写入字符串时通常是多个字节,需要多次调用字节写入函数,可以直接将函数写成多字节写入函数

/*EEPROM写数据*/
void EEPROM_WriteBuff(uint8_t addr,uint8_t *sendBuff,uint32_t numByteToWrite)
{
	I2CStart();//开始通信 起始信号
	I2CSendByte(0xa0);//发送从设备地址 EEPROM地址为0XA0 最后一位数据为0方向为写入
	I2CWaitAck();//等待应答
	I2CSendByte(addr);//发送存储地址
	I2CWaitAck();//等待应答

	while(numByteToWrite--)//连续写入
    {
        I2CSendByte(*sendBuff);//发送数据
        I2CWaitAck();//等待应答
        sendBuff++;//指针自增
    }

	I2CStop();//结束通信 发送终止信号
}

/*EEPROM读数据*/
void EEPROM_ReadBuff(uint8_t addr,uint8_t *readBuff,uint32_t numByteToRead)
{
	I2CStart();//开始通信 起始信号
	I2CSendByte(0xa0);//发送从设备地址 EEPROM地址为0XA0 最后一位数据为0方向为写入
	I2CWaitAck();//等待应答
	I2CSendByte(addr);//发送存储地址
	I2CWaitAck();//等待应答
	
    I2CStart();//再次发送起始信号
    I2CSendByte(0xa1);//发送从设备地址 EEPROM地址为0XA0 最后一位数据为1方向为读取
    I2CWaitAck();//等待应答

    while(numByteToRead--)//连续读取
    {
        *readBuff=I2CReceiveByte();//读取一字节数据
        if(numByteToRead==0)//如果读取结束
        {
            I2CSendNotAck();//主机发送非应答
        }
        else//未结束
        {
            readBuff++;//指针自增
            I2CSendAck();//主机发送应答信号 继续接收下一字节数据
        }
    }
    I2CStop();//结束通信 发送终止信号
}

​2.连续读写加延时等待,单片机工作频率远大于EEPROM工作频率,需等待数据完整保存或读出
3.EEPROM得存储的地址不能过大(从0开始用起就好了)
4.EEPROM设备地址为0XA0(写方向) 0XA1(读方向)
5.每写入一个字节数据都需要调用I2CWaitAck();//等待应答
6.读数据时要先以写方式确定数据地址,再重新发送起始信号改为读方式

I2C-可编程电阻

1.设备地址为0X5E(写方向) 0X5F(读方向)
2.不需要确定数据存储地址
3.读数据时无需先以写方式和设备通讯
4.代码:

/*可编程电阻*/
void write_resistor(uint8_t value)
{   
	I2CStart();//起始信号
	I2CSendByte(0x5E);//查找设备,写方式
	I2CWaitAck();//等待应答
	
	I2CSendByte(value);//发送数据,更改阻值 
	I2CWaitAck();//等待应答
	I2CStop();//终止信号
}

uint8_t read_resistor(void)
{   
	uint8_t value; 
	I2CStart();//起始信号
	I2CSendByte(0x5F);//查找设备,读方式  
	I2CWaitAck();//等待应答
	
	value = I2CReceiveByte();//读取数据
	I2CSendNotAck();//非应答,结束读取
	I2CStop();//终止信号

	return value;
}

5.若向可编程电阻写入数值x,则该电阻阻值为 x*0.78740kΩ
6.MCP4017T-104ELT是一种4通道数字可调电阻,其可调阻值范围为0Ω到100kΩ,一共有0~127个档位,100kΩ/127≈0.7874kΩ
7.写入后可以通过测量PB14电压检来判断阻值是否发生变化,
PB14电压=3.3V×可编程电阻值(kΩ) /(10kΩ+可编程电阻值(kΩ))
可编程编组寄存器写入10时,PB14的电压应为1.4273V左右

LCD

​引脚:

PC8~PC15

1.使用前初始化LCD_Init();
2.每行有20单位长
3.有0~9共10行
4.每个字符高度为24,宽度为16
5.函数:

显示一行	  
LCD_DisplayStringLine(Line0,(unsigned char *)"                    ");

显示单个字符
LCD_DisplayChar(u8 Line, u16 Column, u8 Ascii);  (第一个参数为纵坐标,字符高度为24;第二个参数为横	坐标,字符宽度为16;屏幕最右边横坐标为0,最坐边为19*16

RTC

秒中断(每秒刷一次LCD)

1.配置:

A.使能时钟源
B.使能日历
C.开启闹钟
D.开启中断
E.配置初始化时间
F.配置闹钟参数

2.结构体:

时间结构体		RTC_TimeTypeDef
日期结构体		RTC_DateTypeDef
闹钟结构体		RTC_AlarmTypeDef

3.函数:

开启闹钟中断
HAL_RTC_SetAlarm_IT(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format);

回调函数	  
HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc);

读取时间	  
HAL_StatusTypeDef HAL_RTC_GetTime(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format);

读取日期	 
HAL_StatusTypeDef HAL_RTC_GetDate(RTC_HandleTypeDef *hrtc, RTC_TimeTypeDef *sTime, uint32_t Format);

设置时间	 
HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD);

设置闹钟	  
HAL_RTC_SetAlarm_IT(&hrtc,&sAlarm, RTC_FORMAT_BIN);

4.注意:
读完时间后要读日期时钟才能继续走

5.秒中断代码:

RTC_AlarmTypeDef sAlarm = {0};//将生成代码中的这一结构体定义为全局变量,并将生成的这一行代码删除
RTC_TimeTypeDef	Now_Time;//时间结构体
RTC_DateTypeDef	Now_Date;//日期结构体

/*获取当前时间*/
void Get_Time()
{
	HAL_RTC_GetTime(&hrtc,&Now_Time,RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc,&Now_Date,RTC_FORMAT_BIN);
}

/*显示当前时间*/
void Show_Now_Time()
{
	char str[30];
	sprintf(str,"    T:%02d-%02d-%02d         ",Now_Time.Hours,Now_Time.Minutes,Now_Time.Seconds);
	LCD_DisplayStringLine(Line6,(unsigned char*)str);
}

/*设置下一秒的闹钟*/
void Set_Alarm() 
{
	sAlarm.AlarmTime.Seconds = Now_Time.Seconds+1;
	if(sAlarm.AlarmTime.Seconds==60)sAlarm.AlarmTime.Seconds=0;
	HAL_RTC_SetAlarm_IT(&hrtc,&sAlarm, RTC_FORMAT_BIN);
}

/*RTC闹钟中断 时间显示(每秒) */
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
	Get_Time();//获取发生中断的时间
	Set_Alarm();//设定下一秒的闹钟
	Show_Now_Time();//显示当前时间
}

正计时时钟(一直刷LCD)

1.配置:

A.使能时钟源
B.使能日历

2.结构体:

时间结构体		RTC_TimeTypeDef
日期结构体		RTC_DateTypeDef

3.代码(在主循环中不断调用下面两个函数即可实现时钟正计时):

RTC_TimeTypeDef	Now_Time;//时间结构体
RTC_DateTypeDef	Now_Date;//日期结构体

/*读取当前时间*/
void Get_Time()
{
	HAL_RTC_GetTime(&hrtc,&Time_Now,RTC_FORMAT_BIN);
	HAL_RTC_GetDate(&hrtc,&Date_Now,RTC_FORMAT_BIN);
}

/*显示当前时间*/
void Show_Now_Time()
{
	char str[30];
	sprintf(str,"    T:%02d-%02d-%02d         ",Now_Time.Hours,Now_Time.Minutes,Now_Time.Seconds);
	LCD_DisplayStringLine(Line6,(unsigned char*)str);
}

ADC

引脚:

PB1    ------> ADC1_IN12
PB12   ------> ADC1_IN11
PB15   ------> ADC2_IN15

1.12位分辨率对应转换值上限为4096
2.不需要开启中断
3.配置

A.使用MX初始化时直接默认配置即可

4.函数:

开启ADC		   HAL_ADC_Start(ADC_HandleTypeDef *hadc);
读取ADC的转换值	HAL_ADC_GetValue(ADC_HandleTypeDef *hadc)(每次getvalue之前都需要先Start);

​ 5.代码:

double ADC_GetValue()
{
	uint32_t count;//保存计数值
	HAL_ADC_Start(&hadc2);//每次GetValue前都需要重新Start
	count=HAL_ADC_GetValue(&hadc2);
	return count*3.3/4096;
}	

DAC

引脚:

PA4     ------> DAC1_OUT1
PA5     ------> DAC1_OUT2

1.DA转换值上限为4096
2.配置

A.OUT1 mode 选择 Connected to external pin only

3.函数:

开启DAC		    HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);
设置DAC的转换值  HAL_DAC_SetValue(&hdac1,DAC_CHANNEL_1,DAC_ALIGN_12B_R,temp);

​4.代码:

void Dac1_Set_Vol(float vol)
{
	uint16_t temp;
	temp = (4096*vol/3.24);//将电压转换为0~4096的值
	HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1,DAC_ALIGN_12B_R,temp);//设置DAC的值
}

​4.调用代码:

void main()
{
	HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);//仅需调用一次
	Dac1_Set_Vol(2.55);//后续直接调用该函数即可,参数为输出电压值
}

USART(用mx配置时要修改引脚和GPIO时钟)!

改代码:
需要更改HAL_UART_MspInit() 和 HAL_UART_MspDeInit();
改CubeMX配置:
建议在CubeMX中重新指定引脚,在CubeMX中先选择PA9、PA10引脚功能为UART再去配置串口即可。

引脚:

PA9    ------> USART1_TX
PA10   ------> USART1_RX 

1.参数配置:
Mode 异步通信
PIN GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10;(MX内配置有误)
2.函数:

发送	 
HAL_UART_Transmit(&huart1,(unsigned char *)str,strlen(str),100); 注意长度

printf重定向
int fputc(int ch,FILE *f) 
{
	HAL_UART_Transmit(&husart1,(uint8_t *)&ch,1,100); 
	return ch;
 }
 
接收中断(每接收size字节数据中断一次)
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)

接收中断回调函数(每次中断后需要重新开启接收中断)
HAL_UART_RxCpltCallback( UART_HandleTypeDef *huart ) 

TIM

1.基本定时器:(TIM6 TIM7)
A.参数配置:

Prescaler(PSC)  			预分频值,决定定时器工作频率
Counter Mode  				计数模式
Counter Period				重装载值
Trigger Event Selection		触发事件

B.函数:

回调函数			 
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

以中断方式开启定时器	 
HAL_TIM_Base_Start_IT(&htim6);

关闭定时器			
HAL_TIM_Base_Stop_IT(&htim6);

2.高级定时器:(输入频率测量)
A.参数配置:

模式选择输入捕获
使能定时器中断

B.函数:

初始化函数启动输入捕获中断模式
获取定时器计数值				
__HAL_TIM_GET_COUNTER(&htimx);//读TIMX->CNT也可以

设置定时器计数值				
__HAL_TIM_SetCounter(&htimx,0);//清零 TIM->CNT=0也可以

输入捕获回调函数				
HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);//测量频率

在中断模式下启动TIM输入捕获测量	
HAL_TIM_IC_Start_IT(&htimx, TIM_CHANNEL_x);//使用此函数时必须在主函数先调用一次

C.例:(TIM测量输入频率)
定时器输入捕获方式设置为上升沿触发,中断后在回调函数中获取当前计数值,用分频后的时钟频率/计数值即可得出输入频率,需要计数器清零并且开启一次中断为下次测量做准备。
D.测输入频率占空比:需要将输入捕获中的捕获方式更改为双沿捕获,即上升沿和下降沿时都发生中断,再用高电平的计数值除以一个周期的计数值即可得出占空比。

/*得到一个周期和一个周期内高电平对应的计数值*/
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim == &htim2)
	{
		if(HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_15) == GPIO_PIN_SET)//上升沿
		{
			Tim2_cycleCount = TIM2->CNT;//一个周期对应的计数值
			TIM2->CNT = 0;
		}
		else//下降沿
		{
			Tim2_highCount = TIM2->CNT;//高电平对应的计数值
		}
	}
	else if(htim == &htim3)
	{
		if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_4) == GPIO_PIN_SET)//上升沿
		{
			Tim3_cycleCount = TIM3->CNT;//一个周期对应的计数值
			TIM3->CNT = 0;//计数值清零
		}
		else//下降沿
		{
			Tim3_highCount = TIM3->CNT;//高电平对应的计数值
		}
	}
}

/*用计数值计算输入信号频率以及占空比并显示*/
sprintf(buf,"PA15:%05dHz %02d%%",1000000/Tim2_cycleCount,Tim2_highCount*100/Tim2_cycleCount);
LCD_DisplayStringLine(Line4,(unsigned char *)buf);
sprintf(buf,"PB4 :%05dHz %02d%%",1000000/Tim3_cycleCount,Tim3_highCount*100/Tim3_cycleCount);
LCD_DisplayStringLine(Line5,(unsigned char *)buf);

PWM

1.配置:

A.选择引脚功能为TIMx_CHx
B.将定时器对应通道功能选择为PWM输出
C.Prescaler为分频系数。将系统时钟进行分频
D.Counter为溢出值
E.Pulse是一个阈值,区分Pulse/Counter之前与之后的电平
F.配置的参数都减一

2.函数:

回调函数  
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);

开启PWM	
HAL_TIM_PWM_Start(&htimx, TIM_CHANNEL_x);

停止PWM	
HAL_TIM_PWM_Stop(&htimx, TIM_CHANNEL_x);

配置占空比 
__HAL_TIM_SET_COMPARE;

3.例:
输出一个频率为1KHz,占空比为80%的PWM
输出PWM频率=系统时钟频率/Prescaler/Counter,假设系统时钟频率为80MHz,则将Prescaler配置为80,Counter配置为1000,可得频率为1KHz。
占空比:高电平时间/一个周期时间,1KHz对应一个周期时间为1ms,可将Pulse设置为800,在0 ~ 800计数值时输出高电平,时间为8/10个周期即0.8ms,801~1000时输出低电平,可得占空比为80%

4.用PWM输出引脚输出持续的高电平或低电平
​持续的高电平,CCR>ARR
持续的低电平,CCR=0

总结

以上就是全部内容,如有错误请批评指正。
做好赛前梳理,争取在比赛中发挥正常水平,愿各位明天旗开得胜~

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AゞOctopus๊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值