藍橋杯STM32G431RBT6
藍橋杯
注意事項
-
移植lcd之后要記得在main函數內LCD_Init();開啟lcd。
-
如果lcd顯示時led全亮大概率是PD2鎖存器出問題
-
lcd顯示最好都各自帶一個char text[30];
-
按鍵按下表啟動,進入下一步
-
adc一定要在main開啟校準函數HAL_ADCEx_Calibration_Start(&hadc2,ADC_SINGLE_ENDED);記住Calibration和ADC_SINGLE_ENDED
-
adc自寫函數里每一次最好都開啟一遍HAL_ADC_Start(hadc);
-
adc自寫函數里value=HAL_ADC_GetValue(hadc);用uint32_t存儲,之后轉換到float數
-
adc的轉換先乘以3.3再除以4096,順序反了就歸零了
-
串口發送簡單,接收需要中斷函數void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart);
-
把串口接收單數據和數據數組分開寫,用于接收的單數據是uint8_t類型,不是char類型
-
接收的數據數組是char類型
-
單數據接收函數HAL_UART_Receive_IT(&huart1,&data,1);最后接收的長度是1,而且中間的單數據必須取地址,這里接收函數最好用中斷模式
-
要先在主函數寫一個HAL_UART_Receive_IT(&huart1,&data,1);用來開啟
//串口接收函數,沒有寫道串口中斷函數內部,注意
if(接收到的數據>0)
{
if(接收到的數據==正常長度)
{
if(接收到的數據==正常值)
{
操作;
}
else
{
異常標記;
}
}
else
{
異常標記;
}
數據數組清零;
標記長度清零;
}
//主函數串口接收處理
if(接收長度!=0)
{
int 臨時長度=接收長度;
延時1ms;
if(臨時長度==接收長度)
{
串口接收處理函數;
}
}
-
發送數據函數HAL_UART_Transmit(&huart1,(uint8_t *)text1,strlen(text1),50);中間的char類型text要強制轉換(uint8 *),發送函數最好別用中斷模式,第三個長度用strlen,最后一個數記為50
-
memset函數用法memset(datas,0,30);最后一個直接寫數字就好
-
主函數需要開啟pwm定時器HAL_TIM_PWM_Start(&htim17,TIM_CHANEL_1);
-
改變占空比函數__HAL_TIM_SET_COMPARE
-
基礎定時器(定時中斷功能):開啟示例HAL_TIM_Base_Start_IT(&htim3);使用中斷模式
//main.h補全頭文件
#include "stdbool.h"
#include "string.h"
#include "stdio.h"
#include "stdlib.h"
//cubemx生成代碼要注意把部分頭文件從main.c復制到main.h
產品手冊










初始配置





然后創建工程代碼
LED


//led.c
#include "led.h"
void LED_Disp(uchar t)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_ALL,GPIO_PIN_SET);//先把所有的都關上
HAL_GPIO_WritePin(GPIOC,t<<8,GPIO_PIN_RESET);//再把高八位設置
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);//先開啟PD2鎖存器
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);//信息儲存完后再關上PD2鎖存器
}
void LED_Init(void)
{
LED_Disp(0x00);全部關上為初始化
}
//led.h
#ifndef _LED_H_
#define _LED_H_
#include "main.h"
void LED_Disp(uchar t);
void LED_Init(void);
#endif
LED第二種寫法
void Led_Set(unsigned char t)
{
HAL_GPIO_WritePin(GPIOC,GPIO_PIN_All,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOC,t<<8,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
}
key按鍵
選用基本定時器的定時功能來實現延時操作和檢驗按鍵狀態




//interrupt.c
#include "interrupt.h"
struct keys key[4]={0,0,0,0};
void HAL_TIM_PeriodElapsedCallback(TIM HandleTypeDef *htim)//中斷回調函數,中斷都會到這里
{
if(htim->Instance==TIM3)//判斷是TIM3引發的中斷
{
key[0].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);//PB0
key[1].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);//PB1
key[2].key_sta=HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);//PB2
key[3].key_sta=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);//PA0
for(int i=0;i<4;i++)
{
switch(key[i].judge)
{
case 0:
{
if(key[i].key_sta==0)//按鍵按下
{
key[i].time=0;//按鍵按下,計時開始
key[i].judge=1;//進入第二步
}
}break;
case 1:
{
if(key[i].key_sta==0)//延時10ms后仍然是按下狀態,確認按下,進入第三步
{
key[i].judge=2;
}
else //延時10ms后按鍵卻處于沒有按下狀態,消抖,回到第一步
{
key[i].judge=0;
}
}break;
case 2:
{
if(key[i].key_sta==1)//20ms后按鍵已經松開
{
key[i].judge=0;//下次判斷回到第一步
if(key[i].time<70)
{
key[i].flag=1;//按鍵短按過一次的標志位置為1
}
}
else //20ms后按鍵仍然未松開
{
key[i].time+=10;//計時+10ms
if(key[i].time>=70)
{
key[i].longflag=1;//大于70ms,長按標志位置為1(一直按著直接啟動反應)
}
}
}break;
}
}
}
}
//interrupt.h
#ifndef _interrupt_H_
#define _interrupt_H_
#include "main.h"
#include "stdbool.h" //想要使用bool類型必須包含這個頭文件
struct keys{
uchar judge;
bool key_sta;
bool flag;
uint32_t time;
bool longflag;
};
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
#endif
//main.c
#include "interrupt.h"
extern struct keys key[];
int main(void)
{
HAL_TIM_Base_Start_IT(&htim3); //這個必須打開,這個表示打開定時器3
while(1)
{
if(key[0].flag==1)//如果按鍵按下
{
//咋樣咋樣咋樣咋樣……
key[0].flag=0;//標志位用完了置為0
}
}
}
后面修改了下邏輯,松開后再判斷長短,上面的代碼沒改哦
按鍵第二種寫法(滴答定時器)
#define KB1 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define KB2 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define KB3 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define KB4 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)
#define KBGET KB1 | (KB2<<1) | (KB3<<2) | (KB4<<3) | 0XF0
uint16_t short_key=0;
uint16_t long_key=0;
__IO uint32_t KeyTick=0;
void Key_Process(void)
{
if(uwTick-KeyTick<10)return;//10ms
KeyTick=uwTick;
uint16_t temp=(KBGET)^0XFF;
short_key=temp&(temp^long_key);
long_key=temp;
if(short_key&0x01)//B1
{
//Menu_Flag=!Menu_Flag;
//Led_Set(0x01);
}
if(short_key&0x02)//B2
{
//Led_Set(0x02);
//PWM_Set(1000,40);
}
if(short_key&0x04)//B3
{
//Led_Set(0x04);
//PWM_Set(2000,30);
}
if(short_key&0x08)//B4
{
//Led_Set(0x08);
//PWM_Set(3000,20);
}
}
按鍵雙擊

//雙擊
if(短按)
{
標志位++;
if(標志位==1)
{
//開始計時
}
if(標志位==2&&時間<0.5s)
{
雙擊操作;
標志位=0;
}
}
if(時間>=0.5)
{
標志位=0;
}
按鍵長按

LCD

LCD的cubemx配置配置對應IO口為輸出即可,也可以不配置,直接copy數據包代碼


//main.c
int main(void)
{
LCD_Init();
LCD_Clear(Blue);
LCD_SetBackColor(Blue);
LCD_SetTextColor(White);
char text[30];
sprintf(text," frq_eep:%d ",temp);
LCD_DisplayStringLine(Line1, (uint8_t *)text);
while(1)
{
}
}
定時器輸出PWM
共10個定時器分別為
2個基本定時器(TIM6和ITIM7) 。
3個通用定時器(TIM2~TIM4) :全功能通用定時器。
3個通用定時器(TIM15~TIM17):只有1個或者2個通道。
2個高級控制定時器(TIM1和ITIM8) 。
以下是基本定時器的輸出PWM配置?



//main.c
uchar duty=20; //uchar是unsigned char ,main.h里面define一下
int main(void)
{
HAL_TIM_PWM_Start(&htim17,TIM_CHANEL_1); //記住開啟輸出PWM的定時器
while(1)
{
}
}
void key_proc(void)
{
if(key[3].flag==1)//按鍵來回切換PA7輸出PWM
{
duty=((duty==20)?0:20);
__HAL_TIM_SetCompare(&htim17,TIM_CHANEL_1,duty); //記住改占空比
key[3].flag=0;
}
}
輸入捕獲定時器
共10個定時器分別為
2個基本定時器(TIM6和ITIM7) 。
3個通用定時器(TIM2~TIM4) :全功能通用定時器。
3個通用定時器(TIM15~TIM17):只有1個或者2個通道。
2個高級控制定時器(TIM1和ITIM8) 。
兩個555定時器生成占空比為50%的脈沖通過IO口PA15和PB4輸入,電阻R40,R39調節可以改變信號脈沖的頻率。




//interrupt.c
#include "interrupt.h"
double val=0;//直接輸入捕獲定時值
double val2=0;//間接輸入捕獲定時值
float frq=0;//輸入捕獲頻率
float ca_duty=0;//輸入捕獲占空比
void HAL_TIM_IC_CaputureCallback(TIM_HandleTypedef *htim)//輸入捕獲定時器回調函數
{
if(htim->Instance==TIM2)//如果起作用的是TIM2
{
if(htim->Channel==HAL_TIM_ACTIVE_CHANNEL_1)//中斷消息來源,選擇直接輸入的通道
{
val=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_1);//讀取TIM2直接捕獲的定時器計數值
val2=HAL_TIM_ReadCapturedValue(htim,TIM_CHANNEL_2);//讀取TIM2間接捕獲的定時器計數值
__HAL_TIM_SetCounter(htim,0);//讀取完成后將計數器清零
frq=(80000000/80/val);//這里的val相當于是TIM2直接捕獲的重裝載值,計算出頻率
ca_duty=(val2/val)*100;//計算占空比
HAL_TIM_IC_Start(htim,TIM_CHANNEL_1);//重新開啟輸入捕獲定時器的通道一
HAL_TIM_IC_Start(htim,TIM_CHANNEL_2);//重新開啟輸入捕獲定時器的通道二
}
}
}
//interrupt.h
#ifndef _interrupt_H_
#define _interrupt_H_
#include "main.h"
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);//輸入捕獲定時器回調函數
#endif
//main.c
extern float frq;//輸入捕獲頻率
extern float ca_duty;//輸入捕獲占空比
int main(void)
{
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_1);
HAL_TIM_IC_Start_IT(&htim2,TIM_CHANNEL_2);//打開輸入捕獲定時器
while(1)
{
}
}
void show_proc(void)
{
sprintf(text," FRQ:%.2f ",frq);
LCD_DisplayStringLine(Line8, (uint8_t *)text);
sprintf(text," DUTY:%.2f ",ca_duty);
LCD_DisplayStringLine(Line9, (uint8_t *)text);
}
ADC數模轉換
電壓從0v到3.3v的變化


//psbadc.c
#include "bspadc.h"
double GetADC(ADC_HandleTypeDef *pin)
{
uint32_t adc;
HAL_ADC_Start(pin);//開啟這個ADC對應引腳的adc
adc=HAL_ADC_GetValue(pin);//獲取ADC值
return adc*3.3/4096; //讀取到的adc是2的12次方內的量化值,將其變成3.3V內的量化值
}
//bspadc.h
#ifndef _bspadc_H_
#define _bspadc_H_
#include "main.h"
double GetADC(ADC_HandleTypeDef *pin);
#endif
//main.c
int main(void)
{
HAL_ADCEx_Calibration_Start(&hadc1,ADC_SINGLE_ENDED);//ADC校準函數 使用后測量更準確
while(1)
{
}
}
void show_proc(void)
{
sprintf(text," R37:%.2lf ",GetADC(&hadc2));
LCD_DisplayStringLine(Line9, (uint8_t *)text);
}
I2C讀寫eeprom
需要將PB6,PB7設置為輸出模式



0XA0:寫,0XA1:讀(01與MPC相同,看手冊)
I2C讀取寫入eeprom、MPC
uint8_t MPC_read(void)
{
uint8_t temp;
I2CStart();//開啟I2C
I2CSendByte(0X5F);//發送讀
I2CWaitAck();//等待
temp=I2CReceiveByte();//接收數據
I2CSendNotAck();//發送非應答
I2CStop();//關閉I2C
return temp;
}
void MPC_write(uint8_t data)
{
I2CStart();//開啟I2C
I2CSendByte(0X5E);//發送寫
I2CWaitAck();//等待
I2CSendByte(data);//發送數據
I2CWaitAck();//等待
I2CStop();//關閉I2C
}
void eeprom_write(uint8_t add,uint8_t data)
{
I2CStart();//開啟I2C
I2CSendByte(0Xa0);//發送寫
I2CWaitAck();//等待
I2CSendByte(1);//發送add
I2CWaitAck();//等待
I2CSendByte(data);//發送數據
I2CWaitAck();//等待
I2CStop();//關閉I2C
}
uint8_t eeprom_read(uint8_t add)
{
uint8_t temp;
I2CStart();//開啟I2C
I2CSendByte(0Xa0);//發送寫
I2CWaitAck();//等待
I2CSendByte(1);//發送add
I2CWaitAck();//等待
I2CStart();//開啟I2C
I2CSendByte(0Xa1);//發送讀
I2CWaitAck();//等待
temp=I2CReceiveByte();///接收數據
I2CSendNotAck();//發送非應答
I2CStop();//關閉I2C
return temp;
}
在main中的實際操作
//main.c
int main(void)
{
I2CInit();
while(1)
{
}
}
void key_proc(void)
{
if(key[1].key_flag==1&&view==0)//eeprom寫入數據
{
uchar frq_h=frq>>8;//at24c02只能讀取8位,而frq是16位
uchar frq_l=frq&0xff;//把at24c02拆分成上八位和下八位
eeprom_write(1,frq_h);
HAL_Delay(10);//操作之間需要延時
eeprom_write(2,frq_l);
key[1].key_flag=0;
}
}
void show_proc(void)
{
if(view==0)
{
uint16_t temp=(eeprom_read(1)<<8)+eeprom_read(2);//把8位數據恢復成16位
sprintf(text," frq_eep:%d ",temp);
LCD_DisplayStringLine(Line1, (uint8_t *)text);
}
}
USART通信



USART發送
//main.c
int main(void)
{
while(1)
{
char temp[20];
sprintf(temp,"frq=%d\r\n",frq);
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
}
}
UART接收
//main.c
extern char rxdata[30];
extern uint8_t rxdat;
extern uchar rx_pointer;
char car_type[5];
char car_data[5];
char car_time[24];
void uart_rx_proc(void);
int main(void)
{
HAL_UART_Receive_IT(&huart1, &rxdat, 1);//打開uart
while(1)
{
if(rx_pointer!=0)
{
int temp=rx_pointer;
HAL_Delay(1);
if(temp==rx_pointer)
uart_rx_proc();
}
}
}
void shoe_proc(void)
{
//……
else if(view==2)
{
sprintf(text," Car_Messege ");
LCD_DisplayStringLine(Line1, (uint8_t *)text);
sprintf(text,"Car_type=%s ",car_type);
LCD_DisplayStringLine(Line2, (uint8_t *)text);
sprintf(text,"Car_data=%s ",car_data);
LCD_DisplayStringLine(Line4, (uint8_t *)text);
sprintf(text,"t=%s ",car_time);
LCD_DisplayStringLine(Line6, (uint8_t *)text);
sprintf(text," ");
LCD_DisplayStringLine(Line8, (uint8_t *)text);
sprintf(text," ");
LCD_DisplayStringLine(Line9, (uint8_t *)text);
}
}
void uart_rx_proc(void)
{
if(rx_pointer>0)
{
if(rx_pointer==22)
{
sscanf(rxdata,"%4s:%4s:%12s",car_type,car_data,car_time);
}
else
{
char temp[20];
sprintf(temp,"Error\r\n");
HAL_UART_Transmit(&huart1,(uint8_t *)temp,strlen(temp),50);
}
rx_pointer=0;
memset(rxdata,0,30);
}
}
//interrupt.c
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);
}
RTC時鐘

void RTC_Process(void)
{
HAL_RTC_GetTime(&hrtc,&sTime1, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&sDate1, RTC_FORMAT_BIN);
}
void HAL_RTC_AlarmAEventCallback(RTC_HandleTypeDef *hrtc)
{
Menu_Flag=!Menu_Flag;
}
滴答計時器使用方法
//定時50ms發送---代碼格式
if(uwTick-UARTTick<50)return;//50ms
UARTTick=uwTick;

浙公網安備 33010602011771號