STM32CUBEMX 配置12脚3641BS以及串口显示RTC时间

最近在学习STM32F4系列的RTC时钟系统,同时也在学习4位7段数码管显示驱动。而市面上很多3641BS数码管都是集成了74HC595移位寄存器芯片的PCB板,故网上大多基于STM32驱动该型号数码管的驱动都是基于五引脚(VCC, DIO, RCLK, SCLK, GND)设计的。但我手头只有最原始的12引脚版本,因此在这篇博客里我会讲解最原始版本的,也当作是一个备忘录和对RTC时钟唤醒中断配置以及3641BS工作原理的认识吧。

1. 实验原理

如下图,四位七段数码管有12个引脚。除了显示"abcdefg"的7段和显示小数点的"h"之外,还有4个引脚D1、D2、D3和D4用作"位"引脚。当四个数码管的"位"引脚为高电平时,相应的数码管被点亮。
四个数码管的显示原理是连续扫描D1、D2、D3、D4,然后相应的八段管依次亮起。由于点亮速度很快,小于人眼分辨的时间差值极限,所以看起来就像四个数码管同时显示。

对于STM32实时时钟(RTC)来说,它是一个独立的BCD定时器/计数器,提供日历时钟、两个可编程报警中断和一个具有中断功能的可编程定期唤醒标志,还包含一个自动唤醒单元来管理低功耗模式。
两个32位的寄存器包含了秒、分、小时(12或24小时格式)、星期、日期、月份和年份的二进制十进制(BCD)格式。此外,亚秒值以二进制格式提供。
系统可以自动将每月的天数偏移到28、29(闰年)、30和31天。还可提供夏令时补偿。其他32位寄存器包含可编程的报警亚秒、秒、分钟、小时、星期和日期。此外,晶体精度的偏差可以通过数字校准功能进行补偿。在上电复位后,所有的RTC寄存器都受到保护,防止可能的异常写入。无论设备状态如何(工作模式、低功耗模式或重启),只要电源电压保持在工作范围内,RTC就不会停止工作。

 

2.实验材料

STM32F407ZG开发板一块
面包板及配套连接线(公对母)
四位共阳极数码管3641BS(最原始版本,不包含任何集成芯片)
4个220Ω限流电阻

3.实验电路连接图

 

数码管的a~h引脚分别接到单片机的PC0~PC7引脚,d0,d1,d2,d3引脚分别接到单片机的PF0~PF3引脚,并且在中间串联220Ω的限流电阻,以免永久损坏数码管LED。

4. CUBEMX配置以及代码编写

首先将A~H八端初始化为高电平,D0~D3初始化为低电平,每个引脚上下拉模式为浮空

 RCC系统时钟源的HSE, LSE都调为晶振源

 

打开RTC时钟源和日历,启用内部时钟唤醒,把时间调为24小时制,存储模式用二进制(这点很重要!!!等会读取时间的时候也要以二进制形式读取,否则数字会有偏差),设置好时分秒以及年月日、星期几,根据自己需要是否关闭夏令时,唤醒定时器的时钟输入设置为1Hz(即RTC_WAKEUPCLOCK_RTCCLK / 16),其他参数默认就好

 

 接下来配置唤醒中断以及优先级。在这里,我将中断优先级分组设置为2位抢占优先级、2位响应优先级,同时将唤醒中断激活并将抢占、响应优先级均设置为2,以保证不会干扰其他系统底层中断的运行。最后勾选生成中断请求处理函数和HAL调用处理函数。

 

为了在中断处理函数中通过printf输出日期和其他更详细的信息,需要启用串口。在SYS中将Debug模式调为Serial Wire,并且启用USART1,设置为异步传输模式,波特率115200,其他默认就好。

确认无误后生成基于HAL库的KEIL5 MDK工程文件

接下来到了实现关键代码部分的时候了! 

首先,为了保证RTC断电后时间不丢失、同时每次上电后时间日期不被重复设置,需要在RTC 后备寄存器中留下标记值,以标记是否为第一次设置时间。但后备寄存器需要启用写权限,于是在main()函数HAL_Init()初始化完成后插入:

HAL_PWR_EnableBkUpAccess();

然后在rtc.c文件中的MX_RTC_Init()函数初始化时间部分前后加上判断后备寄存器是否有标记的逻辑,若没有标记则初始化并写寄存器。

/* USER CODE BEGIN Check_RTC_BKUP */
if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) != 0x5050) { // Add this line
  /* USER CODE END Check_RTC_BKUP */
  sTime.Hours = 14;
  sTime.Minutes = 10;
  sTime.Seconds = 12;
  // ....

  /* USER CODE BEGIN RTC_Init 2 */
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0x5050); // Add this line
} // Add this line
  /* USER CODE END RTC_Init 2 */
  /** Enable the WakeUp
  */
  if (HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0, RTC_WAKEUPCLOCK_CK_SPRE_16BITS) != HAL_OK)
  {
    Error_Handler();
  }

添加中断回调处理函数

int finale = 0;

void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc) {
	char buf[40];
	RTC_TimeTypeDef RTC_TimeStruct;
  RTC_DateTypeDef RTC_DateStruct;
	
	HAL_RTC_GetTime(hrtc,&RTC_TimeStruct,RTC_FORMAT_BIN);
	sprintf((char*)buf,"Time: %02d:%02d:%02d",RTC_TimeStruct.Hours,RTC_TimeStruct.Minutes,RTC_TimeStruct.Seconds);
	printf("%s\r\n",buf);
	
	HAL_RTC_GetDate(hrtc,&RTC_DateStruct,RTC_FORMAT_BIN);
	sprintf((char*)buf,"Date: 20%02d-%02d-%02d  Day %d",RTC_DateStruct.Year,RTC_DateStruct.Month,RTC_DateStruct.Date,RTC_DateStruct.WeekDay);
	printf("%s\r\n",buf);
	
	finale = RTC_TimeStruct.Hours * 100 + RTC_TimeStruct.Minutes;
}

并且在头文件中添加显示小时和分钟的外部int值,供主函数while循环中扫描显示在数码管上

extern int finale;

最后在usart.c和usart.h中添加fputc重载函数,以保证将printf函数输出设备从控制台重定向到串口1

#include <stdio.h>
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
  HAL_UART_Transmit(&huart1,(uint8_t*)&ch, 1, 0xFFFF);
  return ch;
}

可能有同学就在这里问了:为什么我加了printf函数以后的程序编译烧写进板子,其系统不能启动,出现死机的情况?这是因为在嵌入式的编程中HAL库并没有对IO函数的底层实现(比如_sys_exit(), _sys_open(), _sys_close()这些函数),使得设备运行时会进入软件中断BAEB处,即进入了半主机模式,这时就需要__use_no_semihosting_swi这个声明,同时用空壳实现这些函数(dummy函数),使程序遇到这些文件操作函数时不停在此中断处。将以下代码粘贴进新建的源文件并加入工程即可:

/* USER CODE BEGIN 0 */
#include <time.h>

#pragma import(__use_no_semihosting_swi)  
#pragma import(_main_redirection)

const char __stdin_name[150];  
const char __stdout_name[150];  
const char __stderr_name[150];  
typedef int FILEHANDLE;  
 
void _sys_exit(int status)  
{  
    while(1);  
}  

FILEHANDLE _sys_open(const char *name, int openmode)  
{  
    return 0;  
}  

int _sys_close(FILEHANDLE fh)  
{  
    return 0;  
}  

int _sys_write(FILEHANDLE fh, const unsigned char *buf, unsigned len, int mode)  
{  
    return 0;  
}  

int _sys_read(FILEHANDLE fh, unsigned char*buf, unsigned len, int mode)  
{  
    return 0;  
}  

int _sys_istty(FILEHANDLE fh)  
{  
    return 0;  
}  

int _sys_seek(FILEHANDLE fh, long pos)  
{  
    return 0;  
}  

int _sys_ensure(FILEHANDLE fh)  
{  
    return 0;  
}  

long _sys_flen(FILEHANDLE fh)  
{  
    return 0;  
}  

int _sys_tmpnam(char *name, int fileno, unsigned maxlength)  
{  
    return 0;  
}  

void _ttywrch(int ch)  
{  
}

time_t time(time_t *t)  
{  
    return 0;  
}  
int remove(const char *filename)  
{  
    return 0;  
}  

char *_sys_command_string(char *cmd, int len)  
{  
    return 0;  
}  

clock_t clock(void)  
{  
    return 0;  
}

/* USER CODE END 0 */

接下来的重中之重,就是实现四位七段数码管的驱动程序了。首先打出码表和对应的引脚数组

int dat[10][8] = {
    // Last bit is reserved for H (i.e. point)
    {0, 0, 0, 0, 0, 0, 1, 1}, //0
    {1, 0, 0, 1, 1, 1, 1, 1}, //1
    {0, 0, 1, 0, 0, 1, 0, 1}, //2
    {0, 0, 0, 0, 1, 1, 0, 1}, //3
    {1, 0, 0, 1, 1, 0, 0, 1}, //4
    {0, 1, 0, 0, 1, 0, 0, 1}, //5
    {0, 1, 0, 0, 0, 0, 0, 1}, //6
    {0, 0, 0, 1, 1, 1, 1, 1}, //7
    {0, 0, 0, 0, 0, 0, 0, 1}, //8
    {0, 0, 0, 0, 1, 0, 0, 1} //9
};

uint16_t dbit[4] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3}; //使能位引脚(D0~D3), 高电平有效
uint16_t tbit[8] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3, GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7}; //控制点亮哪根数码管(a~h), 低电平有效

然后是显示单个数字

void DisplayInt(int n, int nb, int punto) {
    int i = 0;
    if(n < 10) {
        for(i = 0; i < 8; i++) {
            HAL_GPIO_WritePin(GPIOC, tbit[i], 
					(nb == 1 && i == 7) ? (punto ? 0 : 1) : dat[n][i]);
        }
    }
}

接下来是四位数字同时显示,punto形参表示是否显示小时和分钟之间的小数点

void delay_us(uint32_t nus) {
    uint32_t ticks;
    uint32_t told,tnow,tcnt=0;
    uint32_t reload=SysTick->LOAD;
	uint32_t freq = HAL_RCC_GetSysClockFreq();
    ticks=nus * freq / 1e6;
    told=SysTick->VAL;
    while(1) {
        tnow=SysTick->VAL;
        if(tnow!=told) {
            if(tnow<told)tcnt+=told-tnow;
            else tcnt+=reload-tnow+told;
            told=tnow;
            if(tcnt>=ticks) break;
        }
    }
}

void DisplayNumber(int x, int punto) {
    int nb = 0;
    int display_arr[5] = {0};
    int bit_base = 1000;
    for(nb = 0; nb < 4; nb++) {
        if(x / bit_base != 0) {
            display_arr[nb] = x/bit_base;
            x = x%bit_base;
        } else {
            display_arr[nb] = 0;
        }
        bit_base = bit_base / 10;
    }
    for(nb = 0; nb < 4; nb++) {
        DisplayInt(display_arr[nb], nb, punto);
        HAL_GPIO_WritePin(GPIOF, dbit[nb], GPIO_PIN_SET); // 4位数字轮流显示200us后熄灭,形成循环,小于人眼分辨时间,看上去就是连续的
        delay_us(200);
        HAL_GPIO_WritePin(GPIOF, dbit[nb], GPIO_PIN_RESET);
    }
}

最后是显示小时和分中的总函数,要保持一直显示而不熄灭,必须将DisplayNumber()置入该函数的定时循环之中,单位是毫秒,以下是代码:

void DisHHMM(int x, uint32_t ms, int punto) {
  uint32_t tickstart = HAL_GetTick();
  uint32_t wait = ms;

  /* Add a freq to guarantee minimum wait */
  if (wait < HAL_MAX_DELAY)
  {
    wait += (uint32_t)(uwTickFreq);
  }

  while((HAL_GetTick() - tickstart) < wait)
  {
		DisplayNumber(x, punto);
  }
}

至此,基础函数部分已经全部实现完毕,可以在main()函数中测试了。

int main(void)
{
  // ...
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_RTC_Init();
  
  // ...

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		if(finale != 0) {
			DisHHMM(finale, 500, 1);
			DisHHMM(finale, 500, 0);
		}
  }
  /* USER CODE END 3 */
}

5. 实验结果

 

6. 参考文章 

1. stm32半主机模式 - chenguan - 博客园

2. How to drive a 7 segment display directly on Raspberry Pi in Python – RasPi.TV

版权声明:本文为CSDN博主「Las Palomas」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012783994/article/details/122716659

最近在学习STM32F4系列的RTC时钟系统,同时也在学习4位7段数码管显示驱动。而市面上很多3641BS数码管都是集成了74HC595移位寄存器芯片的PCB板,故网上大多基于STM32驱动该型号数码管的驱动都是基于五引脚(VCC, DIO, RCLK, SCLK, GND)设计的。但我手头只有最原始的12引脚版本,因此在这篇博客里我会讲解最原始版本的,也当作是一个备忘录和对RTC时钟唤醒中断配置以及3641BS工作原理的认识吧。

1. 实验原理

如下图,四位七段数码管有12个引脚。除了显示"abcdefg"的7段和显示小数点的"h"之外,还有4个引脚D1、D2、D3和D4用作"位"引脚。当四个数码管的"位"引脚为高电平时,相应的数码管被点亮。
四个数码管的显示原理是连续扫描D1、D2、D3、D4,然后相应的八段管依次亮起。由于点亮速度很快,小于人眼分辨的时间差值极限,所以看起来就像四个数码管同时显示。

对于STM32实时时钟(RTC)来说,它是一个独立的BCD定时器/计数器,提供日历时钟、两个可编程报警中断和一个具有中断功能的可编程定期唤醒标志,还包含一个自动唤醒单元来管理低功耗模式。
两个32位的寄存器包含了秒、分、小时(12或24小时格式)、星期、日期、月份和年份的二进制十进制(BCD)格式。此外,亚秒值以二进制格式提供。
系统可以自动将每月的天数偏移到28、29(闰年)、30和31天。还可提供夏令时补偿。其他32位寄存器包含可编程的报警亚秒、秒、分钟、小时、星期和日期。此外,晶体精度的偏差可以通过数字校准功能进行补偿。在上电复位后,所有的RTC寄存器都受到保护,防止可能的异常写入。无论设备状态如何(工作模式、低功耗模式或重启),只要电源电压保持在工作范围内,RTC就不会停止工作。

 

2.实验材料

STM32F407ZG开发板一块
面包板及配套连接线(公对母)
四位共阳极数码管3641BS(最原始版本,不包含任何集成芯片)
4个220Ω限流电阻

3.实验电路连接图

 

数码管的a~h引脚分别接到单片机的PC0~PC7引脚,d0,d1,d2,d3引脚分别接到单片机的PF0~PF3引脚,并且在中间串联220Ω的限流电阻,以免永久损坏数码管LED。

4. CUBEMX配置以及代码编写

首先将A~H八端初始化为高电平,D0~D3初始化为低电平,每个引脚上下拉模式为浮空

 RCC系统时钟源的HSE, LSE都调为晶振源

 

打开RTC时钟源和日历,启用内部时钟唤醒,把时间调为24小时制,存储模式用二进制(这点很重要!!!等会读取时间的时候也要以二进制形式读取,否则数字会有偏差),设置好时分秒以及年月日、星期几,根据自己需要是否关闭夏令时,唤醒定时器的时钟输入设置为1Hz(即RTC_WAKEUPCLOCK_RTCCLK / 16),其他参数默认就好

 

 接下来配置唤醒中断以及优先级。在这里,我将中断优先级分组设置为2位抢占优先级、2位响应优先级,同时将唤醒中断激活并将抢占、响应优先级均设置为2,以保证不会干扰其他系统底层中断的运行。最后勾选生成中断请求处理函数和HAL调用处理函数。

 

为了在中断处理函数中通过printf输出日期和其他更详细的信息,需要启用串口。在SYS中将Debug模式调为Serial Wire,并且启用USART1,设置为异步传输模式,波特率115200,其他默认就好。

确认无误后生成基于HAL库的KEIL5 MDK工程文件

接下来到了实现关键代码部分的时候了! 

首先,为了保证RTC断电后时间不丢失、同时每次上电后时间日期不被重复设置,需要在RTC 后备寄存器中留下标记值,以标记是否为第一次设置时间。但后备寄存器需要启用写权限,于是在main()函数HAL_Init()初始化完成后插入:

HAL_PWR_EnableBkUpAccess();

然后在rtc.c文件中的MX_RTC_Init()函数初始化时间部分前后加上判断后备寄存器是否有标记的逻辑,若没有标记则初始化并写寄存器。

/* USER CODE BEGIN Check_RTC_BKUP */
if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0) != 0x5050) { // Add this line
  /* USER CODE END Check_RTC_BKUP */
  sTime.Hours = 14;
  sTime.Minutes = 10;
  sTime.Seconds = 12;
  // ....

  /* USER CODE BEGIN RTC_Init 2 */
	HAL_RTCEx_BKUPWrite(&hrtc, RTC_BKP_DR0, 0x5050); // Add this line
} // Add this line
  /* USER CODE END RTC_Init 2 */
  /** Enable the WakeUp
  */
  if (HAL_RTCEx_SetWakeUpTimer_IT(&hrtc, 0, RTC_WAKEUPCLOCK_CK_SPRE_16BITS) != HAL_OK)
  {
    Error_Handler();
  }

添加中断回调处理函数

int finale = 0;

void HAL_RTCEx_WakeUpTimerEventCallback(RTC_HandleTypeDef *hrtc) {
	char buf[40];
	RTC_TimeTypeDef RTC_TimeStruct;
  RTC_DateTypeDef RTC_DateStruct;
	
	HAL_RTC_GetTime(hrtc,&RTC_TimeStruct,RTC_FORMAT_BIN);
	sprintf((char*)buf,"Time: %02d:%02d:%02d",RTC_TimeStruct.Hours,RTC_TimeStruct.Minutes,RTC_TimeStruct.Seconds);
	printf("%s\r\n",buf);
	
	HAL_RTC_GetDate(hrtc,&RTC_DateStruct,RTC_FORMAT_BIN);
	sprintf((char*)buf,"Date: 20%02d-%02d-%02d  Day %d",RTC_DateStruct.Year,RTC_DateStruct.Month,RTC_DateStruct.Date,RTC_DateStruct.WeekDay);
	printf("%s\r\n",buf);
	
	finale = RTC_TimeStruct.Hours * 100 + RTC_TimeStruct.Minutes;
}

并且在头文件中添加显示小时和分钟的外部int值,供主函数while循环中扫描显示在数码管上

extern int finale;

最后在usart.c和usart.h中添加fputc重载函数,以保证将printf函数输出设备从控制台重定向到串口1

#include <stdio.h>
#ifdef __GNUC__
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
PUTCHAR_PROTOTYPE
{
  HAL_UART_Transmit(&huart1,(uint8_t*)&ch, 1, 0xFFFF);
  return ch;
}

可能有同学就在这里问了:为什么我加了printf函数以后的程序编译烧写进板子,其系统不能启动,出现死机的情况?这是因为在嵌入式的编程中HAL库并没有对IO函数的底层实现(比如_sys_exit(), _sys_open(), _sys_close()这些函数),使得设备运行时会进入软件中断BAEB处,即进入了半主机模式,这时就需要__use_no_semihosting_swi这个声明,同时用空壳实现这些函数(dummy函数),使程序遇到这些文件操作函数时不停在此中断处。将以下代码粘贴进新建的源文件并加入工程即可:

/* USER CODE BEGIN 0 */
#include <time.h>

#pragma import(__use_no_semihosting_swi)  
#pragma import(_main_redirection)

const char __stdin_name[150];  
const char __stdout_name[150];  
const char __stderr_name[150];  
typedef int FILEHANDLE;  
 
void _sys_exit(int status)  
{  
    while(1);  
}  

FILEHANDLE _sys_open(const char *name, int openmode)  
{  
    return 0;  
}  

int _sys_close(FILEHANDLE fh)  
{  
    return 0;  
}  

int _sys_write(FILEHANDLE fh, const unsigned char *buf, unsigned len, int mode)  
{  
    return 0;  
}  

int _sys_read(FILEHANDLE fh, unsigned char*buf, unsigned len, int mode)  
{  
    return 0;  
}  

int _sys_istty(FILEHANDLE fh)  
{  
    return 0;  
}  

int _sys_seek(FILEHANDLE fh, long pos)  
{  
    return 0;  
}  

int _sys_ensure(FILEHANDLE fh)  
{  
    return 0;  
}  

long _sys_flen(FILEHANDLE fh)  
{  
    return 0;  
}  

int _sys_tmpnam(char *name, int fileno, unsigned maxlength)  
{  
    return 0;  
}  

void _ttywrch(int ch)  
{  
}

time_t time(time_t *t)  
{  
    return 0;  
}  
int remove(const char *filename)  
{  
    return 0;  
}  

char *_sys_command_string(char *cmd, int len)  
{  
    return 0;  
}  

clock_t clock(void)  
{  
    return 0;  
}

/* USER CODE END 0 */

接下来的重中之重,就是实现四位七段数码管的驱动程序了。首先打出码表和对应的引脚数组

int dat[10][8] = {
    // Last bit is reserved for H (i.e. point)
    {0, 0, 0, 0, 0, 0, 1, 1}, //0
    {1, 0, 0, 1, 1, 1, 1, 1}, //1
    {0, 0, 1, 0, 0, 1, 0, 1}, //2
    {0, 0, 0, 0, 1, 1, 0, 1}, //3
    {1, 0, 0, 1, 1, 0, 0, 1}, //4
    {0, 1, 0, 0, 1, 0, 0, 1}, //5
    {0, 1, 0, 0, 0, 0, 0, 1}, //6
    {0, 0, 0, 1, 1, 1, 1, 1}, //7
    {0, 0, 0, 0, 0, 0, 0, 1}, //8
    {0, 0, 0, 0, 1, 0, 0, 1} //9
};

uint16_t dbit[4] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3}; //使能位引脚(D0~D3), 高电平有效
uint16_t tbit[8] = {GPIO_PIN_0, GPIO_PIN_1, GPIO_PIN_2, GPIO_PIN_3, GPIO_PIN_4, GPIO_PIN_5, GPIO_PIN_6, GPIO_PIN_7}; //控制点亮哪根数码管(a~h), 低电平有效

然后是显示单个数字

void DisplayInt(int n, int nb, int punto) {
    int i = 0;
    if(n < 10) {
        for(i = 0; i < 8; i++) {
            HAL_GPIO_WritePin(GPIOC, tbit[i], 
					(nb == 1 && i == 7) ? (punto ? 0 : 1) : dat[n][i]);
        }
    }
}

接下来是四位数字同时显示,punto形参表示是否显示小时和分钟之间的小数点

void delay_us(uint32_t nus) {
    uint32_t ticks;
    uint32_t told,tnow,tcnt=0;
    uint32_t reload=SysTick->LOAD;
	uint32_t freq = HAL_RCC_GetSysClockFreq();
    ticks=nus * freq / 1e6;
    told=SysTick->VAL;
    while(1) {
        tnow=SysTick->VAL;
        if(tnow!=told) {
            if(tnow<told)tcnt+=told-tnow;
            else tcnt+=reload-tnow+told;
            told=tnow;
            if(tcnt>=ticks) break;
        }
    }
}

void DisplayNumber(int x, int punto) {
    int nb = 0;
    int display_arr[5] = {0};
    int bit_base = 1000;
    for(nb = 0; nb < 4; nb++) {
        if(x / bit_base != 0) {
            display_arr[nb] = x/bit_base;
            x = x%bit_base;
        } else {
            display_arr[nb] = 0;
        }
        bit_base = bit_base / 10;
    }
    for(nb = 0; nb < 4; nb++) {
        DisplayInt(display_arr[nb], nb, punto);
        HAL_GPIO_WritePin(GPIOF, dbit[nb], GPIO_PIN_SET); // 4位数字轮流显示200us后熄灭,形成循环,小于人眼分辨时间,看上去就是连续的
        delay_us(200);
        HAL_GPIO_WritePin(GPIOF, dbit[nb], GPIO_PIN_RESET);
    }
}

最后是显示小时和分中的总函数,要保持一直显示而不熄灭,必须将DisplayNumber()置入该函数的定时循环之中,单位是毫秒,以下是代码:

void DisHHMM(int x, uint32_t ms, int punto) {
  uint32_t tickstart = HAL_GetTick();
  uint32_t wait = ms;

  /* Add a freq to guarantee minimum wait */
  if (wait < HAL_MAX_DELAY)
  {
    wait += (uint32_t)(uwTickFreq);
  }

  while((HAL_GetTick() - tickstart) < wait)
  {
		DisplayNumber(x, punto);
  }
}

至此,基础函数部分已经全部实现完毕,可以在main()函数中测试了。

int main(void)
{
  // ...
  MX_GPIO_Init();
  MX_USART1_UART_Init();
  MX_RTC_Init();
  
  // ...

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
		if(finale != 0) {
			DisHHMM(finale, 500, 1);
			DisHHMM(finale, 500, 0);
		}
  }
  /* USER CODE END 3 */
}

5. 实验结果

 

6. 参考文章 

1. stm32半主机模式 - chenguan - 博客园

2. How to drive a 7 segment display directly on Raspberry Pi in Python – RasPi.TV

版权声明:本文为CSDN博主「Las Palomas」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/u012783994/article/details/122716659

生成海报
点赞 0

Las Palomas

我还没有学会写个人说明!

暂无评论

发表评论

相关推荐

STM32CUBEMX 配置12脚3641BS以及串口显示RTC时间

最近在学习STM32F4系列的RTC时钟系统,同时也在学习4位7段数码管显示驱动。而市面上很多3641BS数码管都是集成了74HC595移位寄存器芯片的PCB板,故网上大多基于STM32驱动该型号数码管的驱动都是基于

STM32F4最小系统硬件设计

对于硬件工程师来讲,想要入门STM32相关的开发,我想除了深入阅读一下STM32的数据手册外,最实用且有效的方法就是自己实际做一个STM32的最小系统板了。本文将以一个小的STM32F427VG的电路最

HAL 层简介

简介: HAL层又称硬件抽象层,HAL层在Android体系中有着深远的意义,因为Android究竟是完 全开源还是完全不开源的秘密就在这一层·Google将硬件厂商的驱动程序放在这一层&#xff0c