【各种中断】STM32cubeMX HAL库综合复习

小蜜蜂老师32cubemx学习笔记,对一些实验过程中可能出现的情况做出解释,对老师做补充,重复性工作ctrl+c/v使用,前期略显粗糙,后续随时更新,有错误感谢指正,根据自己板子的情况对视频中的程序做了调整,比如实在懒得加第二个LED灯~还有手头没有光敏,但有一个土壤湿度~很多地方写了注释,说不定你不明白的地方就写在注释里面~


在这里插入图片描述

这个是参考视频的链接,感谢这位UP我才能学的这么快😄点击即可:小蜜蜂老师B站课程链接
在这里插入图片描述
编译一下就有那个打开折叠的加号了~

有IT就对应中断,就要设置NVIC使能,就有回调函数,满足对应条件就会触发回调函数,中断的思维很重要~
要注意你的板载LED灯是不是低电平才亮,接的单片机的哪个引脚捏~
不得不说这软件真的好好用啊~
回调函数里不要用HAL_Delay()

一、按键

如果按键接地的话把设置为输入的脚上拉,否则会一直判断成低电平
while的效果起到暂停的效果,while()里面的判断式为真,就会一直卡在循环里不执行后面的内容。

/* USER CODE BEGIN 0 */
#define key HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_4)

void delay(unsigned int t)
{
	while(t--);
}
void button_scan()
{
	if(key==0)
	{
		delay(1000);
		if(key==0)
		{
			while(key==0);
			HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);//调换while的位置,实现是按钮按下瞬间执行翻转,还是按下后再抬起的瞬间执行反转电平
		}
	}
}
//这一段发现灯高频闪烁,是因为PB4是悬空状态(没上下拉),PB4应该是低电平,将PB4上拉即可稳定下来
/* USER CODE END 0 */

二、外部中断

在cubemx里面设置好是上升沿下降沿还是上升下降都行,如上升沿,电平上升就会进入回调函数,如下:

/* USER CODE BEGIN 0 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
	if (GPIO_Pin==GPIO_PIN_10)
	{
	HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
	}
}
/* USER CODE END 0 */

三、定时器中断

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{

	if(htim->Instance==TIM2)
	{
	
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
		
	}
	if(htim->Instance==TIM3)
	{
	
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
		
	}

}
//定时器中断回调函数与中断判断,对应定时器中断会触发电平反转

初始化部分:

HAL_TIM_Base_Start_IT(&htim2);//定时器启动函数

原型:

HAL_StatusTypeDef HAL_TIM_Base_Start_IT(TIM_HandleTypeDef *htim);

四、PWM

PWM参考视频
通过预分频器调整计数频率为1MHz,每秒(1000ms)计数1000000个(每毫秒1000个数),计数从零达到重装载值,计数清空为一个周期,对舵机而言要调整为一个周期为20ms,也就是计到20000个数清空计数到0,重装载值是20000(填到cubemx里是20000-1),比较值根据你要转动的角度计算占空比,根据占空比计算比较值,如舵机转0°需要20ms周期,0.5ms脉冲,占空比就是0.5/20=0.025,重装载值-重装载值×占空比=比较值

项目数值
舵机转动角度
占空比0.025
重装载值20000
比较值19500

前提是你的预分频器设置的保证计数频率是1MHz,根据你设置的时钟来定。

  /* USER CODE BEGIN 2 */
	HAL_TIM_PWM_Start(&htim1,TIM_CHANNEL_4);
  /* USER CODE END 2 */
  while (1)
  {
		for(uint16_t cnt=0;cnt<1000;cnt++)
		{
		__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_4,cnt);
			HAL_Delay(1);
		}
		for(uint16_t cnt=1000;cnt>0;cnt--)
		{
		__HAL_TIM_SetCompare(&htim1,TIM_CHANNEL_4,cnt);
			HAL_Delay(1);
		}	
    /* USER CODE END WHILE */

五、UART

有的串口助手没有显示文本功能,所以没办法显示中文(中文是乱码,改成同义的英文即可)。
定义与宏:

uint8_t Tx_str1[]="Hello,world!\r\n";
uint8_t Tx_str2[]="LED1_Open!\r\n";
uint8_t Tx_str3[]="LED1_Close!\r\n";
//字符串声明

#define LED1_ON() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,1);//宏定义的方法,不仅可以定义返回值,还可以定义函数

串口接收中断回调函数:

//回调函数的用法

void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

	if(huart->Instance==USART2)//传递过来的实例是usart2的话
	{
	
		if(Rx_dat==0xa1)
		{
			LED1_ON();
			
			LED2_OFF();
			
			HAL_UART_Transmit(&huart2,Tx_str1,sizeof(Tx_str1),10000);
		
			LED2_OFF();
			
			HAL_UART_Receive_IT(&huart2,&Rx_dat,1);//记得重新打开,写到内层的if里就行,继续接收中断请求,写到while(1)里面也可以
		}	
			
		else if(Rx_dat==0xa2)
		{
			LED1_OFF();
			
			LED2_ON();
			
			HAL_UART_Transmit(&huart2,Tx_str1,sizeof(Tx_str1),10000);
		
			LED2_OFF();
			
			HAL_UART_Receive_IT(&huart2,&Rx_dat,1);
		}
	}
}

初始化部分:

HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);

HAL_UART_Transmit(&huart2,Tx_str1,sizeof(Tx_str1),10000);          //非阻塞式输出,启动提示,最后一位是超时重置

HAL_UART_Receive_IT(&huart2,&Rx_dat,1);      //初始化的时候需要写一下,开启接收

//接收和传输

六、定时器串口混合应用

在cubemx里面把预分频器与主计数器设置好数值~
手动引入新的头文件、定义与宏,位置:BEGIN0:

#include "stdio.h"//手动加入在USER CODE BEGIN Includes 中
#define LED_on() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET)//这个位置不带分号,后边调用该宏就要加上分号,如果这个位置加分号,后面调用时再加分号相当于有两个分号
#define LED_off() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET)
#define LED_invert() HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13)

uint8_t str1[]="==============Hello!================\r\n";
uint8_t hh=0,mm=0,ss=0,ss05=0;
uint8_t str_buff[64];//64个字节
uint8_t Rx_dat[16];//16个字节
		
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim->Instance==TIM2)
	{
	
		HAL_GPIO_TogglePin(GPIOC,GPIO_PIN_13);
		
		ss05++;
		if(ss05==2)
		{
		
			ss05=0;
			ss++;
			if(ss==60)
			{
				ss=0;
				mm++;
				if(mm==60)
				{
					mm=0;
					hh++;
				}
			}
		}
	}
}
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{

	if(huart->Instance==USART2)//传递过来的实例是usart2的话
	{
		if(Rx_dat[0]==0xBF&&Rx_dat[2]==0xFB)//帧头和帧尾
		{
			switch(Rx_dat[1])
			{
				case 0xa1:
				sprintf((char *)str_buff,"%d:%d:%d   LED open!\r\n",hh,mm,ss);//标准化输出
				break;
				
				case 0xa2:
				sprintf((char *)str_buff,"%d:%d:%d   LED close!\r\n",hh,mm,ss);
				break;

				default://都不是的话
				sprintf((char *)str_buff,"%d:%d:%d   This is an error!\r\n",hh,mm,ss);
				break;
			}
			HAL_UART_Transmit(&huart2,str_buff,sizeof(str_buff),10000);
			HAL_UART_Receive_IT(&huart2,Rx_dat,3); 
		}	
	}
}

if(ss05==2),当时设置的是500ms中断一次,所以中断两次秒才能加一
初始化,位置:BEGIN2:

	HAL_UART_Transmit(&huart2,str1,sizeof(str1),10000);
	HAL_TIM_Base_Start_IT(&htim2);//定时器启动函数
	HAL_UART_Receive_IT(&huart2,Rx_dat,3);  
	
	//HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET);
	
	//HAL_UART_Transmit(&huart2,Tx_str1,sizeof(Tx_str1),10000);
	
	//HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET);
		
	//HAL_UART_Receive_IT(&huart2,&Rx_dat,1);

感觉在循环中写电平翻转有点问题??

七、数模转换

若是查询式

#include "stdio.h"
#define LED1_ON() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET)
#define LED1_OFF() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET)
uint8_t str1[]="==============Hello!================\r\n";
uint16_t ADC_Val = 0,ADC_Volt=0;

uint8_t str_buff[44];

void UART1_Send_Info()
{
	sprintf((char *)str_buff,"Sample_Val:%d,Volt_Val:%d.%d%dV\r\n",ADC_Val,ADC_Volt/100,(ADC_Volt%100)/10,ADC_Volt%10);
	HAL_UART_Transmit(&huart1,str_buff,sizeof(str_buff),10000);
}//用到哪个函数就要在用到的位置之前就已经写好,或者在之后再写但前面提前做好声明

void ADC0_Get_Val()
{
	HAL_ADC_Start(&hadc1);//用ADC的实例地址启动
	LED1_ON();
	if(HAL_ADC_PollForConversion(&hadc1,10) == HAL_OK)//10是超时时间
	{
		ADC_Val = HAL_ADC_GetValue(&hadc1);
		ADC_Volt = ADC_Val*330/4096;	
	}	
	UART1_Send_Info();
	LED1_OFF();
	HAL_ADC_Stop(&hadc1);
}
————————————————初始化——————————————————————	
	HAL_UART_Transmit(&huart1,str1,sizeof(str1),10000);
————————————————主循环——————————————————————
ADC0_Get_Val();
		HAL_Delay(500);

若是中断式

#include "stdio.h"
#define LED1_ON() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET)
#define LED1_OFF() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET)
uint8_t str1[]="==============Are you ok?================\r\n";
uint16_t ADC_Val = 0,ADC_Volt=0;

uint8_t str_buff[44];

void UART1_Send_Info()
{
	sprintf((char *)str_buff,"Sample_Val:%d,Volt_Val:%d.%d%dV\r\n",ADC_Val,ADC_Volt/100,(ADC_Volt%100)/10,ADC_Volt%10);
	HAL_UART_Transmit(&huart1,str_buff,sizeof(str_buff),10000);
}//用到哪个函数就要在用到的位置之前就已经写好,或者在之后再写但前面提前做好声明

void HAL_ADC_ConvCpltCallback(ADC_HandleTypeDef* hadc)
{
	if(hadc->Instance == ADC1)
	{
		ADC_Val = HAL_ADC_GetValue(&hadc1);
		ADC_Volt = ADC_Val*330/4096;	
		UART1_Send_Info();
		LED1_OFF();
	}
}

————————————————————————————begin2——————————————————————————
	HAL_UART_Transmit(&huart1,str1,sizeof(str1),10000);
————————————————————————————主循环————————————————————————————
	LED1_ON();
	HAL_ADC_Start_IT(&hadc1);
	HAL_Delay(500);

ADC_Volt = ADC_Val*330/4096;
这句话里的4096是212得来的,12位的AD嘛不是,330是3.3V乘100得来的,3.3是参考电压,也是我们插上STlinkV2给板子的输入电压,为什么乘100呢?因为在下面这个句子里,为了让输出的电压单位为V,就不得不用小数去表示电压,为了不让测得的电压显示时损失太多精度,先乘了100后边又除以了一个100,保证了两位小数的电压实际值输出。
sprintf((char *)str_buff,“Sample_Val:%d,Volt_Val:%d.%d%dV\r\n”,ADC_Val,ADC_Volt/100,(ADC_Volt%100)/10,ADC_Volt%10);
好像说的不是很明白。。。。后续再说

八、OLED(SPI)

四脚的是IIC的,七脚的是SPI的,两者差别很大,视频用的后者~
小蜜蜂老师整理的OLED资料与函数讲解
注意:RES脚要接到开发板的RESET脚上(每个板子应该都有的,引出来现成的),这样当你按下32的手动reset按钮时,也会给OLED屏幕复位,这个脚接不好屏幕直接不工作,不是连一个按钮接地这么简单(当时找错误找了好久好久)。

/* USER CODE BEGIN Includes */
#include "XMF_OLED_STM32Cube.h"
/* USER CODE END Includes */

/* USER CODE BEGIN 0 */
extern unsigned char BMP1[];

void OLED_display_pic()
{
	OLED_Clear();
	OLED_DrawBMP(0,0,128,7,BMP1);
}


void OLED_display_info()
{
	OLED_Clear();
	//OLED_ShowString(16,0,(uint8_t *)"Hello,world!");//十六个像素是一行字,8个像素是一页,也就是一行字要两页,老师好像是这个意思
	//OLED_ShowChinese(16,2,(uint8_t *)"Hello,world!");//占掉第34页
	//OLED_ShowString(16,4,(uint8_t *)"Hello,world!");//占掉第56页
	//OLED_ShowString(16,6,(uint8_t *)"2022/01/28");//占掉第78页,一列一共64个像素,一共8页,对应编号0—7
	OLED_ShowString(16,0,(uint8_t *)"Hello,World!");
	OLED_ShowCHinese(10,3,0);
	OLED_ShowCHinese(28,3,1);
	OLED_ShowCHinese(46,3,2);
	OLED_ShowCHinese(64,3,3);
	OLED_ShowCHinese(82,3,4);
	
}

/* USER CODE END 0 */

/* USER CODE BEGIN 2 */
	OLED_Init();
	OLED_display_pic();
	HAL_Delay(500);
	OLED_display_info();
/* USER CODE END 2 */

OLED_Init();别忘了初始化
OLED_ShowCHinese(82,3,4);最后一位4是指汉字库(HZK)里的第四个字~
看好了他拼的有点奇怪,不是Chinese而是CHinese~

九、OLED+AD

/* USER CODE BEGIN 0 */
#include "stdio.h"

#define LED_ON() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_RESET)
#define LED_OFF() HAL_GPIO_WritePin(GPIOC,GPIO_PIN_13,GPIO_PIN_SET)
extern unsigned char BMP1[];
uint16_t ADC_val=0,ADC_volt=0;
uint8_t str_buff[64];
void led_check()
{
	LED_ON();
	HAL_Delay(300);
	LED_OFF();
	HAL_Delay(300);
}


void oled_display_pic()
{
	OLED_Clear();
	OLED_DrawBMP(0,0,128,8,BMP1);
}
void oled_display_info()
{
	OLED_Clear();//不清屏就会和前一个图像同时输出在一个屏幕上
	OLED_ShowString(0,0,(uint8_t *)"Blog of Z_0_0");//强制类型转换?
	OLED_ShowCHinese(0,3,3);
	OLED_ShowCHinese(18,3,4);
	OLED_ShowCHinese(36,3,8);
	OLED_ShowString(54,3,(uint8_t *)":");
	
	OLED_ShowCHinese(0,5,0);
	OLED_ShowCHinese(18,5,1);
	OLED_ShowCHinese(36,5,8);
	OLED_ShowString(54,5,(uint8_t *)":");
	OLED_ShowString(100,5,(uint8_t *)"V");
	
	
}

void oled_display_data()
{
	sprintf((char *)str_buff,"%d",ADC_val);
	OLED_ShowString(64,3,str_buff);
	sprintf((char *)str_buff,"%d.%d%d",ADC_volt/100,(ADC_volt%100)/10,ADC_volt%10);//把文字与变化的数值结合,下一步再输出
	OLED_ShowString(64,5,str_buff);//sprintf可以把数字字母都塞到str_buff里面
	
}

void GET_adc_val()
{
	HAL_ADC_Start(&hadc1);
	if(HAL_ADC_PollForConversion(&hadc1,10)== HAL_OK)
	{
		ADC_val=HAL_ADC_GetValue(&hadc1);
		ADC_volt=ADC_val*330/4096;
		oled_display_data();
	}
	if(ADC_volt<200)
	{
		LED_ON();
	}
	else
	{
		LED_OFF();
	}
}

/* USER CODE END 0 */

  /* USER CODE BEGIN 2 */	
	OLED_Init();
	oled_display_pic();
	oled_display_info();
  /* USER CODE END 2 */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
		
		GET_adc_val();
		HAL_Delay(300);
    /* USER CODE END WHILE */

土壤湿度传感器与沾水的卫生纸配合使用~
在这里插入图片描述
电压值最大是3.3V肯定
在这里插入图片描述
把土壤湿度传感器摁在湿润的卫生纸上~
在这里插入图片描述

使用%d输出时,上一次采样到的模拟值有四位数字这次采样是三位的话,如上一次3978,这次采样得到465,屏幕上会显示4658,这个多出来的8是由于上次3978第四位的8还留在屏幕上没有刷新掉,换成%4d三位数字实际上是465加一个空格,这样把上一次采样第四位8给挤掉了,显示就正常了。

实际上就是显示的问题,数字采样没错误,不用屏幕输出的情况完全不用考虑这个BUG。
测试%d与%4d的区别,%d是按照原型输出,有多少位就有多少位,另外c语言中数据的宽度是从右边开始算起的,比如%4d就表示数据宽度为4,不够的那空格来补,如果超出四位,则应该原型输出。具体测试程序如下,只需粘贴复制到VC6.0上运行就可知道两者的区别。
该段出自:%4d与%d讲解

本文后续再更新~感谢大家支持!

  • 0
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值