1.背景介紹及基礎認知
8大輸入輸出
斯密特觸發器:高于設定閾值輸出高電平,低于設定閾值輸出低電平
有關上拉輸入、下拉輸入、推挽輸出、開漏輸出、復用開漏輸出、復用推挽輸出以及浮空輸入、模擬輸入的區別
1、上拉輸入:上拉就是把電位拉高,比如拉到Vcc。上拉就是將IO口上不確定的信號通過一個上拉電阻把IO上拉為高電平!電阻同時起限流作用!弱強只是上拉電阻的阻值不同,沒有什么嚴格區分。
2、下拉輸入:就是把電壓拉低,拉到GND。與上拉原理相似。
3、浮空輸入:浮空(floating)就是邏輯器件的輸入引腳即不接高電平,也不接低電平。由于邏輯器件的內部結構,當它輸入引腳懸空時,相當于該引腳接了高電平。一般實際運用時,引腳不建議懸空,易受干擾。?通俗講就是讓管腳什么都不接,浮空著。
4、模擬輸入:模擬輸入是指傳統方式的輸入.數字輸入是輸入PCM數字信號,即0,1的二進制數字信號,通過數模轉換,轉換成模擬信號,經前級放大進入功率放大器,功率放大器還是模擬的。
5、推挽輸出:可以輸出高,低電平,連接數字器件;?推挽結構一般是指兩個三極管分別受兩互補信號的控制,總是在一個三極管導通的時候另一個截止。高低電平由IC的電源低定。
6、開漏輸出:輸出端相當于三極管的集電極.?要得到高電平狀態需要上拉電阻才行.?適合于做電流型的驅動,其吸收電流的能力相對強(一般20ma以內)。
7、復用開漏輸出、復用推挽輸出:可以理解為GPIO口被用作第二功能時的配置情況(即并非作為通用IO口使用)。
在STM32中選用IO模式,下面是參考網上的總結一下。
(1)?浮空輸入_IN_FLOATING?——浮空輸入,可以做KEY識別,RX
(2)帶上拉輸入_IPU——IO內部上拉電阻輸入
(3)帶下拉輸入_IPD——?IO內部下拉電阻輸入
(4)?模擬輸入_AIN?——應用ADC模擬輸入,或者低功耗下省電
(5)開漏輸出_OUT_OD?——IO輸出0接GND,IO輸出1,懸空,需要外接上拉電阻,才能實現輸出高電平。當輸出為1時,IO口的狀態由上拉電阻拉高電平,但由于是開漏輸出模式,這樣IO口也就可以由外部電路改變為低電平或不變。可以讀IO輸入電平變化,實現C51的IO雙向功能。
(6)推挽輸出_OUT_PP?——IO輸出0-接GND,?IO輸出1?-接VCC,讀輸入值是未知的
(7)復用功能的推挽輸出_AF_PP?——片內外設功能(I2C的SCL,SDA)
(8)復用功能的開漏輸出_AF_OD——片內外設功能(TX1,MOSI,MISO.SCK.SS)
1.1.內核劃分
A: 應用在手機?R: 實時性很高的領域 M: 應用在單片機
1.2.外設
1.3.在線安裝支持包
2.新建工程文件
2.1.正確插線
2.2.添加必要的工程文件
2.2.1.在工程文件下新建一個Start文件夾,講必要的工程文件添加到此文件夾
2.2.2.添加頭文件路徑?
2.2.3.創建User 文件夾創建Main函數
2.2.4.添加庫文件 :工程下創建Library文件夾,把庫函數都添加進去,創建Library組,在添加Library下的現有項
2.2.5.復制到工程User文件夾,再在Keil下添加現有項
2.2.6.定義宏配置工程文件
2.2.7.添加library和User頭文件的路徑
2.2.8.如何選擇啟動文件?
2.2.9.新建工程的步驟?
2.2.10.工程架構
3.GPIO
3.1.元氣件兩端有高低電平才可以形成電流:上:單片機低電平形成電流 下:單片機高電平形成電流
3.2.二極管:PN結
二極管有顏色的一極為負極:P為正極,N為負極;電流只能從正極流到負極,電子只能從負極客流到正極?
3.2.1.整流橋
3.3.三極管:NPN
PNP:
3.3.1.三極管發射極和集電極為什么不能互換
3.3.2.三極管連接的元器件位置(漏電問題):防止基極的小電流流過
3.3.3.開關電路
3.4.面包板的構造
3.5.清除工程文件
4.GPIO輸入輸出
4.1.RCC、GPIO最常用的3個函數
RCC簡介:RCC是Reset and Clock Control (復位和時鐘控制)的縮寫,它是STM32內部的一個重要外設,負責管理各種時鐘源和時鐘分頻,以及為各個外設提供時鐘使能。RCC模塊可以通過寄存器操作或者庫函數來配置。
4.2.使用GPIO的函數點亮和熄滅燈
//設置時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//初始化GPIOGPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//輸入低電平//GPIO_ResetBits(GPIOA,GPIO_Pin_0);//輸入高電平//GPIO_SetBits(GPIOA, GPIO_Pin_0);//輸入低電平//GPIO_WriteBit(GPIOA,GPIO_Pin_0, Bit_RESET);//輸入高電平GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);
4.3.使用延時函數達到使用燈閃爍的效果
4.3.1.在工程添加延時函數庫
電路將LED接到A0
代碼
int main(void)
{//設置時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//初始化GPIOGPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);while(1){//輸入低電平//GPIO_WriteBit(GPIOA,GPIO_Pin_0, Bit_RESET);Delay_ms(100);//輸入高電平GPIO_WriteBit(GPIOA, GPIO_Pin_0, Bit_SET);Delay_ms(100);}return 0;
}
4.4.蜂鳴器
4.4.1.VCC/VSS/VEE等含義
一文讀懂電路中VCC、VDD、VEE、VSS的區別 - 知乎 (zhihu.com)
在電子電路設計中,VCC、VDD、VEE和VSS是常見的電源和地線標識,它們各自代表不同的電源電壓和地線類型。理解這些術語的區別對于正確設計和分析電路至關重要。
VCC
- 定義:VCC是“Collector Voltage”或“Circuit Voltage”的縮寫,通常用于雙極型晶體管(如NPN晶體管)的集電極電源電壓。在數字電路中,VCC通常指代正電源電壓,用于為電路提供所需的正電壓。
- 應用:VCC廣泛應用于模擬電路和數字電路中,提供電路所需的正電壓。例如,在NPN晶體管電路中,VCC為正電壓。
- 特點:VCC通常用于模擬電源,提供穩定的電壓。
VDD
- 定義:VDD是“Drain Voltage”或“Device Voltage”的縮寫,主要用于MOS晶體管和CMOS電路。它表示器件內部的工作電壓。
- 應用:VDD常見于集成電路(IC)和數字電路中,提供芯片的正電源電壓。例如,在CMOS電路中,VDD通常連接到PMOS晶體管的源極。
- 特點:VDD通常用于數字電源,提供芯片內部的工作電壓。
VEE
- 定義:VEE是“Emitter Voltage”或“Emitter-Emitter Voltage”的縮寫,通常用于ECL電路的負電源電壓。
- 應用:VEE一般用于模擬電路中,提供負電源電壓。例如,在PNP型晶體管電路中,VEE表示連接到發射極的電源電壓。
- 特點:VEE在放大電路中較為常見,用于提供對稱電源。
VSS
- 定義:VSS是“Source Voltage”或“Supply Voltage”的縮寫,表示電源地或0V。
- 應用:VSS通常用于數字電路中,作為電路的參考點或接地。在CMOS電路中,VSS指負電源。
- 特點:VSS是電路的公共接地端,用于消除噪聲和提供參考電平。
總結
- 電壓極性:VCC和VDD均表示正電源引腳,而VEE表示負電源引腳。VSS則代表接地引腳,不涉及電壓極性。
- 應用領域:VCC和VDD主要用于數字電路中,提供操作所需的正電壓;VEE通常用于模擬電路中,提供負電壓;VSS則用于連接電路到地,確保電路工作正常。
- 傳統用法:VCC和VSS這對術語通常用于晶體管和集成電路中;VDD和VEE則更常見于模擬電路設計中。
通過理解VCC、VDD、VEE和VSS的區別,可以更好地設計和調試電子設備和電路,確保電路的穩定性和可靠性。
代碼:設置時鐘 初始化對應接口 使用延時函數
//蜂鳴器int main(){RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure);while(1){GPIO_ResetBits(GPIOB, GPIO_Pin_13);Delay_ms(100);GPIO_SetBits(GPIOB, GPIO_Pin_13);Delay_ms(100);GPIO_ResetBits(GPIOB, GPIO_Pin_13);Delay_ms(100);GPIO_SetBits(GPIOB, GPIO_Pin_13);Delay_ms(700);}return 0;}
4.4.分壓電路
R1變小U0變大,R2變小U0變小
4.5.每一個開關控制一個LED的點亮和熄滅
線路圖
?main
//開關uint8_t keyNum;int main(){LED_Init();Key_Init();LED1_Off();LED2_Off();while(1){keyNum = GetKeyNum();if(keyNum == 1)LED1_turn();if(keyNum == 2)LED2_turn();}return 0;}
LED函數:LED取反:讀取對應端口的高低電平,再設置取反的電平
#include "stm32f10x.h" void LED_Init() {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_2;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure); }//LED的熄滅和點開 void LED1_ON() {GPIO_ResetBits(GPIOA, GPIO_Pin_1); } void LED1_Off() {GPIO_SetBits(GPIOA, GPIO_Pin_1); } void LED2_ON() {GPIO_ResetBits(GPIOA, GPIO_Pin_2); } void LED2_Off() {GPIO_SetBits(GPIOA, GPIO_Pin_2); } void LED1_turn() {//如果為低電平變為高電平if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_1) == 0)GPIO_SetBits(GPIOA, GPIO_Pin_1); elseGPIO_ResetBits(GPIOA, GPIO_Pin_1); } void LED2_turn() {//如果為低電平變為高電平if(GPIO_ReadOutputDataBit(GPIOA, GPIO_Pin_2) == 0)GPIO_SetBits(GPIOA, GPIO_Pin_2); elseGPIO_ResetBits(GPIOA, GPIO_Pin_2); }
Key函數:按下/松開開關會有抖動,使用延遲函數消除
#include "stm32f10x.h" #include "Delay.h"void Key_Init() {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1 | GPIO_Pin_11;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); }uint8_t GetKeyNum() {uint8_t keyNum = 0;//LED2if( GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11 ) == 0 ){//消除抖動Delay_ms(20);while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_11) == 0);//等松手//消除抖動Delay_ms(20);keyNum = 2;}//LED1if( GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1 ) == 0 ){//消除抖動Delay_ms(20);while(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0);//等松手//消除抖動Delay_ms(20);keyNum = 1;}return keyNum; }
4.6. 光敏電阻控制蜂鳴器
電路圖
main?
//關敏電阻驅動蜂鳴器int main(){Buzzer_Init();LightSensor_Init();Buzzer_Off();while(1){if(GetLightSenNum() == 1)Buzzer_ON();elseBuzzer_Off();}return 0;}
Lightsensor?
#include "stm32f10x.h" #include "Delay.h"void LightSensor_Init() {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_13;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); }uint8_t GetLightSenNum() {return GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_13); }
?Buzzer
#include "stm32f10x.h" void Buzzer_Init() {RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_12;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOB, &GPIO_InitStructure); }void Buzzer_ON() {GPIO_ResetBits(GPIOB, GPIO_Pin_12); } void Buzzer_Off() {GPIO_SetBits(GPIOB, GPIO_Pin_12); } void Buzzer_turn() {//如果為低電平變為高電平if(GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_12) == 0)GPIO_SetBits(GPIOB, GPIO_Pin_12); elseGPIO_ResetBits(GPIOB, GPIO_Pin_12); }
?4.7.OLED調試
OLED驅動函數
接線線路圖
?4.6.1.調試
5.EXIT外部中斷
5.1.中斷的理論
不能使用pin相同的端口:例,PA1、PB1
5.2.對射式紅外傳感器
配置中斷:初始化配置此4項
AFIO沒有專門的庫,內容和GPIO寫在GPIO.h庫內
NVIC函數在雜項文件內
main
//對射式紅外傳感器int main(){CountSensor_Init();OLED_Init();OLED_ShowString(1, 1, "Count:");while(1){uint16_t countSensor_Count = CountSensor_Get();OLED_ShowNum(1, 7, countSensor_Count, 5);}return 0;}
countSensor
#include "stm32f10x.h" uint16_t countSensor_Count = 0; void CountSensor_Init() {//設置時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//初始化端口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉輸入,未檢測到輸入時默認為高電平GPIO_InitStructure.GPIO_Pin = GPIO_Pin_14;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//輸入不需要速度GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource14);//配置外部中斷引腳選擇//配置EXTIEXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line14;//那個端口EXTI_InitStructure.EXTI_LineCmd = ENABLE;//開啟EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中斷模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下拉觸發:遮擋時觸發,上拉觸發:遮擋移開后觸發EXTI_Init(&EXTI_InitStructure);//配置NVICNVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI15_10_IRQn;//選擇通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//搶占優先級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//響應優先級NVIC_Init(&NVIC_InitStructure); }void EXTI15_10_IRQHandler() {if(EXTI_GetITStatus(EXTI_Line14) == SET)//檢測14端口,是否發生異常{countSensor_Count++;EXTI_ClearITPendingBit(EXTI_Line14);//清除標志位,防止死循環} }uint16_t CountSensor_Get() {return countSensor_Count; }
5.3.編碼器計次
電路圖
main
int16_t encoderCount = 0;//編碼器改變數值int main(){Encoder_Init();OLED_Init();OLED_ShowString(1, 1, "Count:");while(1){encoderCount += EncoderNum_Get();OLED_ShowSignedNum(1, 7, encoderCount, 5);}return 0;}
encoder
#include "stm32f10x.h" int16_t encoderNum = 0; void Encoder_Init() {//設置時鐘RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);//初始化端口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//上拉輸入,未檢測到輸入時默認為高電平GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;//輸入不需要速度GPIO_Init(GPIOB, &GPIO_InitStructure);GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource0);//配置外部中斷引腳選擇GPIO_EXTILineConfig(GPIO_PortSourceGPIOB, GPIO_PinSource1);//配置EXTIEXTI_InitTypeDef EXTI_InitStructure;EXTI_InitStructure.EXTI_Line = EXTI_Line0 | EXTI_Line1;//那個端口EXTI_InitStructure.EXTI_LineCmd = ENABLE;//開啟EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中斷模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Falling;//下拉觸發:遮擋時觸發,上拉觸發:遮擋移開后觸發EXTI_Init(&EXTI_InitStructure);//配置NVICNVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = EXTI0_IRQn;//選擇通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//搶占優先級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//響應優先級NVIC_Init(&NVIC_InitStructure);NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//選擇通道NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;//搶占優先級NVIC_InitStructure.NVIC_IRQChannelSubPriority = 2;//響應優先級NVIC_Init(&NVIC_InitStructure); }void EXTI0_IRQHandler() {if(EXTI_GetITStatus(EXTI_Line0) == SET)//檢測0端口,是否發生異常{if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_1) == 0)encoderNum--;EXTI_ClearITPendingBit(EXTI_Line0);//清除標志位,防止死循環} } void EXTI1_IRQHandler() {if(EXTI_GetITStatus(EXTI_Line1) == SET)//檢測1端口,是否發生異常{if(GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_0) == 0)encoderNum++;EXTI_ClearITPendingBit(EXTI_Line1);//清除標志位,防止死循環} }uint16_t EncoderNum_Get() {int16_t tmp = encoderNum;encoderNum =0;return tmp; }
6.定時器
TIM ( Timer )定時器
- 定時器可以對輸入的時鐘進行計數,并在計數值達到設定值時觸發中斷
- 16 位計數器、預分頻器、自動重裝寄存器的時基單元,在 72MHz 計數時鐘下可以實現最大 59.65s 的定時
- 不僅具備基本的定時中斷功能,而且還包含內外時鐘源選擇、輸入 捕獲、輸出比較、編碼器接口、主從觸發模式等多種功能
- 根據復雜度和應用場景分為了高級定時器、通用定時器、基本定時器三種類型
基礎定時器:只有向上計時模式
通用定時器:向上計數、向下計數、中央對齊模式
6.1.定時器內部中斷和定時器外部中斷
time.h?
#include "stm32f10x.h" //定時器內部中斷 // void Time_Init() // { // RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//定時器1時鐘開啟 // TIM_InternalClockConfig(TIM2); // // //初始化時基單位 // TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; // TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//時鐘分頻 // TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//時鐘模式 // TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;//預分頻器,此值和下值上限都為2^16 // TIM_TimeBaseInitStructure.TIM_Prescaler =7200 - 1;//自動重裝器 // TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重復計時器,高級定時器才需要 // TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure); // // //清除一下標志位,防止一上電就進中斷 // TIM_ClearFlag(TIM2, TIM_FLAG_Update); // // TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//中斷配置 // // //NVIC中斷配置 // NVIC_InitTypeDef NVIC_InitStructure; // NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; // NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // NVIC_Init(&NVIC_InitStructure); // // TIM_Cmd(TIM2, ENABLE);//使能控制 // }//定時器外部中斷void Time_Init(){RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//定時器1時鐘開啟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_Initstructure;GPIO_Initstructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_Initstructure.GPIO_Pin = GPIO_Pin_0;GPIO_Initstructure.GPIO_Speed = GPIO_Speed_50MHz;TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x00);//初始化時基單位TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//時鐘分頻TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//時鐘模式TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;//預分頻器,此值和下值上限都為2^16TIM_TimeBaseInitStructure.TIM_Prescaler =1 - 1;//自動重裝器TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重復計時器,高級定時器才需要TIM_TimeBaseInit( TIM2, &TIM_TimeBaseInitStructure);//清除一下標志位,防止一上電就進中斷TIM_ClearFlag(TIM2, TIM_FLAG_Update);TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//中斷配置//NVIC中斷配置NVIC_InitTypeDef NVIC_InitStructure;NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_InitStructure);TIM_Cmd(TIM2, ENABLE);//使能控制}
main.h
//時鐘中斷uint32_t num = 0; int main(){OLED_Init();Time_Init();while(1){OLED_ShowNum(1, 5, num, 5);OLED_ShowNum(2, 5, TIM_GetCounter(TIM2), 5);//獲取自動重裝值}return 0;}void TIM2_IRQHandler()//對應的中斷函數{if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET ){TIM_ClearITPendingBit(TIM2, TIM_IT_Update);num++;}}
6.2. PWM
6.2.1.PWM的基礎概念
OC ( Output Compare )輸出比較?
- 輸出比較可以通過比較 CNT(計數器) 與 CCR(捕獲比較寄存器) 寄存器值的關系,來對輸出電平進行置1 、置0或翻轉的操作,用于輸出一定頻率和占空比的 PWM 波形
- 每個高級定時器和通用定時器都擁有 4 個輸出比較通道
- 高級定時器的前 3 個通道額外擁有死區生成和互補輸出的功能
計算一個頻率為1KHZ、占空比為50%、分辨率為1%的ARR、CCR、PSC?
6.2.2.呼吸燈
#include "stm32f10x.h"void PWM_Init() {//打開時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//選擇時鐘TIM_InternalClockConfig(TIM2);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//復用推挽輸出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化時基單元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARRTIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //TIM_OCPolarity_High極性不翻轉 TIM_OCPolarity_Low極性翻轉TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能TIM_OCInitStructure.TIM_Pulse = 100; //CCR值TIM_OC1Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE); }//修改CCR的值 void PWM_SetCompare1(uint32_t compare) {TIM_SetCompare1(TIM2, compare); }
void LightClink(int ms1, int ms2){PWM_SetCompare1(100);Delay_ms(ms1);PWM_SetCompare1(0);Delay_ms(ms2);}//時鐘內部/外部中斷int main(){PWM_Init();while(1){LightClink(2000, 1000);LightClink(2000, 1000);LightClink(2000, 1000);LightClink(1000, 1000);LightClink(1000, 1000);// for(int i = 0; i <=100; i++) // { // PWM_SetCompare1(i); // Delay_ms(40); // } // PWM_SetCompare1(0); // Delay_ms(40); // for(int i =0; i < 100 ; i++) // { // PWM_SetCompare1(100 - i); // Delay_ms(40); // } // PWM_SetCompare1(100); // Delay_ms(20);}return 0;}
6.2.3.端口重映射
//重映射端口RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO);GPIO_PinRemapConfig(GPIO_PartialRemap1_TIM2, ENABLE);GPIO_PinRemapConfig(GPIO_Remap_SWJ_NoJTRST, ENABLE);
6.2.4.PWM操作舵機
void PWM_Init() {//打開時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//選擇時鐘TIM_InternalClockConfig(TIM2);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化時基單元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrucure;TIM_TimeBaseInitStrucure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStrucure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStrucure.TIM_Period = 20000 - 1;TIM_TimeBaseInitStrucure.TIM_Prescaler = 72 - 1;TIM_TimeBaseInitStrucure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStrucure);//輸出比較TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = 0;TIM_OC2Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE); }//修改CCR的值 void PWM_SetCompare2(uint16_t compare) {TIM_SetCompare2(TIM2, compare); } void Set_Angle(int angle) {PWM_SetCompare2(angle * 2000 / 180 + 500); }
uint8_t keyNum ;int angle;//時鐘內部/外部中斷int main(){PWM_Init();Key_Init();OLED_Init();while(1){keyNum = GetKeyNum();if(keyNum == 1){angle += 30;if(angle > 180)angle = 0;}Set_Angle(angle);OLED_ShowString(1, 1, "angle:");OLED_ShowNum(1, 7, angle, 3);OLED_ShowString(2, 1, "num:");OLED_ShowNum(2, 5, keyNum, 1);}return 0;}
6.4.3.PWM操作電機
int8_t speed;int8_t keyNum;//PWM操作電機int main(){PWM_Init();Key_Init();OLED_Init();while(1){keyNum = GetKeyNum();if(keyNum == 1){speed += 20;if(speed > 100) speed = -100;Set_MotorSpeed(speed);}OLED_ShowString(1,1,"Speed:");OLED_ShowSignedNum(1, 7, speed, 4);}return 0;}
void PWM_Init() {//打開時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//選擇時鐘TIM_InternalClockConfig(TIM2);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2 ;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化時基單元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStrucure;TIM_TimeBaseInitStrucure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStrucure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStrucure.TIM_Period = 100 - 1;TIM_TimeBaseInitStrucure.TIM_Prescaler = 36 - 1;TIM_TimeBaseInitStrucure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStrucure);//輸出比較TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High;TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;TIM_OCInitStructure.TIM_Pulse = 0;TIM_OC3Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE); }//修改CCR的值 void PWM_SetCompare3(uint16_t compare) {TIM_SetCompare3(TIM2, compare); } void Set_MotorSpeed(int8_t speed) {if(speed >= 0){GPIO_SetBits(GPIOA, GPIO_Pin_4);GPIO_ResetBits(GPIOA, GPIO_Pin_5);PWM_SetCompare3(speed);}else{GPIO_SetBits(GPIOA, GPIO_Pin_5);GPIO_ResetBits(GPIOA, GPIO_Pin_4);PWM_SetCompare3(-speed);//speed當前小于0 } }
6.4.4.?輸入捕獲測頻率和占空比
輸入捕獲流程
輸入捕獲檢測方法
PSC數值修改?
//輸入捕獲測頻率int main(){PWM_Init();IC_Init();OLED_Init();PWM_SetCompare1(0);PWM_SetPrescaler(720 - 1);OLED_ShowString(1, 1, "Freq:00000Hz");OLED_ShowString(2, 1, "Duty:00%");while(1){OLED_ShowNum(1, 6, GetFreq(), 5);OLED_ShowNum(2, 6, GetDuty(), 2);}return 0;}
#include "stm32f10x.h"void PWM_Init() {//打開時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//選擇時鐘TIM_InternalClockConfig(TIM2);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;//復用推挽輸出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化時基單元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 100 - 1; //ARRTIM_TimeBaseInitStructure.TIM_Prescaler = 720 - 1; //PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//TIM_OCInitTypeDef TIM_OCInitStructure;TIM_OCStructInit(&TIM_OCInitStructure);TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1;TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //TIM_OCPolarity_High極性不翻轉 TIM_OCPolarity_Low極性翻轉TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //使能TIM_OCInitStructure.TIM_Pulse = 100; //CCR值TIM_OC1Init(TIM2, &TIM_OCInitStructure);TIM_Cmd(TIM2, ENABLE); }//輸入捕獲測頻率和占空比void IC_Init() {//打開時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//選擇時鐘TIM_InternalClockConfig(TIM3);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;//復用推挽輸出GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化時基單元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARRTIM_TimeBaseInitStructure.TIM_Prescaler = 72 - 1; //PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);//配置通道1TIM_ICInitTypeDef TIM_ICInitstructure;TIM_ICInitstructure.TIM_Channel = TIM_Channel_1;TIM_ICInitstructure.TIM_ICFilter = 0xf;//濾波器,參數數值增大可以消除噪音和毛刺的干擾TIM_ICInitstructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//邊沿檢測、極性選擇;上沿觸發TIM_ICInitstructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//分頻器TIM_ICInitstructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//數據選擇器,直連輸入TIM_ICInit(TIM3, &TIM_ICInitstructure);//配置通道2TIM_PWMIConfig(TIM3, &TIM_ICInitstructure);//等同于下面的代碼,函數內部進行ifelse判斷TIM_ICInitstructure.TIM_Channel = TIM_Channel_2;TIM_ICInitstructure.TIM_ICFilter = 0xf;//濾波器,參數數值增大可以消除噪音和毛刺的干擾TIM_ICInitstructure.TIM_ICPolarity = TIM_ICPolarity_Falling;//邊沿檢測、極性選擇;上沿觸發TIM_ICInitstructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;//分頻器TIM_ICInitstructure.TIM_ICSelection = TIM_ICSelection_IndirectTI;//數據選擇器,交叉輸入TIM_ICInit(TIM3, &TIM_ICInitstructure); TIM_SelectInputTrigger(TIM3, TIM_TS_TI1FP1);//選擇觸發源TIM_SelectSlaveMode(TIM3, TIM_SlaveMode_Reset);//選擇從模式TIM_Cmd(TIM3, ENABLE); }//修改CCR的值 void PWM_SetCompare1(uint16_t compare) {TIM_SetCompare1(TIM2, compare); } //修改PSC void PWM_SetPrescaler(uint16_t prescaler) {TIM_PrescalerConfig(TIM2, prescaler, TIM_PSCReloadMode_Immediate); }//獲取頻率 uint32_t GetFreq() {return 1000000 / (TIM_GetCapture1(TIM3) + 1); }//獲取占空比 uint32_t GetDuty() {return (TIM_GetCapture2(TIM3) + 1) * 100 / (TIM_GetCapture1(TIM3) + 1); }
6.4.5.編碼器接口
//輸入捕獲測頻率int main(){Encoder_Init();OLED_Init();OLED_ShowString(1, 1, "CNT:");while(1){OLED_ShowSignedNum(1, 5, Get_Encoder(), 5);Delay_ms(1000);}return 0;}
void Encoder_Init() {//打開時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);//初始化GPIORCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化時基單元TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;TIM_TimeBaseInitStructure.TIM_Period = 65536 - 1; //ARRTIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //PSCTIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure);//配置通道1TIM_ICInitTypeDef TIM_ICInitstructure;TIM_ICStructInit(&TIM_ICInitstructure);TIM_ICInitstructure.TIM_Channel = TIM_Channel_1;TIM_ICInitstructure.TIM_ICFilter = 0xf;//濾波器,參數數值增大可以消除噪音和毛刺的干擾TIM_ICInitstructure.TIM_Channel = TIM_Channel_2;TIM_ICStructInit(&TIM_ICInitstructure);TIM_ICInitstructure.TIM_ICFilter = 0xf;//濾波器,參數數值增大可以消除噪音和毛刺的干擾TIM_EncoderInterfaceConfig(TIM3, TIM_EncoderMode_TI12, TIM_ICPolarity_Rising, TIM_ICPolarity_Rising);TIM_Cmd(TIM3, ENABLE); }int16_t Get_Encoder() {int tmp = TIM_GetCounter(TIM3);TIM_SetCounter(TIM3, 0);return tmp; }
7.AD模數轉換
7.1.AD的概念
4種轉換模式
- 單次轉換, 非 掃 描模式
- 連續轉換,非掃描模式
- 單次轉換,掃描模式
- 連續轉換, 掃描模式
// //AD // uint16_t ADValue; // float voltage; // int main() // { // OLED_Init(); // OLED_ShowString(1 ,1 , "ADValue:"); // OLED_ShowString(2 ,1 , "Voltage:0.00V"); // AD_Init(); // while(1) // { // ADValue = AD_GetValue(); // voltage = (float)ADValue / 4095 * 3.3;//需先轉為浮點數進行除法運算 // // OLED_ShowNum(1, 9, ADValue, 5); // OLED_ShowNum(2, 9, voltage, 1); // OLED_ShowNum(2, 11, (uint16_t)( voltage * 100 ) % 100, 2); // // } // return 0; // }//多ADuint16_t ad1, ad2, ad3, ad4;int main(){OLED_Init();OLED_ShowString(1 ,1 , "ad1:");OLED_ShowString(2 ,1 , "ad2:");OLED_ShowString(3 ,1 , "ad3:");OLED_ShowString(4 ,1 , "ad4:");AD_Init();while(1){ad1 = AD_GetValue(ADC_Channel_0);ad2 = AD_GetValue(ADC_Channel_1);ad3 = AD_GetValue(ADC_Channel_2);ad4 = AD_GetValue(ADC_Channel_3);OLED_ShowNum(1, 5, ad1, 5);OLED_ShowNum(2, 5, ad2, 5);OLED_ShowNum(3, 5, ad3, 5);OLED_ShowNum(4, 5, ad4, 5);}return 0;}
#include "stm32f10x.h"//void AD_Init() //{ // RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE); // RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); // // RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72MHz / 6 = 12MHz // // //配置GPIO口 // GPIO_InitTypeDef GPIO_InitStructure; // GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN; // GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0; // GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // GPIO_Init(GPIOA, &GPIO_InitStructure); // // //規則組通道配置 // ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//ADC_Channel_0根據引腳定義表選擇 ADC_SampleTime_55Cycles5采樣周期數值越小速度越快,數值越大數據轉換越穩定 // // //初始化ADC // ADC_InitTypeDef ADC_InitStructure; // ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//獨立轉換模式 // ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右對齊 // ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//無外部源即軟件觸發 // ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//連續模式 // ADC_InitStructure.ADC_ScanConvMode = DISABLE;//掃描模式 // ADC_InitStructure.ADC_NbrOfChannel = 1; // ADC_Init(ADC1, &ADC_InitStructure); // // //運行控制 // ADC_Cmd(ADC1, ENABLE); // // //校準復位 // ADC_ResetCalibration(ADC1); // while(ADC_GetResetCalibrationStatus(ADC1) == SET); // ADC_StartCalibration(ADC1); // while(ADC_GetCalibrationStatus(ADC1) == SET); //}//uint16_t AD_GetValue() //{ // ADC_SoftwareStartConvCmd(ADC1, ENABLE);//啟動 // while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待 // return ADC_GetConversionValue(ADC1);//獲取 //}//連續轉換模式 //void AD_Init() //{ // ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//連續模式 // ADC_SoftwareStartConvCmd(ADC1, ENABLE);//連續轉換模式,只需要觸發啟動一次 //}//uint16_t AD_GetValue() //{ // return ADC_GetConversionValue(ADC1);//獲取 //}//非連續非掃描實現多AD void AD_Init() {RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72MHz / 6 = 12MHz//配置GPIO口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//規則組通道配置ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//ADC_Channel_0根據引腳定義表選擇 ADC_SampleTime_55Cycles5采樣周期數值越小速度越快,數值越大數據轉換越穩定//初始化ADCADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//獨立轉換模式ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右對齊ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//無外部源即軟件觸發ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;//連續模式ADC_InitStructure.ADC_ScanConvMode = DISABLE;//掃描模式ADC_InitStructure.ADC_NbrOfChannel = 1;ADC_Init(ADC1, &ADC_InitStructure);//運行控制ADC_Cmd(ADC1, ENABLE);//校準復位ADC_ResetCalibration(ADC1);while(ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while(ADC_GetCalibrationStatus(ADC1) == SET); }uint16_t AD_GetValue(uint8_t ADC_Channel) {ADC_RegularChannelConfig(ADC1, ADC_Channel, 1, ADC_SampleTime_55Cycles5);ADC_SoftwareStartConvCmd(ADC1, ENABLE);//啟動while(ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);//等待return ADC_GetConversionValue(ADC1);//獲取 }
8.DMA ( Direct Memory Access )直接存儲器存取
DMA數據轉運
//DMA數據轉運uint8_t arr1[] = {0x12, 0x23, 0xa1, 0x3b};uint8_t arr2[4];int main(){OLED_Init();MyDMA_Init((uint32_t)arr1, (uint32_t)arr2, 4);//數據轉運while(1){OLED_ShowString(1, 1, "arr1:");OLED_ShowString(3, 1, "arr2:");OLED_ShowNum(1, 7, (uint32_t)arr1, 8); OLED_ShowNum(3, 7, (uint32_t)arr1, 8); OLED_ShowHexNum(2, 1, arr1[0], 2);OLED_ShowHexNum(2, 4, arr1[1], 2);OLED_ShowHexNum(2, 7, arr1[2], 2);OLED_ShowHexNum(2, 10, arr1[3], 2);Delay_s(1); MyDMA_Transfer();arr1[0]++;arr1[1]++;arr1[2]++;arr1[3]++;OLED_ShowHexNum(4, 1, arr2[0], 2);OLED_ShowHexNum(4, 4, arr2[1], 2);OLED_ShowHexNum(4, 7, arr2[2], 2);OLED_ShowHexNum(4, 10, arr2[3], 2);Delay_s(1);}return 0;}
#include "stm32f10x.h"//ADC數據轉移 uint32_t mySize; void MyDMA_Transfer() {DMA_Cmd(DMA1_Channel1, DISABLE);//失能DMA_SetCurrDataCounter(DMA1_Channel1, mySize);//修改計數器DMA_Cmd(DMA1_Channel1, ENABLE);//使能DMA_GetFlagStatus(DMA1_FLAG_TC1);//等待轉運完成DMA_ClearFlag(DMA1_FLAG_TC1); } void MyDMA_Init(uint32_t addrA, uint32_t addrB, uint32_t size) {//時鐘開啟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = addrA;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_Byte;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Enable;DMA_InitStructure.DMA_MemoryBaseAddr = addrB;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;mySize = size;DMA_InitStructure.DMA_BufferSize = mySize;//計數器DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外部是來源還是目的地DMA_InitStructure.DMA_M2M = DMA_M2M_Enable;//是否使用軟件觸發DMA_InitStructure.DMA_Mode = DMA_Mode_Normal;//是否自動重裝DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//優先級DMA_Init(DMA1_Channel1, &DMA_InitStructure);//DMA使能DMA_Cmd(DMA1_Channel1, DISABLE);MyDMA_Transfer(); }
//DMA數據轉運ADCint main(){OLED_Init();MyDMA_Init();//數據轉運OLED_ShowString(1 ,1 , "ad1:");OLED_ShowString(2 ,1 , "ad2:");OLED_ShowString(3 ,1 , "ad3:");OLED_ShowString(4 ,1 , "ad4:");while(1){ // ADDMA_GetValue();OLED_ShowNum(1, 5, AD_Value[0], 5);OLED_ShowNum(2, 5, AD_Value[1], 5);OLED_ShowNum(3, 5, AD_Value[2], 5);OLED_ShowNum(4, 5, AD_Value[3], 5);}return 0;}
uint16_t AD_Value[4];void MyDMA_Init(void) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//時鐘開啟RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);RCC_ADCCLKConfig(RCC_PCLK2_Div6);//72MHz / 6 = 12MHz//配置GPIO口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 | GPIO_Pin_1 | GPIO_Pin_2 | GPIO_Pin_3;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//規則組通道配置ADC_RegularChannelConfig(ADC1, ADC_Channel_0, 1, ADC_SampleTime_55Cycles5);//ADC_Channel_0根據引腳定義表選擇 ADC_SampleTime_55Cycles5采樣周期數值越小速度越快,數值越大數據轉換越穩定ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 2, ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1, ADC_Channel_2, 3, ADC_SampleTime_55Cycles5);ADC_RegularChannelConfig(ADC1, ADC_Channel_3, 4, ADC_SampleTime_55Cycles5);//初始化ADCADC_InitTypeDef ADC_InitStructure;ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;//獨立轉換模式ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;//右對齊ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;//無外部源即軟件觸發ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;//連續模式ADC_InitStructure.ADC_ScanConvMode = ENABLE;//掃描模式ADC_InitStructure.ADC_NbrOfChannel = 4;ADC_Init(ADC1, &ADC_InitStructure);DMA_InitTypeDef DMA_InitStructure;DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)AD_Value;DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;DMA_InitStructure.DMA_BufferSize = 4;//計數器DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;//外部是來源還是目的地DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;//是否使用軟件觸發DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;//是否自動重裝DMA_InitStructure.DMA_Priority = DMA_Priority_Medium;//優先級DMA_Init(DMA1_Channel1, &DMA_InitStructure);//不可以在未使能前校準復位 // //校準復位 // ADC_ResetCalibration(ADC1); // while(ADC_GetResetCalibrationStatus(ADC1) == SET); // ADC_StartCalibration(ADC1); // while(ADC_GetCalibrationStatus(ADC1) == SET);//DMA使能DMA_Cmd(DMA1_Channel1, ENABLE);ADC_DMACmd(ADC1, ENABLE);//開啟DMA到ADC信號//ADC使能ADC_Cmd(ADC1, ENABLE);//校準復位ADC_ResetCalibration(ADC1);while(ADC_GetResetCalibrationStatus(ADC1) == SET);ADC_StartCalibration(ADC1);while(ADC_GetCalibrationStatus(ADC1) == SET);//自動轉換轉運的情況下使用ADC_SoftwareStartConvCmd(ADC1, ENABLE);//啟動 } void ADDMA_GetValue(void) {DMA_Cmd(DMA1_Channel1, DISABLE);//失能DMA_SetCurrDataCounter(DMA1_Channel1, 4);//修改計數器DMA_Cmd(DMA1_Channel1, ENABLE);//使能ADC_SoftwareStartConvCmd(ADC1, ENABLE);//啟動//等待轉運完成while(DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);DMA_ClearFlag(DMA1_FLAG_TC1); }
9.串口通信(USART)?
9.1.基礎概率
、
、
9.2.串口通信
使用printf函數,先打開Use MicroLIB
//此函數是printf函數底層調用打印的函數 int fputc(int ch, FILE *f) {Serial_SendByte(ch);return ch; } //串口int main(){Serial_Init();//printf函數移植方法printf("Num = %d\r\n",666);char str[100];sprintf(str, "Num = %d\r\n", 666);//指定打印位置Serial_SendString(str);while(1){}return 0;}
?串口接收
1.查詢
//串口發送int main(){Serial_Init();OLED_Init();while(1){//查詢if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET){receiveData = USART_ReceiveData(USART1);//讀取后標志位自動置空OLED_ShowHexNum(1, 1, receiveData, 2);}}return 0;}
2.中斷
//串口發送int main(){Serial_Init();OLED_Init();while(1){//中斷if(Serial_GetRxFLag() == 1){receiveData = Serial_GetRxData();//讀取后標志位自動置空OLED_ShowHexNum(1, 1, receiveData, 2);//回傳Serial_SendByte(receiveData);}}return 0;}
uint8_t serial_RxData,serial_RxFlag;uint8_t Serial_GetRxFLag(void) {if(serial_RxFlag == 1){serial_RxFlag = 0;return 1;}return 0; }uint8_t Serial_GetRxData() {return serial_RxData; }//串口接收和發送 void Serial_Init(void) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//開啟gpio口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化串口USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 9600;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStructure.USART_Parity = USART_Parity_No;//效驗位USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位長度USART_InitStructure.USART_WordLength = USART_WordLength_8b;//報文8bitUSART_Init(USART1, &USART_InitStructure);//中斷USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_Initstructure;NVIC_Initstructure.NVIC_IRQChannel = USART1_IRQn;NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_Initstructure);USART_Cmd(USART1, ENABLE); }void USART1_IRQHandler(void) {if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET){serial_RxFlag = 1;serial_RxData = USART_ReceiveData(USART1);} }
9.3.串口通信數據包·
數據包
串口發送HEX數據包
uint8_t receiveData;//串口發送HEX數據包int main(){Serial_Init();OLED_Init();Key_Init();serial_TxPacket[0] = 0x12;serial_TxPacket[1] = 0x23;serial_TxPacket[2] = 0x34;serial_TxPacket[3] = 0x45;uint8_t keyNum;//按鍵while(1){keyNum = GetKeyNum();if(keyNum == 1){serial_TxPacket[0]++;serial_TxPacket[1]++;serial_TxPacket[2]++;serial_TxPacket[3]++;Serial_SendPacket();OLED_ShowHexNum(2, 1, serial_TxPacket[0], 2); OLED_ShowHexNum(2, 4, serial_TxPacket[1], 2); OLED_ShowHexNum(2, 7, serial_TxPacket[2], 2); OLED_ShowHexNum(2, 10, serial_T xPacket[3], 2); }if(Serial_GetRxFLag()){OLED_ShowHexNum(1, 1, serial_RxPacket[0], 2); OLED_ShowHexNum(1, 4, serial_RxPacket[1], 2); OLED_ShowHexNum(1, 7, serial_RxPacket[2], 2); OLED_ShowHexNum(1, 10, serial_RxPacket[3], 2); }}return 0;}
//串口發送HEX數據包uint8_t serial_RxPacket[4], serial_TxPacket[4]; uint8_t serial_Flag;uint8_t Serial_GetRxFLag(void) {if(serial_Flag == 1){serial_Flag = 0;return 1;}return 0; } //發送數據包 void Serial_SendPacket() {Serial_SendByte(0xFF);//報頭Serial_SendArray(serial_TxPacket, 4);Serial_SendByte(0xFE);//報尾 } //接受數據包//串口接收和發送 void Serial_Init(void) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//開啟gpio口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化串口USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 9600;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStructure.USART_Parity = USART_Parity_No;//效驗位USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位長度USART_InitStructure.USART_WordLength = USART_WordLength_8b;//報文8bitUSART_Init(USART1, &USART_InitStructure);//中斷USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_Initstructure;NVIC_Initstructure.NVIC_IRQChannel = USART1_IRQn;NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_Initstructure);USART_Cmd(USART1, ENABLE); }void USART1_IRQHandler(void) {if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET){static uint8_t serial_State = 0;//當前狀態static uint8_t pRxData = 0;//接受到第幾個數據uint8_t rxData = USART_ReceiveData(USART1);//讀取當前數據if(serial_State == 0){if(rxData == 0xFF){serial_State = 1;}}else if (serial_State == 1){serial_RxPacket[pRxData] = rxData;pRxData++;if(pRxData == 4){pRxData %= 4;serial_State = 2;}}else if(serial_State == 2){if(rxData == 0xFE){serial_State = 0;//狀態改變serial_Flag = 1;//標志位可讀}}} }
串口發送文本數據包
//串口發送文本數據包char serial_RxPacket[100]; uint8_t serial_Flag;uint8_t Serial_GetRxFLag(void) {if(serial_Flag == 1){serial_Flag = 0;return 1;}return 0; } //接受數據包//串口接收和發送 void Serial_Init(void) {RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);//開啟gpio口GPIO_InitTypeDef GPIO_InitStructure;GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化串口USART_InitTypeDef USART_InitStructure;USART_InitStructure.USART_BaudRate = 9600;USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;USART_InitStructure.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;USART_InitStructure.USART_Parity = USART_Parity_No;//效驗位USART_InitStructure.USART_StopBits = USART_StopBits_1;//停止位長度USART_InitStructure.USART_WordLength = USART_WordLength_8b;//報文8bitUSART_Init(USART1, &USART_InitStructure);//中斷USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitTypeDef NVIC_Initstructure;NVIC_Initstructure.NVIC_IRQChannel = USART1_IRQn;NVIC_Initstructure.NVIC_IRQChannelCmd = ENABLE;NVIC_Initstructure.NVIC_IRQChannelPreemptionPriority = 1;NVIC_Initstructure.NVIC_IRQChannelSubPriority = 1;NVIC_Init(&NVIC_Initstructure);USART_Cmd(USART1, ENABLE); }void USART1_IRQHandler(void) {if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET){static uint8_t serial_State = 0;//當前狀態static uint8_t pRxData = 0;//接受到第幾個數據uint8_t rxData = USART_ReceiveData(USART1);//讀取當前數據if(serial_State == 0){if(rxData == '@'){serial_State = 1;pRxData = 0;}}else if (serial_State == 1){if(rxData == '\r')serial_State = 2;else{serial_RxPacket[pRxData] = rxData;pRxData++;}}else if(serial_State == 2){if(rxData == '\n'){serial_State = 0;//狀態改變serial_RxPacket[pRxData] = '\0';//c串以'\0'結尾serial_Flag = 1;//標志位可讀}}} }
uint8_t receiveData;//串口發送文本數據包int main(){Serial_Init();OLED_Init();LED_Init();Key_Init();OLED_ShowString(1, 1, "TxPacket:");OLED_ShowString(3, 1, "RxPacket:");while(1){if(Serial_GetRxFLag()){OLED_ShowString(4, 1, " ");OLED_ShowString(4, 1, serial_RxPacket);if(strcmp(serial_RxPacket, "LED_ON") == 0){LED1_ON();Serial_SendString("LED_ON\r\n");OLED_ShowString(2, 1, " ");OLED_ShowString(2, 1, "LED_ON_OK");}else if(strcmp(serial_RxPacket, "LED_OFF") == 0){LED1_Off();Serial_SendString("LED_OFF\r\n");OLED_ShowString(2, 1, " ");OLED_ShowString(2, 1, "LED_OFF_OFF");}else{Serial_SendString("ERROR_COMMOD\r\n");OLED_ShowString(2, 1, " ");OLED_ShowString(2, 1, "ERROR_COMMOD");}}}return 0;}
?10.I2C
10.I2C通信
指定地址讀取/寫入一片區域的時序
12.Unix時間戳
12.1.時間戳的基礎概念
12.2.BKP、RTC概念
12.3.BKP讀寫數據
- BKP掉電數據(主和備用電源不同時掉電)不丟失
uint16_t writeArray[] = {0x1234, 0x5678};
uint16_t readArray[2];//BKP讀寫數據int main(){OLED_Init();Key_Init();//初始化時鐘PWR/BKPRCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);PWR_BackupAccessCmd(ENABLE);//備用電源訪問使能OLED_ShowString(1, 1, "W:");OLED_ShowString(2, 1, "R:");uint8_t keyNum;while(1){keyNum = GetKeyNum();//按鍵按下,寫入數據++并寫入if(keyNum == 1){writeArray[0]++;writeArray[1]++;BKP_WriteBackupRegister(BKP_DR1, writeArray[0]);BKP_WriteBackupRegister(BKP_DR2, writeArray[1]);OLED_ShowHexNum(1, 3, writeArray[0], 4);OLED_ShowHexNum(1, 8, writeArray[1], 4);}//實時讀取數據readArray[0] = BKP_ReadBackupRegister(BKP_DR1);readArray[1] = BKP_ReadBackupRegister(BKP_DR2);OLED_ShowHexNum(2, 3, readArray[0], 4);OLED_ShowHexNum(2, 8, readArray[1], 4);}return 0;}
?12.4.實時時間
//讀取RTCint main(){OLED_Init();MyRTC_Init();/*顯示靜態字符串*/OLED_ShowString(1, 1, "Date:XXXX-XX-XX");OLED_ShowString(2, 1, "Time:XX:XX:XX");OLED_ShowString(3, 1, "CNT :");OLED_ShowString(4, 1, "DIV :");while(1){struct tm time_data = MyRTC_ReadTime(); //RTC讀取時間,最新的時間存儲到MyRTC_Time數組中OLED_ShowNum(1, 6, time_data.tm_year, 4); //顯示MyRTC_Time數組中的時間值,年OLED_ShowNum(1, 11, time_data.tm_mon, 2); //月OLED_ShowNum(1, 14, time_data.tm_mday, 2); //日OLED_ShowNum(2, 6, time_data.tm_hour, 2); //時OLED_ShowNum(2, 9, time_data.tm_min, 2); //分OLED_ShowNum(2, 12, time_data.tm_sec, 2); //秒OLED_ShowNum(3, 6, RTC_GetCounter(), 10); //顯示32位的秒計數器OLED_ShowNum(4, 6, RTC_GetDivider(), 10); //顯示余數寄存器}return 0;}
#include "stm32f10x.h" #include <time.h>void MyRTC_SetTime(struct tm time_date) {time_t time_cnt; //定義秒計數器數據類型time_cnt = mktime(&time_date) - 8 * 60 * 60; //調用mktime函數,將日期時間轉換為秒計數器格式//- 8 * 60 * 60為東八區的時區調整RTC_SetCounter(time_cnt); //將秒計數器寫入到RTC的CNT中RTC_WaitForLastTask(); //等待上一次操作完成 }struct tm MyRTC_ReadTime(void) {time_t time_cnt; //定義秒計數器數據類型struct tm time_date; //定義日期時間數據類型time_cnt = RTC_GetCounter() + 8 * 60 * 60; //讀取RTC的CNT,獲取當前的秒計數器//+ 8 * 60 * 60為東八區的時區調整time_date = *localtime(&time_cnt); //使用localtime函數,將秒計數器轉換為日期時間格式time_date.tm_year += 1900;time_date.tm_mon += 1;return time_date; }void MyRTC_Init(void) {if (BKP_ReadBackupRegister(BKP_DR1) != 0xA5A5) //通過寫入備份寄存器的標志位,判斷RTC是否是第一次配置//if成立則執行第一次的RTC配置{//初始化時鐘,備用電源訪問打開RCC_APB1PeriphClockCmd(RCC_APB1Periph_BKP, ENABLE);RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);PWR_BackupAccessCmd(ENABLE);//時鐘源配置,等待時鐘準備好RCC_LSEConfig(RCC_LSE_ON);while(RCC_GetFlagStatus(RCC_FLAG_LSERDY) == RESET);//選擇時鐘源RCC_RTCCLKConfig(RCC_RTCCLKSource_LSE);RCC_RTCCLKCmd(ENABLE);//等待時鐘同步和上一次寫入程序結束RTC_WaitForSynchro();RTC_WaitForLastTask();//設定分頻RTC_SetPrescaler(32768 - 1);//分頻到1HZRTC_WaitForLastTask();//每次操作要等待上一次操作結束//設置起始值struct tm time_data;time_data.tm_year = 2025 - 1900;time_data.tm_mon = 5 -1 ;time_data.tm_mday = 19;time_data.tm_hour = 5;time_data.tm_min = 30;time_data.tm_sec = 15;MyRTC_SetTime(time_data);//RTC_SetCounter(1747599833);RTC_WaitForLastTask();//每次操作要等待上一次操作結束BKP_WriteBackupRegister(BKP_DR1, 0xA5A5); //在備份寄存器寫入自己規定的標志位,用于判斷RTC是不是第一次執行配置}else //RTC不是第一次配置{RTC_WaitForSynchro(); //等待同步RTC_WaitForLastTask(); //等待上一次操作完成} }
13.電源控制
13.1.基礎理論
13.2.修改主頻?
只讀文件,修改權限
//修改主頻int main(){OLED_Init();OLED_ShowString(1, 1, "SYSCLK:");OLED_ShowNum(1, 8, SystemCoreClock, 8);while(1){OLED_ShowString(2, 1, "Running");Delay_ms(500);OLED_ShowString(2, 1, " ");Delay_ms(500);}return 0;}
13.3.睡眠模式+串口收發
uint8_t receiveData;//串口發送int main(){Serial_Init();OLED_Init();while(1){
// //查詢
// if(USART_GetFlagStatus(USART1, USART_FLAG_RXNE) == SET)
// {
// receiveData = USART_ReceiveData(USART1);
// //讀取后標志位自動置空
// OLED_ShowHexNum(1, 1, receiveData, 2);
// }//中斷if(Serial_GetRxFLag() == 1){receiveData = Serial_GetRxData();//讀取后標志位自動置空OLED_ShowHexNum(1, 1, receiveData, 2);//回傳Serial_SendByte(receiveData);OLED_ShowHexNum(1, 1, receiveData, 2);}OLED_ShowString(2, 1, "Running");Delay_ms(100);OLED_ShowString(2, 1, " ");Delay_ms(100);__WFI();}return 0;}
?13.4.停機模式
- 進入停止模式后會使用HSI時鐘,需要重新選擇時鐘
//對射式紅外傳感器 + 停止模式int main(){CountSensor_Init();OLED_Init();OLED_ShowString(1, 1, "Count:");while(1){OLED_ShowNum(1, 7, CountSensor_Get(), 5);OLED_ShowString(2, 1, "Running");Delay_ms(100);OLED_ShowString(2, 1, " ");Delay_ms(100);//進入停止模式RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);PWR_EnterSTOPMode(PWR_Regulator_ON, PWR_STOPEntry_WFI);//進入停止模式后會使用HSI時鐘,需要重新使用時鐘SystemInit();}return 0;}
13.5.待機模式
?? ??? ??? ?//進入待機模式,程序將從頭開始執行,后續代碼不在執行,這也是不用再選擇開啟主頻的原因
//對射式紅外傳感器 + 待機模式int main(){MyRTC_Init();//開啟時鐘RCC_APB1PeriphClockCmd(RCC_APB1Periph_PWR, ENABLE);OLED_Init();OLED_ShowString(1, 1, "CNT:");OLED_ShowString(2, 1, "ALR:");OLED_ShowString(3, 1, "ALRF:");uint32_t alarm = RTC_GetCounter() + 10;RTC_SetAlarm(alarm );//鬧鐘設置為10s后OLED_ShowNum(2, 6, alarm , 10);//使用wakeup喚醒PWR_WakeUpPinCmd(ENABLE);while(1){OLED_ShowNum(1, 6, RTC_GetCounter(), 10);OLED_ShowNum(3, 6, RTC_GetFlagStatus(RTC_FLAG_ALR), 1);OLED_ShowString(4, 1, "Running");Delay_ms(100);OLED_ShowString(4, 1, " ");Delay_ms(100);//進入待機模式,程序將從頭開始執行,后續代碼不在執行,這也是不用再選擇開啟主頻的原因PWR_EnterSTANDBYMode();}return 0;}
14.看門狗
14.1.基礎概念
獨立看門狗?
窗口看門狗
WWDG 工作特性
- 遞減計數器 T[6:0] 的值小于 0x40 時, WWDG 產生復位
- 遞減計數器 T[6:0] 在窗口 W[6:0] 外被重新裝載時, WWDG 產生 復位
- 遞減計數器 T[6:0] 等于 0x40 時可以產生早期喚醒中斷( EWI ), 用于重裝載計數器以避免 WWDG 復位:作用:復位前保護數據,關閉一些危險元器件
- 定期寫入 WWDG_CR 寄存器(喂狗)以避免 WWDG 復位
獨立看門狗和窗口看門狗的區別
14.2.代碼實現
不用開啟時鐘的原因?
//獨立看門狗int main(){OLED_Init();Key_Init();OLED_ShowString(1, 1, "IWDG test:");if(RCC_GetFlagStatus(RCC_FLAG_IWDGRST) == SET){OLED_ShowString(2, 1, "IWDG RESET");Delay_ms(500);OLED_ShowString(2, 1, " ");Delay_ms(100);RCC_ClearFlag();}else{OLED_ShowString(3, 1, "ELSE RESET");Delay_ms(500);OLED_ShowString(3, 1, " ");Delay_ms(100);}IWDG_WriteAccessCmd(IWDG_WriteAccess_Enable);//取消寫保護IWDG_SetPrescaler(IWDG_Prescaler_16);IWDG_SetReload(2500 - 1);IWDG_ReloadCounter();//啟動前先喂一次狗,第一次就是完整的時間IWDG_Enable();//啟動看門狗會改變鍵寄存器,進入寫保護while(1){GetKeyNum();//函數內有一個等放手的while函數,會導致堵塞IWDG_ReloadCounter();OLED_ShowString(4, 1, "FEED");Delay_ms(600);OLED_ShowString(4, 1, " ");Delay_ms(200);}return 0;}
窗口看門狗
//獨立看門狗int main(){OLED_Init();Key_Init();OLED_ShowString(1, 1, "WWDG test:");if(RCC_GetFlagStatus(RCC_FLAG_WWDGRST) == SET){OLED_ShowString(2, 1, "WWDG RESET");Delay_ms(500);OLED_ShowString(2, 1, " ");Delay_ms(100);RCC_ClearFlag();}else{OLED_ShowString(3, 1, "ELSE RESET");Delay_ms(500);OLED_ShowString(3, 1, " ");Delay_ms(100);}RCC_APB1PeriphClockCmd(RCC_APB1Periph_WWDG, ENABLE);WWDG_SetPrescaler(WWDG_Prescaler_8);WWDG_SetWindowValue(0x40 | 21);WWDG_Enable(0x40 | 54);//啟動看門狗while(1){GetKeyNum();//函數內有一個等放手的while函數,會導致堵塞OLED_ShowString(4, 1, "FEED");Delay_ms(20);OLED_ShowString(4, 1, " ");Delay_ms(20);WWDG_SetCounter(0x40 | 54);//喂狗}return 0;}
15. flash閃存
- 使用flash閃存存儲數據時會占用閃存,這是代碼不在執行,因為程序存儲器(閃存的一部分)存儲著可執行程序
15.2.代碼實現使用閃存讀寫
uint8_t KeyNum; //定義用于接收按鍵鍵碼的變量int main(void) {/*模塊初始化*/OLED_Init(); //OLED初始化Key_Init(); //按鍵初始化Store_Init(); //參數存儲模塊初始化,在上電的時候將閃存的數據加載回Store_Data,實現掉電不丟失/*顯示靜態字符串*/OLED_ShowString(1, 1, "Flag:");OLED_ShowString(2, 1, "Data:");while (1){KeyNum = GetKeyNum(); //獲取按鍵鍵碼if (KeyNum == 1) //按鍵1按下{Store_Data[1] ++; //變換測試數據Store_Data[2] += 2;Store_Data[3] += 3;Store_Data[4] += 4;Store_Save(); //將Store_Data的數據備份保存到閃存,實現掉電不丟失}if (KeyNum == 2) //按鍵2按下{Store_Clear(); //將Store_Data的數據全部清0}OLED_ShowHexNum(1, 6, Store_Data[0], 4); //顯示Store_Data的第一位標志位OLED_ShowHexNum(3, 1, Store_Data[1], 4); //顯示Store_Data的有效存儲數據OLED_ShowHexNum(3, 6, Store_Data[2], 4);OLED_ShowHexNum(4, 1, Store_Data[3], 4);OLED_ShowHexNum(4, 6, Store_Data[4], 4);} }
?
#include "stm32f10x.h" // Device headeruint32_t MyFLASH_ReadWord(uint32_t Address) {return *((__IO uint32_t *)(Address)); //使用指針訪問指定地址下的數據并返回 }uint16_t MyFLASH_ReadHalfWord(uint32_t Address) {return *((__IO uint16_t *)(Address)); //使用指針訪問指定地址下的數據并返回 }uint8_t MyFLASH_ReadByte(uint32_t Address) {return *((__IO uint8_t *)(Address)); //使用指針訪問指定地址下的數據并返回 }void MyFLASH_EraseAllPages(void) {FLASH_Unlock(); //解鎖FLASH_EraseAllPages(); //全擦除FLASH_Lock(); //加鎖 }void MyFLASH_ErasePage(uint32_t PageAddress) {FLASH_Unlock(); //解鎖FLASH_ErasePage(PageAddress); //頁擦除FLASH_Lock(); //加鎖 }void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data) {FLASH_Unlock(); //解鎖FLASH_ProgramWord(Address, Data); //編程字FLASH_Lock(); //加鎖 }void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data) {FLASH_Unlock(); //解鎖FLASH_ProgramHalfWord(Address, Data); //編程半字FLASH_Lock(); //加鎖 }
#ifndef __MYFLASH_H #define __MYFLASH_Huint32_t MyFLASH_ReadWord(uint32_t Address); uint16_t MyFLASH_ReadHalfWord(uint32_t Address); uint8_t MyFLASH_ReadByte(uint32_t Address);void MyFLASH_EraseAllPages(void); void MyFLASH_ErasePage(uint32_t PageAddress);void MyFLASH_ProgramWord(uint32_t Address, uint32_t Data); void MyFLASH_ProgramHalfWord(uint32_t Address, uint16_t Data);#endif
#include "stm32f10x.h" // Device header #include "MyFLASH.h"#define STORE_START_ADDRESS 0x0800FC00 //存儲的起始地址 #define STORE_COUNT 512 //存儲數據的個數uint16_t Store_Data[STORE_COUNT]; //定義SRAM數組void Store_Init(void) {/*判斷是不是第一次使用*/if (MyFLASH_ReadHalfWord(STORE_START_ADDRESS) != 0xA5A5) //讀取第一個半字的標志位,if成立,則執行第一次使用的初始化{MyFLASH_ErasePage(STORE_START_ADDRESS); //擦除指定頁MyFLASH_ProgramHalfWord(STORE_START_ADDRESS, 0xA5A5); //在第一個半字寫入自己規定的標志位,用于判斷是不是第一次使用for (uint16_t i = 1; i < STORE_COUNT; i ++) //循環STORE_COUNT次,除了第一個標志位{MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, 0x0000); //除了標志位的有效數據全部清0}}/*上電時,將閃存數據加載回SRAM數組,實現SRAM數組的掉電不丟失*/for (uint16_t i = 0; i < STORE_COUNT; i ++) //循環STORE_COUNT次,包括第一個標志位{Store_Data[i] = MyFLASH_ReadHalfWord(STORE_START_ADDRESS + i * 2); //將閃存的數據加載回SRAM數組} }void Store_Save(void) {MyFLASH_ErasePage(STORE_START_ADDRESS); //擦除指定頁for (uint16_t i = 0; i < STORE_COUNT; i ++) //循環STORE_COUNT次,包括第一個標志位{MyFLASH_ProgramHalfWord(STORE_START_ADDRESS + i * 2, Store_Data[i]); //將SRAM數組的數據備份保存到閃存} }void Store_Clear(void) {for (uint16_t i = 1; i < STORE_COUNT; i ++) //循環STORE_COUNT次,除了第一個標志位{Store_Data[i] = 0x0000; //SRAM數組有效數據清0}Store_Save(); //保存數據到閃存 }