STM32的簡易電子表
前言:最近在做嵌入式選修課設,基于STM32F103C8Tx實作電子表的簡單模擬,由于沒有芯片,老師只是要求我們用Proteus進行仿真(Proteus仿真有點不準確),課程結束以后特地寫一篇博客來記錄自己的想法
- 首先我們需要設計原理圖,原理圖共有兩部分,一部分是電源的設計,另一部分是數碼管以及按鍵的電路設計
對于電源部分我們需要對VCC進行降壓處理(STM32芯片正常作業電壓為3.3V),這里用到了BUCK電路,有需要了解的可以點擊了解BUCK電路
下面是數碼管以及按鍵的原理圖
板子實體(布線不是很好)
2. 接下來我們可以利用Cube生成代碼,時間的更新我們采用定時器中斷即可
1.打開STM32CubeMX,新建專案選擇STM32F103C8Tx
2.設定RCC時鐘源
3.設定GPIO引腳
4.配置定時器,這里選擇的是TIM3(有關定時器的知識讀者可自行了解)
5.配置時鐘樹
6.生成代碼并用keil打開
3. 撰寫代碼
1.void SystemClock_Config(void)
void SystemClock_Config(void)
{
HAL_StatusTypeDef ret = HAL_OK;
RCC_OscInitTypeDef RCC_OscInitStructure;
RCC_ClkInitTypeDef RCC_ClkInitStructure;
RCC_OscInitStructure.OscillatorType=RCC_OSCILLATORTYPE_HSE; //時鐘源為HSE
RCC_OscInitStructure.HSEState=RCC_HSE_ON; //打開HSE
RCC_OscInitStructure.HSEPredivValue=RCC_HSE_PREDIV_DIV1; //HSE預分頻
RCC_OscInitStructure.PLL.PLLState=RCC_PLL_ON; //打開PLL
RCC_OscInitStructure.PLL.PLLSource=RCC_PLLSOURCE_HSE; //PLL時鐘源選擇HSE
RCC_OscInitStructure.PLL.PLLMUL=RCC_PLL_MUL9; //主PLL倍頻因子
ret=HAL_RCC_OscConfig(&RCC_OscInitStructure); //初始化
if(ret!=HAL_OK) while(1);
//選中PLL作為系統時鐘源并且配置HCLK,PCLK1和PCLK2
RCC_ClkInitStructure.ClockType=(RCC_CLOCKTYPE_SYSCLK|RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2);
RCC_ClkInitStructure.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK; //設定系統時鐘時鐘源為PLL
RCC_ClkInitStructure.AHBCLKDivider=RCC_SYSCLK_DIV1; //AHB分頻系數為1
RCC_ClkInitStructure.APB1CLKDivider=RCC_HCLK_DIV2; //APB1分頻系數為2
RCC_ClkInitStructure.APB2CLKDivider=RCC_HCLK_DIV1; //APB2分頻系數為1
ret=HAL_RCC_ClockConfig(&RCC_ClkInitStructure,FLASH_LATENCY_2); //同時設定FLASH延時周期
if(ret!=HAL_OK) while(1);
}
2.void GPIO_INIT(void)
void void GPIO_INIT(void)
{
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOA_CLK_ENABLE(); //開啟GPIOA時鐘
__HAL_RCC_GPIOB_CLK_ENABLE(); //開啟GPIOB時鐘
GPIO_Initure.Pin=K4_Pin|K2_Pin; //K4_Pin|K2_Pin
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽輸出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
GPIO_Initure.Pin=K1_Pin; //K3_Pin|K1_Pin
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽輸出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
GPIO_Initure.Pin=K3_Pin; //K3_Pin|K1_Pin
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽輸出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
GPIO_Initure.Pin=LIGHTS_Pin|G_Pin|B_Pin|C_Pin |E_Pin|D_Pin; //LIGHTS_Pin|G_Pin|B_Pin|C_Pin |E_Pin|D_Pin
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽輸出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOA,&GPIO_Initure);
GPIO_Initure.Pin=F_Pin|A_Pin|P_Pin; //F_Pin|A_Pin|P_Pin;
GPIO_Initure.Mode=GPIO_MODE_OUTPUT_PP; //推挽輸出
GPIO_Initure.Pull=GPIO_PULLUP; //上拉
GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
GPIO_Initure.Pin=KEY1_Pin|KEY0_Pin; //KEY1_Pin|KEY0_Pin;
GPIO_Initure.Mode=GPIO_MODE_INPUT; //input
GPIO_Initure.Speed=GPIO_SPEED_LOW; //低速
HAL_GPIO_Init(GPIOB,&GPIO_Initure);
HAL_GPIO_WritePin(GPIOA, K3_Pin|K4_Pin|K2_Pin|LIGHTS_Pin|G_Pin|B_Pin|C_Pin |E_Pin|D_Pin, GPIO_PIN_SET); //置1
HAL_GPIO_WritePin(GPIOB, K1_Pin|F_Pin|A_Pin|P_Pin, GPIO_PIN_SET); //置1
}
3.void TIM3_Init(void)
void TIM3_Init(void)
{
TIM3_Handler.Instance=TIM3; //TIMER3
TIM3_Handler.Init.Prescaler=7200-1; //分頻系數 500ms 500ms中斷一次
TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上計數器
TIM3_Handler.Init.Period=5000-1; //自動裝載值
TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; //時鐘分頻因子
HAL_TIM_Base_Init(&TIM3_Handler);
HAL_TIM_Base_Start_IT(&TIM3_Handler); //使能定時器3和定時器3更新中斷:TIM_IT_UPDATE
}
4.void HAL_TIM_Base_MspInit(TIM_HandleTypeDef htim)
//設定中斷優先級
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance==TIM3)
{
__HAL_RCC_TIM1_CLK_ENABLE(); //使能TIM1時鐘
__HAL_RCC_TIM3_CLK_ENABLE(); //使能TIM3時鐘
HAL_NVIC_SetPriority(TIM3_IRQn,1,3); //設定中斷優先級
HAL_NVIC_EnableIRQ(TIM3_IRQn); //開啟ITM3中斷
}
}
5.void TIM3_IRQHandler(void)
//TIMER3中斷服務函式
void TIM3_IRQHandler(void)
{
//HAL_GPIO_TogglePin(A_GPIO_Port, A_Pin);
//HAL_GPIO_TogglePin(B_GPIO_Port, B_Pin);
//HAL_GPIO_TogglePin(C_GPIO_Port, C_Pin);
//HAL_GPIO_TogglePin(D_GPIO_Port, D_Pin);
//HAL_GPIO_TogglePin(E_GPIO_Port, E_Pin);
//HAL_GPIO_TogglePin(F_GPIO_Port, F_Pin);
//HAL_GPIO_TogglePin(G_GPIO_Port, G_Pin);
//HAL_GPIO_TogglePin(P_GPIO_Port, P_Pin);
//HAL_GPIO_TogglePin(LIGHTS_GPIO_Port, LIGHTS_Pin);
//Timer500ms();
TimerOneSecond();
//HAL_GPIO_TogglePin(LIGHTS_GPIO_Port, LIGHTS_Pin);
//delay_ms(10);
HAL_TIM_IRQHandler(&TIM3_Handler);
}
*6.void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef htim)
//回呼函式,定時器中斷服務函式呼叫
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim==(&TIM3_Handler))
{
//HAL_GPIO_TogglePin(A_GPIO_Port, A_Pin);
//HAL_GPIO_TogglePin(B_GPIO_Port, B_Pin);
}
}
PS:這些都是基本的初始化函式也就是我們在Cube中配置的,我們需要的是設定定時器向上溢位的時間,并在定時器中斷時對時間進行更新
下面我們撰寫顯示函式
依次顯示數碼管的每個數字,利用視覺欺騙效果,人眼是觀察不出來的,弊端就是仿真時會因電腦性能的原因而發生閃爍,實際上人眼是識別不出來的
uint8_t SEG[10]={0xfc, 0x60, 0xda, 0xf2, 0x66, 0xb6, 0xbe, 0xe4, 0xfe, 0xf6};
int DELAY_TIME[4]={10, 10, 10, 10};
//選擇點亮的數碼管并將對應GPIO管腳置1
static void setSegOn(int index)
{
//GPIO PORT RESET
HAL_GPIO_WritePin(K1_GPIO_Port, K1_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(K2_GPIO_Port, K2_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(K3_GPIO_Port, K3_Pin, GPIO_PIN_RESET);
HAL_GPIO_WritePin(K4_GPIO_Port, K4_Pin, GPIO_PIN_RESET);
//GPIO PORT SET
switch(index)
{
case 0:
HAL_GPIO_WritePin(K1_GPIO_Port, K1_Pin, GPIO_PIN_SET);
break;
case 1:
HAL_GPIO_WritePin(K2_GPIO_Port, K2_Pin, GPIO_PIN_SET);
break;
case 2:
HAL_GPIO_WritePin(K3_GPIO_Port, K3_Pin, GPIO_PIN_SET);
break;
case 3:
HAL_GPIO_WritePin(K4_GPIO_Port, K4_Pin, GPIO_PIN_SET);
break;
default:
Error_Handler();
break;
}
}
//通過對不同管腳的置位來顯示對應的數字
static void DisplaySeg(uint8_t SegCode)
{
uint8_t value;
//P
value = SegCode&0x01;
if(value)
{
HAL_GPIO_WritePin(P_GPIO_Port, P_Pin, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(P_GPIO_Port, P_Pin, GPIO_PIN_SET);
}
//G
SegCode = SegCode>>1;
value = SegCode&0x01;
if(value)
{
HAL_GPIO_WritePin(G_GPIO_Port, G_Pin, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(G_GPIO_Port, G_Pin, GPIO_PIN_SET);
}
//F
SegCode = SegCode>>1;
value = SegCode&0x01;
if(value)
{
HAL_GPIO_WritePin(F_GPIO_Port, F_Pin, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(F_GPIO_Port, F_Pin, GPIO_PIN_SET);
}
//E
SegCode = SegCode>>1;
value = SegCode&0x01;
if(value)
{
HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(E_GPIO_Port, E_Pin, GPIO_PIN_SET);
}
//D
SegCode = SegCode>>1;
value = SegCode&0x01;
if(value)
{
HAL_GPIO_WritePin(D_GPIO_Port, D_Pin, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(D_GPIO_Port, D_Pin, GPIO_PIN_SET);
}
//C
SegCode = SegCode>>1;
value = SegCode&0x01;
if(value)
{
HAL_GPIO_WritePin(C_GPIO_Port, C_Pin, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(C_GPIO_Port, C_Pin, GPIO_PIN_SET);
}
//B
SegCode = SegCode>>1;
value = SegCode&0x01;
if(value)
{
HAL_GPIO_WritePin(B_GPIO_Port, B_Pin, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(B_GPIO_Port, B_Pin, GPIO_PIN_SET);
}
//A
SegCode = SegCode>>1;
value = SegCode&0x01;
if(value)
{
HAL_GPIO_WritePin(A_GPIO_Port, A_Pin, GPIO_PIN_RESET);
}
else
{
HAL_GPIO_WritePin(A_GPIO_Port, A_Pin, GPIO_PIN_SET);
}
}
static void DisplayValue(uint8_t value)
{
DisplaySeg(SEG[value]);
}
//顯示時間
void Display(void)
{
//第一個數字HOUR的十位
uint8_t Temp;
Temp = Time_Buf.Hour / 10;
setSegOn(0);
DisplayValue(Temp);
delay_ms(1400);
//第二個數字HOUR的個位
Temp = Time_Buf.Hour % 10;
setSegOn(1);
DisplayValue(Temp);
delay_ms(1400);
//第三個數字MINUTE的十位
Temp = Time_Buf.Min / 10;
setSegOn(2);
DisplayValue(Temp);
delay_ms(1400);
//第四個數字MINUTE的個位
Temp = Time_Buf.Min % 10;
setSegOn(3);
DisplayValue(Temp);
delay_ms(1400);
//點亮秒燈
if(Time_Buf.Ms)
{
HAL_GPIO_WritePin(LIGHTS_GPIO_Port, LIGHTS_Pin, GPIO_PIN_RESET);
//delay_ms(10);
//HAL_GPIO_WritePin(LIGHTS_GPIO_Port, LIGHTS_Pin, GPIO_PIN_SET);
}
else
{
HAL_GPIO_WritePin(LIGHTS_GPIO_Port, LIGHTS_Pin, GPIO_PIN_SET);
}
}
//初始化TIME
void Init(void)
{
Time_Buf.Hour = 0;
Time_Buf.Min = 0;
Time_Buf.Sec = 0;
Time_Buf.Ms = 0;
}
針對按鍵事件,只需要掃描有無按鍵按下而后執行回應的操作
int GLIMMER_TIME = 500;
int COUNT = -1;
int END = 1;
int temp;
//判斷按鍵是否被按下
void IsEdit(void)
{
if(!HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin))
{
//檢查按鈕是否未松開
while(!HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)){
delay_ms(10);
}
COUNT = (COUNT + 1) % 4;
END = 0;
}
}
//按鍵被按下執行相應的改變
void Edit(void)
{
// check whether edit button is push
if(!HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin) && END == 0)
{
//檢查按鈕是否未松開
while(!HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin)){
delay_ms(10);
}
//change the corresponding time
if(COUNT == 0)
{
Time_Buf.Hour = ( (((Time_Buf.Hour / 10) + 1) % 3) * 10 + Time_Buf.Hour % 10) % 24;
}
else if(COUNT == 1)
{
Time_Buf.Hour = ((Time_Buf.Hour / 10) * 10 + ((Time_Buf.Hour % 10) + 1) % 10) % 24;
}
else if(COUNT == 2)
{
Time_Buf.Min = (((Time_Buf.Min / 10 + 1) % 6) * 10 + Time_Buf.Min % 10) % 60;
}
else
{
Time_Buf.Min = (((Time_Buf.Min % 10 + 1) % 10) + Time_Buf.Min / 10 * 10 ) % 60;
}
}
}
定時器中斷時的時間更新
//每500ms在定時器中斷里執行一次時間的改變
void Timer500ms(void)
{
Time_Buf.Ms = (Time_Buf.Ms + 1) % 2;
Time_Buf.Sec += Time_Buf.Ms;
if(Time_Buf.Sec >= 60)
{
Time_Buf.Sec = 0;
Time_Buf.Min += 1;
if(Time_Buf.Min >= 60)
{
Time_Buf.Min = 0;
Time_Buf.Hour = (Time_Buf.Hour + 1) % 24;
}
}
}
Main()
Time_Def Time_Buf;
int main(void)
{
HAL_Init();
Init();
HAL_Init(); //初始化HAL庫
Stm32_Clock_Init(); //設定時鐘,72M
LED_Init(); //初始化LED
TIM3_Init(); //定時器3初始化
while (1)
{
IsEdit();
Edit();
Display();
}
}
4.下面使用Proteus進行仿真
需要下載至少8.6版本的Proteus,低版本不能對STM32芯片進行仿真
至此整個作業就全部完成啦!!!!!
嵌入式軟硬結合,還是很有意思的,雖然專案很簡單但是我還是從中識訓了很多,
革命尚未成功,同志仍需努力!
感謝薛老師的辛勤付出!
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/229297.html
標籤:其他












