注意
- PWM输出和ADC均不需要开全局中断
- 按键定时是有关中断的,应该使用初始化函数
HAL_TIM_Base_Start_IT而不是HAL_TIM_Base_Start - 修改PWM占空比的函数最好直接记住
__HAL_TIM_SetCompare(&htim2,TIM_CHANNEL_2,50); //注意__HAL_TIM_SetCompare与__HAL_TIM_GetCompare的区别 - rx_proc函数最后记得把rx_pointer和rx_data清空,记住memset函数的用法
memset(rx_data,0,strlen(rx_data));1.函数目录
2.1GPIO操作
HAL_GPIO_WritePin//引脚输出高低电平
sscanf(rxdata,"%4s:%4s:%12s",car_type,car_data,car_time);
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);//串口发送函数
1.1软件使用
2.1cubemx配置
3.1第一栏引脚初始化(Pinout&Configuration)
3.2第四栏工具配置
3.3第三栏工程配置Project Manager
第一个选项为加入所有库,第二个选项为仅仅添加必要的库。
第一项为给每一个外设生成一个单独的.c/.h文件,如果不勾选的话生成的所有初始化代码都在main.c文件里
第二项不用管
1.1按键检测与led
2.1LED点亮函数
void LED_Disp(uchar dsLED)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOC,dsLED<<8,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
2.1按键驱动
//按键检测
uint8_t Key_Scan(GPIO_TypeDef* GPIOx,uint16_t GPIO_PIN)
{
/*检测是否有按键按下 */
if(HAL_GPIO_ReadPin(GPIOx,GPIO_PIN) == 0 )
{
HAL_Delay(20);//消抖
while(HAL_GPIO_ReadPin(GPIOx,GPIO_PIN) == 0); /*等待按键释放 */
return 1;
}
else
return 0;
}
2.2按键控制LED灯点亮
创建变量
__IO uint32_t uwTick_KEY_Point = 0;//控制key_check的执行速度
函数部分
void key_proc(void)
{
if(uwTick - uwTick_KEY_Point<100) return;//减速函数
uwTick_KEY_Point = uwTick;
if(Key_Scan(GPIOB,GPIO_PIN_0))//B1
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_8);//翻转电平
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
if(Key_Scan(GPIOB,GPIO_PIN_1))//B2
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_9);//翻转电平
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
if(Key_Scan(GPIOB,GPIO_PIN_2))//B3
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_10);//翻转电平
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
if(Key_Scan(GPIOA,GPIO_PIN_0))//B4
{
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_11);//翻转电平
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
}
1.2LCD程序
找到竞赛包里lcd.h,fonts.h和lcd.c三个文件,直接复制到自己的工程文件夹里,在工程里加入文件
2.1基本函数
下面这段函数在例程里面有的,可以自己加上用
LCD_Init();
LCD_Clear(White);//清屏成白色
LCD_SetBackColor(White);//设置字的底色
LCD_SetTextColor(Blue);//设置字的颜色
LCD_DisplayStringLine(Line4, (unsigned char *)" Hello,world. ");//指定行打印字符串
2.2创建变量
__IO uint32_t uwTick_LCD_Point = 0;//控制LCD_proc的执行速度
unsigned char i=0;
//LCD显示专用变量
unsigned char LCD_Disp_String[22];//用来存储要放的字符串
2.3打印变化量
char text[30];//定义字符数组
uint i=5;
sprintf(text," CNBR:%d ",i);
//用sprintf函数将字符串打印给text
LCD_DisplayStringLine(Line4,(uint8_t *)text);
//显示在屏幕第4行
void LCD_proc(void)
{
if(uwTick - uwTick_LCD_Point<100) return;//减速函数,实现300ms执行一次
uwTick_LCD_Point = uwTick;
if(Key_Scan(GPIOB,GPIO_PIN_0))
i++;
sprintf((char *)LCD_Disp_String," i num: %03d ",(unsigned int)i);
LCD_DisplayStringLine(Line4, LCD_Disp_String);
}
1.3定时器
2.1按键操作
cube配置
四个按键定义为GPIO_Input,==在配置界面将电平拉高==
在cube里面开一个TIM4定时器,在Mode里面吧时钟源改成外部时钟(Internal Clock),再吧分频改成80-1,计时改成10000-1;此时定时器频率为100HZ,
打开全局中断;
在bsp创建一个interrupt.c文件
在stm32g4xx_hal_tim.h文件最下面中找到中断回调函数,直接赋值粘贴
==在interrupt.h文件里==(只有这样才能把变量引入mian.c文件里)定义结构体
struct keys
{
uchar judge_sta;//判断进行到哪一步了
uchar key_sta;//识别到按键喊下标志位
uchar single_flag;//单次按键标志位
uchar long_flag;//长按按键标志位
uint key_time;
};
在interrupt.c文件里结构体数组定义变量key[4]
struct keys key[4]={0,0,0};
在mian.c中引入外部变量key[4]
/* Private variables --------------------*/
/* USER CODE BEGIN PV */
extern struct keys key[4];
/* USER CODE END PV */
3.1按键单次检测和长按检测中断函数
struct keys key[4]={0,0,0};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//中断回调函数
{
if(htim->Instance==TIM3)
{
key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);
key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);
key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);
for(int i=0;i<4;i++)
{
switch (key[i].judge_sta)//一开始judge_sta为0,
{
case 0:
{
if(key[i].key_sta==0)//检测到按键按下,进入状态1
{
key[i].judge_sta=1;
key[i].key_time=0;
}
}
break;
case 1:
{
if(key[i].key_sta==0)//检测到按键没有抖动,single_flag置1
{
key[i].judge_sta=2;
// key[i].single_flag=1;//如果放在这里就是按下检测
}
else
key[i].judge_sta=0;
}
break;
case 2:
{
if(key[i].key_sta==1)//检测到按键松开,回到状态0
{
key[i].judge_sta=0;
if(key[i].key_time<70)
key[i].single_flag=1;//放在这里就是按键抬起检测
}
else
{
key[i].key_time++;
if(key[i].key_time>70)
key[i].long_flag=1;
}
}
break;
}
}
}
}
3.初始化
开启定时器4检测按键,具体函数在HAL库定时器头文件下找到
HAL_TIM_Base_Start_IT(&htim4);
3.2按键检测并显示在屏幕上
while (1)
{
if(key[0].single_flag==1)
{
sprintf(text," key0down ");
LCD_DisplayStringLine(Line5,(uint8_t *)text);
key[0].single_flag=0;
}
else if(key[0].long_flag==1)
{
sprintf(text," long_key0down ");
LCD_DisplayStringLine(Line5,(uint8_t *)text);
key[0].long_flag=0;
}
if(key[1].single_flag==1)
{
sprintf(text," key1down ");
LCD_DisplayStringLine(Line5,(uint8_t *)text);
key[1].single_flag=0;
}
if(key[2].single_flag==1)
{
sprintf(text," key2down ");
LCD_DisplayStringLine(Line5,(uint8_t *)text);
key[2].single_flag=0;
}
if(key[3].single_flag==1)
{
sprintf(text," key3down ");
LCD_DisplayStringLine(Line5,(uint8_t *)text);
key[3].single_flag=0;
}
}
3.3通过按键切换屏幕显示
while (1)
{
key_proc();
disp_proc();
}
/* USER CODE BEGIN 4 */
void key_proc()
{
if(key[0].single_flag==1)
{
view=!view;
LCD_Clear(Black);
key[0].single_flag=0;
}
}
void disp_proc()
{
if(view==0)
{
sprintf(text," Data ");
LCD_DisplayStringLine(Line2,(uint8_t *)text);
sprintf(text," CNBR:2 ");
LCD_DisplayStringLine(Line4,(uint8_t *)text);
sprintf(text," VNBR:4 ");
LCD_DisplayStringLine(Line6,(uint8_t *)text);
sprintf(text," IDLE:2 ");
LCD_DisplayStringLine(Line8,(uint8_t *)text);
}
if(view==1)
{
sprintf(text," Para ");
LCD_DisplayStringLine(Line2,(uint8_t *)text);
sprintf(text," CNBR:3.50 ");
LCD_DisplayStringLine(Line4,(uint8_t *)text);
sprintf(text," VNBR:2.00 ");
LCD_DisplayStringLine(Line6,(uint8_t *)text);
}
}
/* USER CODE END 4 */
2.2生成频率占空比可调的PWM波
生成频率和占空比均可调的脉冲波
[蓝桥杯嵌入式]hal库 stm32 PWM的使用(随时修改占空比,随时修改频率)-CSDN博客
3.1cube配置
把PA6和PA7引脚分别改成TIM16_CH1和TIM17_CH1;
cubemx Timers进行如下配置
分频为4000,定时为100,占空比为20(后期代码可调),总共开了两个PWM波,都是通道一的
3.2初始化
PWM初始化
HAL_TIM_PWM_Start(&htim16,TIM_CHANNEL_1);//PWM初始化
HAL_TIM_PWM_Start(&htim17,TIM_CHANNEL_2);
HAL_TIM_SetCompare(&htim16,TIM_CHANNEL_1,pa6_duty);//设置初始pwm频率
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,pa7_duty);
3.3按键过程函数
实现按下按键PWM波占空比增大,前面定义了两个新变量pa6_duty和pa7_duty,到90后重新变成10
uchar pa6_duty=10;
uchar pa7_duty=10;
void key_proc()
{
if(key[0].single_flag==1)
{
view=!view;
key[0].single_flag=0;
LCD_Clear(Black);
}
if(key[1].single_flag==1)
{
if(pa6_duty>=90) pa6_duty=10;
else
pa6_duty+=10;
__HAL_TIM_SetCompare(&htim16,TIM_CHANNEL_1,pa6_duty);
key[1].single_flag=0;
}
if(key[2].single_flag==1)
{
if(pa7_duty>=90) pa7_duty=10;
else
pa7_duty+=10;
__HAL_TIM_SetCompare(&htim17,TIM_CHANNEL_1,pa7_duty);//直接设置占空比
key[2].single_flag=0;
}
}
3.4显示过程函数
讲pa6_duty和pa7_duty的值显示在屏幕上
char text[30];
void disp_proc()
{
if(view==0)
{
sprintf(text," Data ");
LCD_DisplayStringLine(Line2,(uint8_t *)text);
sprintf(text," CNBR:2 ");
LCD_DisplayStringLine(Line4,(uint8_t *)text);
sprintf(text," VNBR:4 ");
LCD_DisplayStringLine(Line6,(uint8_t *)text);
sprintf(text," IDLE:2 ");
LCD_DisplayStringLine(Line8,(uint8_t *)text);
}
if(view==1)
{
sprintf(text," Para ");
LCD_DisplayStringLine(Line1,(uint8_t *)text);
sprintf(text," PA6:%d ",pa6_duty);
LCD_DisplayStringLine(Line3,(uint8_t *)text);
sprintf(text," PA7:%d ",pa7_duty);
LCD_DisplayStringLine(Line5,(uint8_t *)text);
}
}
2.3占空比和频率捕捉
3.1Cube配置
把引脚PA15和PB4分别定义成TIM2_CH1和TIM3_CH1;
Mode下Channel1改成直接捕获模式Input Capture direct mode,Channe2改成间接捕获模式Input Capture indirect mode
Channel1用来测频率,Channel2用来测占空比
进行如下配置,Channel2用来检测下降沿
3.2初始化
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);//输入捕获初始化
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);
HAL_TIM_IC_Start_IT(&htim3,TIM_CHANNEL_2);
3.3频率与占空比检测函数
写在bsp的interrupt.c文件中
double ccr1_val1a=0,ccr1_val2a=0;//用来读取定时器的数值
uint ccr1_val1b=0,ccr1_val2b=0;
uint frq1=0,frq2=0;//频率
float duty1=0,duty2=0;//占空比
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM2)
{
if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)//中断消息来源 选择直接输入的通道
{
ccr1_val1a=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);//直接
ccr1_val1b=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2);//间接
__HAL_TIM_SetCounter(htim,0);
frq1=(80000000/80)/ccr1_val1a;
duty1=(ccr1_val1b/ccr1_val1a)*100;
HAL_TIM_IC_Start(htim,TIM_CHANNEL_1);
HAL_TIM_IC_Start(htim,TIM_CHANNEL_2);
}
}
if(htim->Instance==TIM3)
{
if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)//中断消息来源 选择直接输入的通道
{
ccr1_val2a=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);
ccr1_val2b=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2);
__HAL_TIM_SetCounter(htim,0);
frq2=(80000000/80)/ccr1_val2a;
duty2=(ccr1_val2b/ccr1_val2a)*100;
HAL_TIM_IC_Start(htim,TIM_CHANNEL_1);
HAL_TIM_IC_Start(htim,TIM_CHANNEL_2);
}
}
}
3.4显示函数
if(view==0)
{
sprintf(text," Data ");
LCD_DisplayStringLine(Line2,(uint8_t *)text);
sprintf(text," FRQ1=%d ",frq1);
LCD_DisplayStringLine(Line4,(uint8_t *)text);
sprintf(text," duty1=%.3f ",duty1);
LCD_DisplayStringLine(Line5, (uint8_t *)text);
sprintf(text," FRQ2=%d ",frq2);
LCD_DisplayStringLine(Line6,(uint8_t *)text);
sprintf(text," duty2=%.3f ",duty2);
LCD_DisplayStringLine(Line7, (uint8_t *)text);
}
1.4ADC
2.1cube配置
在cube中打开对应ADC的引脚PB12和PB15
然后点击Analog,选中对应ADC的通道IN15和IN11
2.2初始化
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED);//ADC校准函数 使用后测量更准确
HAL_ADCEx_Calibration_Start(&hadc2,ADC_SINGLE_ENDED);//本函数视频中未提及
2.3电压读取函数
新建一个bsp_adc.c文件,注意到ADC是一个12位的
#include "bsp_adc.h"
double getADC(ADC_HandleTypeDef *pin)
{
uint adc;
HAL_ADC_Start(pin);//记住,这里不需要地址
adc = HAL_ADC_GetValue(pin);
return adc*3.3/4096;
}
2.4显示函数
直接调用函数在屏幕上显示出来
sprintf(text," V:%.2f ",getADC(&hadc1));
LCD_DisplayStringLine(Line8, (uint8_t *)text);
sprintf(text," V:%.2f ",getADC(&hadc2));
LCD_DisplayStringLine(Line9, (uint8_t *)text);
1.5IIC
eeprom
原理
2.1板子硬件
板子通过IIC连接了两个芯片,本次使用IIC读取EPPROM
2.1Cube配置
本次使用的是软件IIC,故只需将PB6和PB7引脚配置为GPIO_Output
注:截图截错了
2.2比赛代码移植
2.3eeprom读函数
//eeprom_read
uchar eeprom_read(uchar addr)
{
//开启联系芯片
uchar dat;
I2CStart();
I2CSendByte(0xa0);//第一步要先写,告诉eeprom要读取哪个数
I2CWaitAck();//I2C等待确认信号
I2CSendByte(addr);//发送要读取的·地址
I2CWaitAck();
I2CStop();
I2CStart();
I2CSendByte(0xa1);//开始读模式
I2CWaitAck();
dat=I2CReceiveByte();
I2CSendNotAck();//单片机不发送确认信号,防止eeprom继续发送信号
I2CStop();
return dat;
}
2.4eeprom写函数
//eeprom_write
void eeprom_write(uchar addr,uchar dat)
{
//开启联系芯片
I2CStart();
I2CSendByte(0xa0);//2进制表示为1010_0000,=000表示硬件地址,最后一位0表示写入,如果是1表示读取
I2CWaitAck();//I2C等待确认信号
I2CSendByte(addr);//发送存储的地址
I2CWaitAck();
I2CSendByte(dat);//发送数据
I2CWaitAck();
I2CStop();
HAL_Delay(10);//这里一定要有,eeprom写数据需要时间
}
2.5主函数测试
按下按键4将通道1频率值存入eeprom中,然后再用读取函数显示在屏幕上;
/* USER CODE BEGIN 4 */
void key_proc()
{
if(key[3].single_flag==1)
{
uchar frq_h=frq1>>8;//eeprom只能存8位数据,uint是16位数据,故需要取高8位和低8位分别存入
uchar frq_l=frq2&0xff;
eeprom_write(1,frq_h);
HAL_Delay(10);//写入需要时间,不延时可能写不进;
eeprom_write(2,frq_l);
key[3].single_flag=0;
}
}
void disp_proc()
{
if(view==1)
{
uint eep_temp=(eeprom_read(1)<<8)+eeprom_read(2);
sprintf(text," eeprom=%d ",eep_temp);
LCD_DisplayStringLine(Line7, (uint8_t *)text);
}
}
MCP4017可编程电阻
电阻的最大阻值为100K,可调范围为0到127;寄存器每增加一个数,电阻增加787.402欧
原理
这里要记住,它的地址永远是0101111X,0x5f(读模式)或0x5e(写模式)
PB14接入adc检测,配置和Cube一样
代码
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;
}
1.6串口USART
2.1Cube配置
配置PA9和PA10两个引脚
将USART1的Mode改为异步模式
修改波特率为9600
NVIC全局中断勾上
2.2初始化
在变量创建区创建一个串口专用unsigned char变量rxdat
在main函数中初始化
HAL_UART_Receive_IT(&huart1, &rxdat, 1);
第二个变量填入变量rxdat的地址,这样串口接收到的数据会实时赋值给rxdat
2.3串口接收
串口发送函数,在hal库中找
中断回调函数
char rxdata[30];
uint8_t rxdat;
uchar rx_pointer;
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
rxdata[rx_pointer++]=rxdat;
HAL_UART_Receive_IT(&huart1,&rxdat,1);
}
串口接收检测
if(rx_pointer!=0)
{
int temp=rx_pointer;
HAL_Delay(1);
if (temp==rx_pointer)uart_rx_proc();//完成接收
}
串口接收过程函数
void usart_proc()
{
if(strcmp(rxdata,"R37")==0)
{
sprintf(text,"R37:%d,%d,%.1f%%",R37_ALL_count,R37_Pass_count,R37_Hegelv);
HAL_UART_Transmit(&huart1,(u8 *)text,strlen(text),50);
}
if(strcmp(rxdata,"R38")==0)
{
sprintf(text,"R38:%d,%d,%.1f%%",R38_ALL_count,R38_Pass_count,R38_Hegelv);
HAL_UART_Transmit(&huart1,(u8 *)text,strlen(text),50);
}
rx_pointer=0;
memset(rxdata,0,strlen(rxdata));
}























Comments NOTHING