前言
明天就進(jìn)行藍(lán)橋杯的比賽了,最后一天再重新梳理一下各個模塊的使用和代碼的編寫。 如果各個模塊的MX配置是根據(jù)我之前發(fā)的來的,那么這篇文章中的代碼完全適用;如不是,原理部分也是相同的,代碼部分適用,可以自行判斷,作為一個參考。
LED
引腳:
PC8~PC15(LED1 ~ LED8)
1.控制LED燈亮滅時需要更改PD2引腳電平(先高后低)
2.如果不鎖存無法保存數(shù)據(jù)
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發(fā)生沖突時使用數(shù)組解決
/*引腳相關(guān)宏定義->在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控制相關(guān)宏定義*/
#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狀態(tài)數(shù)組*/
uint8_t LED_Close[9] = {0};
/*LED控制函數(shù)*/
void LED_Contorl()
{
LEDALL(OFF);
for(uint8_t i=1;i<=1;i++)//i<?取決于要使用LED燈的數(shù)量
{
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.寫入字符串時通常是多個字節(jié),需要多次調(diào)用字節(jié)寫入函數(shù),可以直接將函數(shù)寫成多字節(jié)寫入函數(shù)
/*EEPROM寫數(shù)據(jù)*/
void EEPROM_WriteBuff(uint8_t addr,uint8_t *sendBuff,uint32_t numByteToWrite)
{
I2CStart();//開始通信 起始信號
I2CSendByte(0xa0);//發(fā)送從設(shè)備地址 EEPROM地址為0XA0 最后一位數(shù)據(jù)為0方向?yàn)閷懭?/span>
I2CWaitAck();//等待應(yīng)答
I2CSendByte(addr);//發(fā)送存儲地址
I2CWaitAck();//等待應(yīng)答
while(numByteToWrite--)//連續(xù)寫入
{
I2CSendByte(*sendBuff);//發(fā)送數(shù)據(jù)
I2CWaitAck();//等待應(yīng)答
sendBuff++;//指針自增
}
I2CStop();//結(jié)束通信 發(fā)送終止信號
}
/*EEPROM讀數(shù)據(jù)*/
void EEPROM_ReadBuff(uint8_t addr,uint8_t *readBuff,uint32_t numByteToRead)
{
I2CStart();//開始通信 起始信號
I2CSendByte(0xa0);//發(fā)送從設(shè)備地址 EEPROM地址為0XA0 最后一位數(shù)據(jù)為0方向?yàn)閷懭?/span>
I2CWaitAck();//等待應(yīng)答
I2CSendByte(addr);//發(fā)送存儲地址
I2CWaitAck();//等待應(yīng)答
I2CStart();//再次發(fā)送起始信號
I2CSendByte(0xa1);//發(fā)送從設(shè)備地址 EEPROM地址為0XA0 最后一位數(shù)據(jù)為1方向?yàn)樽x取
I2CWaitAck();//等待應(yīng)答
while(numByteToRead--)//連續(xù)讀取
{
*readBuff=I2CReceiveByte();//讀取一字節(jié)數(shù)據(jù)
if(numByteToRead==0)//如果讀取結(jié)束
{
I2CSendNotAck();//主機(jī)發(fā)送非應(yīng)答
}
else//未結(jié)束
{
readBuff++;//指針自增
I2CSendAck();//主機(jī)發(fā)送應(yīng)答信號 繼續(xù)接收下一字節(jié)數(shù)據(jù)
}
}
I2CStop();//結(jié)束通信 發(fā)送終止信號
}
?2.連續(xù)讀寫加延時等待,單片機(jī)工作頻率遠(yuǎn)大于EEPROM工作頻率,需等待數(shù)據(jù)完整保存或讀出
3.EEPROM得存儲的地址不能過大(從0開始用起就好了)
4.EEPROM設(shè)備地址為0XA0(寫方向) 0XA1(讀方向)
5.每寫入一個字節(jié)數(shù)據(jù)都需要調(diào)用I2CWaitAck();//等待應(yīng)答
6.讀數(shù)據(jù)時要先以寫方式確定數(shù)據(jù)地址,再重新發(fā)送起始信號改為讀方式
I2C-可編程電阻
1.設(shè)備地址為0X5E(寫方向) 0X5F(讀方向)
2.不需要確定數(shù)據(jù)存儲地址
3.讀數(shù)據(jù)時無需先以寫方式和設(shè)備通訊
4.代碼:
/*可編程電阻*/
void write_resistor(uint8_t value)
{
I2CStart();//起始信號
I2CSendByte(0x5E);//查找設(shè)備,寫方式
I2CWaitAck();//等待應(yīng)答
I2CSendByte(value);//發(fā)送數(shù)據(jù),更改阻值
I2CWaitAck();//等待應(yīng)答
I2CStop();//終止信號
}
uint8_t read_resistor(void)
{
uint8_t value;
I2CStart();//起始信號
I2CSendByte(0x5F);//查找設(shè)備,讀方式
I2CWaitAck();//等待應(yīng)答
value = I2CReceiveByte();//讀取數(shù)據(jù)
I2CSendNotAck();//非應(yīng)答,結(jié)束讀取
I2CStop();//終止信號
return value;
}
5.若向可編程電阻寫入數(shù)值x,則該電阻阻值為 x*0.78740kΩ
6.MCP4017T-104ELT是一種4通道數(shù)字可調(diào)電阻,其可調(diào)阻值范圍為0Ω到100kΩ,一共有0~127個檔位,100kΩ/127≈0.7874kΩ
7.寫入后可以通過測量PB14電壓檢來判斷阻值是否發(fā)生變化,
PB14電壓=3.3V×可編程電阻值(kΩ) /(10kΩ+可編程電阻值(kΩ))
可編程編組寄存器寫入10時,PB14的電壓應(yīng)為1.4273V左右
LCD
?引腳:
PC8~PC15
1.使用前初始化LCD_Init();
2.每行有20單位長
3.有0~9共10行
4.每個字符高度為24,寬度為16
5.函數(shù):
顯示一行
LCD_DisplayStringLine(Line0,(unsigned char *)" ");
顯示單個字符
LCD_DisplayChar(u8 Line, u16 Column, u8 Ascii); (第一個參數(shù)為縱坐標(biāo),字符高度為24;第二個參數(shù)為橫 坐標(biāo),字符寬度為16;屏幕最右邊橫坐標(biāo)為0,最坐邊為19*16)
RTC
秒中斷(每秒刷一次LCD)
1.配置:
A.使能時鐘源
B.使能日歷
C.開啟鬧鐘
D.開啟中斷
E.配置初始化時間
F.配置鬧鐘參數(shù)
2.結(jié)構(gòu)體:
時間結(jié)構(gòu)體 RTC_TimeTypeDef
日期結(jié)構(gòu)體 RTC_DateTypeDef
鬧鐘結(jié)構(gòu)體 RTC_AlarmTypeDef
3.函數(shù):
開啟鬧鐘中斷
HAL_RTC_SetAlarm_IT(RTC_HandleTypeDef *hrtc, RTC_AlarmTypeDef *sAlarm, uint32_t Format);
回調(diào)函數(shù)
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);
設(shè)置時間
HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD);
設(shè)置鬧鐘
HAL_RTC_SetAlarm_IT(&hrtc,&sAlarm, RTC_FORMAT_BIN);
4.注意:
讀完時間后要讀日期時鐘才能繼續(xù)走
5.秒中斷代碼:
RTC_AlarmTypeDef sAlarm = {0};//將生成代碼中的這一結(jié)構(gòu)體定義為全局變量,并將生成的這一行代碼刪除
RTC_TimeTypeDef Now_Time;//時間結(jié)構(gòu)體
RTC_DateTypeDef Now_Date;//日期結(jié)構(gòu)體
/*獲取當(dāng)前時間*/
void Get_Time()
{
HAL_RTC_GetTime(&hrtc,&Now_Time,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&Now_Date,RTC_FORMAT_BIN);
}
/*顯示當(dāng)前時間*/
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);
}
/*設(shè)置下一秒的鬧鐘*/
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();//獲取發(fā)生中斷的時間
Set_Alarm();//設(shè)定下一秒的鬧鐘
Show_Now_Time();//顯示當(dāng)前時間
}
正計(jì)時時鐘(一直刷LCD)
1.配置:
A.使能時鐘源
B.使能日歷
2.結(jié)構(gòu)體:
時間結(jié)構(gòu)體 RTC_TimeTypeDef
日期結(jié)構(gòu)體 RTC_DateTypeDef
3.代碼(在主循環(huán)中不斷調(diào)用下面兩個函數(shù)即可實(shí)現(xiàn)時鐘正計(jì)時):
RTC_TimeTypeDef Now_Time;//時間結(jié)構(gòu)體
RTC_DateTypeDef Now_Date;//日期結(jié)構(gòu)體
/*讀取當(dāng)前時間*/
void Get_Time()
{
HAL_RTC_GetTime(&hrtc,&Time_Now,RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc,&Date_Now,RTC_FORMAT_BIN);
}
/*顯示當(dāng)前時間*/
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位分辨率對應(yīng)轉(zhuǎn)換值上限為4096
2.不需要開啟中斷
3.配置
A.使用MX初始化時直接默認(rèn)配置即可
4.函數(shù):
開啟ADC HAL_ADC_Start(ADC_HandleTypeDef *hadc);
讀取ADC的轉(zhuǎn)換值 HAL_ADC_GetValue(ADC_HandleTypeDef *hadc)(每次getvalue之前都需要先Start);
? 5.代碼:
double ADC_GetValue()
{
uint32_t count;//保存計(jì)數(shù)值
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轉(zhuǎn)換值上限為4096
2.配置
A.OUT1 mode 選擇 Connected to external pin only
3.函數(shù):
開啟DAC HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);
設(shè)置DAC的轉(zhuǎn)換值 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);//將電壓轉(zhuǎn)換為0~4096的值
HAL_DAC_SetValue(&hdac1, DAC_CHANNEL_1,DAC_ALIGN_12B_R,temp);//設(shè)置DAC的值
}
?4.調(diào)用代碼:
void main()
{
HAL_DAC_Start(&hdac1, DAC_CHANNEL_1);//僅需調(diào)用一次
Dac1_Set_Vol(2.55);//后續(xù)直接調(diào)用該函數(shù)即可,參數(shù)為輸出電壓值
}
USART(用mx配置時要修改引腳和GPIO時鐘)!
改代碼:
需要更改HAL_UART_MspInit() 和 HAL_UART_MspDeInit();
改CubeMX配置:
建議在CubeMX中重新指定引腳,在CubeMX中先選擇PA9、PA10引腳功能為UART再去配置串口即可。
引腳:
PA9 ------> USART1_TX
PA10 ------> USART1_RX
1.參數(shù)配置:
Mode 異步通信
PIN GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10;(MX內(nèi)配置有誤)
2.函數(shù):
發(fā)送
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字節(jié)數(shù)據(jù)中斷一次)
HAL_UART_Receive_IT(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size)
接收中斷回調(diào)函數(shù)(每次中斷后需要重新開啟接收中斷)
HAL_UART_RxCpltCallback( UART_HandleTypeDef *huart )
TIM
1.基本定時器:(TIM6 TIM7)
A.參數(shù)配置:
Prescaler(PSC) 預(yù)分頻值,決定定時器工作頻率
Counter Mode 計(jì)數(shù)模式
Counter Period 重裝載值
Trigger Event Selection 觸發(fā)事件
B.函數(shù):
回調(diào)函數(shù)
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
以中斷方式開啟定時器
HAL_TIM_Base_Start_IT(&htim6);
關(guān)閉定時器
HAL_TIM_Base_Stop_IT(&htim6);
2.高級定時器:(輸入頻率測量)
A.參數(shù)配置:
模式選擇輸入捕獲
使能定時器中斷
B.函數(shù):
初始化函數(shù)啟動輸入捕獲中斷模式
獲取定時器計(jì)數(shù)值
__HAL_TIM_GET_COUNTER(&htimx);//讀TIMX->CNT也可以
設(shè)置定時器計(jì)數(shù)值
__HAL_TIM_SetCounter(&htimx,0);//清零 TIM->CNT=0也可以
輸入捕獲回調(diào)函數(shù)
HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);//測量頻率
在中斷模式下啟動TIM輸入捕獲測量
HAL_TIM_IC_Start_IT(&htimx, TIM_CHANNEL_x);//使用此函數(shù)時必須在主函數(shù)先調(diào)用一次
C.例:(TIM測量輸入頻率)
定時器輸入捕獲方式設(shè)置為上升沿觸發(fā),中斷后在回調(diào)函數(shù)中獲取當(dāng)前計(jì)數(shù)值,用分頻后的時鐘頻率/計(jì)數(shù)值即可得出輸入頻率,需要計(jì)數(shù)器清零并且開啟一次中斷為下次測量做準(zhǔn)備。
D.測輸入頻率占空比:需要將輸入捕獲中的捕獲方式更改為雙沿捕獲,即上升沿和下降沿時都發(fā)生中斷,再用高電平的計(jì)數(shù)值除以一個周期的計(jì)數(shù)值即可得出占空比。
/*得到一個周期和一個周期內(nèi)高電平對應(yīng)的計(jì)數(shù)值*/
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;//一個周期對應(yīng)的計(jì)數(shù)值
TIM2->CNT = 0;
}
else//下降沿
{
Tim2_highCount = TIM2->CNT;//高電平對應(yīng)的計(jì)數(shù)值
}
}
else if(htim == &htim3)
{
if(HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_4) == GPIO_PIN_SET)//上升沿
{
Tim3_cycleCount = TIM3->CNT;//一個周期對應(yīng)的計(jì)數(shù)值
TIM3->CNT = 0;//計(jì)數(shù)值清零
}
else//下降沿
{
Tim3_highCount = TIM3->CNT;//高電平對應(yīng)的計(jì)數(shù)值
}
}
}
/*用計(jì)數(shù)值計(jì)算輸入信號頻率以及占空比并顯示*/
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.將定時器對應(yīng)通道功能選擇為PWM輸出
C.Prescaler為分頻系數(shù)。將系統(tǒng)時鐘進(jìn)行分頻
D.Counter為溢出值
E.Pulse是一個閾值,區(qū)分Pulse/Counter之前與之后的電平
F.配置的參數(shù)都減一
2.函數(shù):
回調(diào)函數(shù)
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頻率=系統(tǒng)時鐘頻率/Prescaler/Counter,假設(shè)系統(tǒng)時鐘頻率為80MHz,則將Prescaler配置為80,Counter配置為1000,可得頻率為1KHz。
占空比:高電平時間/一個周期時間,1KHz對應(yīng)一個周期時間為1ms,可將Pulse設(shè)置為800,在0 ~ 800計(jì)數(shù)值時輸出高電平,時間為8/10個周期即0.8ms,801~1000時輸出低電平,可得占空比為80%
4.用PWM輸出引腳輸出持續(xù)的高電平或低電平
?持續(xù)的高電平,CCR>ARR
持續(xù)的低電平,CCR=0文章來源:http://www.zghlxwxcb.cn/news/detail-403084.html
總結(jié)
以上就是全部內(nèi)容,如有錯誤請批評指正。
做好賽前梳理,爭取在比賽中發(fā)揮正常水平,愿各位明天旗開得勝~文章來源地址http://www.zghlxwxcb.cn/news/detail-403084.html
到了這里,關(guān)于STM32G431RB--基于HAL庫(藍(lán)橋杯嵌入式賽前梳理)的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!