深圳市航顺芯片技术研发有限公司
直播中

胡秋阳

13年用户 1598经验值
私信 关注
[问答]

怎样去制作一种基于STM32F103C8Tx的简易电子表

怎样去设计基于STM32F103C8Tx简易电子表的电源部分?
怎样去设计基于STM32F103C8Tx简易电子表的数码管以及按键电路部分?

回帖(2)

吴樵

2021-8-12 15:31:52
  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芯片进行仿真
  
  
  至此整个作业就全部完成啦!!!!!
  嵌入式软硬结合,还是很有意思的,虽然项目很简单但是我还是从中收获了很多。
举报

jf_21877366

2021-10-11 09:14:46
您好,我想问一下这个设计需要买一些什么元器件呢,新手小白不太懂,想按着这个自己去做一下
举报

更多回帖

发帖
×
20
完善资料,
赚取积分