基于STM32的简单电子表的实现

admin | 世界杯冰岛

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芯片进行仿真 至此整个作业就全部完成啦!!!!! 嵌入式软硬结合,还是很有意思的,虽然项目很简单但是我还是从中收获了很多。 革命尚未成功,同志仍需努力! 感谢薛老师的辛勤付出!