【STM32CubeMX项目】小时钟V1.0

前言

  基于STM32CubeMx+STM32F103C6T6编写时钟功能。现在以及能实现时钟的简单功能,做文记录下。还有很多可以改进的地方,也算留下的基础版本下来备份吧。我愿称之为V1.0版本。可供学习参考。

实物

引脚接线:
OLED STM32F103
SCL --> PB8
SDA --> PB9
串口(TTL)
Tx --> PA10
Rx --> PA9

上电演示:

  • 串口设置时间演示

    目前只设置两种指令SetTime和SetDate,使用方法串口发送(波特率为115200):

    SetTime=123456 (时=12 分=34 秒=56) SetDate=230511 (年=23 月=05 日=11)

    在这里插入图片描述
    在这里插入图片描述

  • 部分程序

    /* USER CODE BEGIN Header */
    /**
      ******************************************************************************
      * @file           : main.c
      * @brief          : Main program body
      ******************************************************************************
      * @attention
      *
      * Copyright (c) 2023 STMicroelectronics.
      * All rights reserved.
      *
      * This software is licensed under terms that can be found in the LICENSE file
      * in the root directory of this software component.
      * If no LICENSE file comes with this software, it is provided AS-IS.
      *
      ******************************************************************************
      */
    /* USER CODE END Header */
    /* Includes ------------------------------------------------------------------*/
    #include "main.h"
    #include "rtc.h"
    #include "usart.h" 
    #include "gpio.h"
    
    /* Private includes ----------------------------------------------------------*/
    /* USER CODE BEGIN Includes */
    #include <string.h>
    #include "oled.h"
    #include "bmp.h"
    #include "oledpage.h"
    /* USER CODE END Includes */
    
    /* Private typedef -----------------------------------------------------------*/
    /* USER CODE BEGIN PTD */
    unsigned char*  UART_Char;
    unsigned char UART_Data[16];
    unsigned char UART_Num=0;
    RTC_TimeTypeDef TimeData;		//RTC时间结构定义
    RTC_DateTypeDef TimeDate;		//RTC日期结构定义
    /* USER CODE END PTD */
    
    /* Private define ------------------------------------------------------------*/
    /* USER CODE BEGIN PD */
    //BCD码转10进制码
    uint8_t bcd_to_dec(uint8_t bcd_value)
    {
        uint8_t dec_value = 0;
        uint8_t high_nibble = (uint8_t)(bcd_value >> 4);
        uint8_t low_nibble = (uint8_t)(bcd_value & 0x0F);
        
        dec_value = (high_nibble * 10) + low_nibble;
        
        return dec_value;
    }
    //将10进制数转换为BCD码
    uint32_t dec_to_bcd(uint32_t dec)
    {
        uint32_t bcd = 0;
        uint32_t shift = 0;
        while (dec > 0) {
            bcd |= (dec % 10) << shift;
            dec /= 10;
            shift += 4;
        }
        return bcd;
    }
    //字符转整型
    uint32_t char_to_int(char str)
    {
    	char Num=str-'0';
    	return Num;
    }
    //字符转整型
    uint32_t char2_to_int(char strH,char strL)
    {
    	int Num=char_to_int(strH)*10+char_to_int(strL);
    	return Num;
    }
    //数字字符转显示
    void OLED_Num24(char Num)
    {
    	
    }
    /* USER CODE END PD */
    
    /* Private macro -------------------------------------------------------------*/
    /* USER CODE BEGIN PM */
    
    /* USER CODE END PM */
    
    /* Private variables ---------------------------------------------------------*/
    
    /* USER CODE BEGIN PV */
    
    /* USER CODE END PV */
    
    /* Private function prototypes -----------------------------------------------*/
    void SystemClock_Config(void);
    /* USER CODE BEGIN PFP */
    void TIME_SetTime(char Hours,char Minutes,char Seconds);
    
    void OLEDPage0_Mian()
    {
    	//时间控制页
    	char OLED_TimeX=18;
    	char OLED_TimeY=2;
    	OLED_DrawBMP(OLED_TimeX,OLED_TimeY,OLED_TimeX+16,OLED_TimeY+3,BMP_NUM[bcd_to_dec(TimeData.Hours)/10]);OLED_TimeX+=16;
    	OLED_DrawBMP(OLED_TimeX,OLED_TimeY,OLED_TimeX+16,OLED_TimeY+3,BMP_NUM[bcd_to_dec(TimeData.Hours)%10]);OLED_TimeX+=16;
    	OLED_DrawBMP(OLED_TimeX,OLED_TimeY,OLED_TimeX+8,OLED_TimeY+3,BMP_NUMFH);OLED_TimeX+=8;
    	OLED_DrawBMP(OLED_TimeX,OLED_TimeY,OLED_TimeX+16,OLED_TimeY+3,BMP_NUM[TimeData.Minutes/10]);OLED_TimeX+=16;
    	OLED_DrawBMP(OLED_TimeX,OLED_TimeY,OLED_TimeX+16,OLED_TimeY+3,BMP_NUM[TimeData.Minutes%10]);OLED_TimeX+=20;
    	OLED_ShowNum(OLED_TimeX,OLED_TimeY+1,bcd_to_dec(TimeData.Seconds),2,16);
    	//日期控制页
    	char OLED_DateX=12;
    	char OLED_DateY=5;
    	char OLED_DateSize=16;
    	OLED_ShowNum(OLED_DateX,OLED_DateY,20,2,OLED_DateSize);OLED_DateX+=OLED_DateSize;
    	OLED_ShowNum(OLED_DateX,OLED_DateY,bcd_to_dec(TimeDate.Year),2,OLED_DateSize);OLED_DateX+=OLED_DateSize;
    	OLED_ShowChar(OLED_DateX,OLED_DateY,'-',OLED_DateSize);OLED_DateX+=OLED_DateSize/2;
    	OLED_ShowNum(OLED_DateX,OLED_DateY,bcd_to_dec(TimeDate.Month),2,OLED_DateSize);OLED_DateX+=OLED_DateSize;
    	OLED_ShowChar(OLED_DateX,OLED_DateY,'-',OLED_DateSize);OLED_DateX+=OLED_DateSize/2;
    	OLED_ShowNum(OLED_DateX,OLED_DateY,bcd_to_dec(TimeDate.Date),2,OLED_DateSize);OLED_DateX+=OLED_DateSize;
    	OLED_DateX+=OLED_DateSize/2;//留空
    	OLED_ShowNum(OLED_DateX,OLED_DateY,bcd_to_dec(TimeDate.WeekDay),2,OLED_DateSize);
    }
    /* USER CODE END PFP */
    
    /* Private user code ---------------------------------------------------------*/
    /* USER CODE BEGIN 0 */
    void USART_Events(unsigned char* UART_Data);
    void TIME_SetDate(char Year,char Month,char Date);
    //判断是否全为空
    int USART_IsAllNull(unsigned char *str, int len)
    {
        for (int i = 0; i < len; i++) {
            if (*(str + i) != ' ') {
                return 0;  //不全为空
            }
        }
        return 1;  //全为空
    }
    //串口监听事件
    void USART_Listen()
    {
    	//接收管理
    	HAL_UART_Receive(&huart1,(uint8_t *)UART_Data,sizeof(UART_Data),200);
    	if(USART_IsAllNull(UART_Data,sizeof(UART_Data))==0)
    	{
    		USART_Events(UART_Data);
    		//HAL_UART_Transmit(&huart1,UART_Data,6,100);
    		HAL_UART_Transmit(&huart1,(uint8_t *)"\r\n",2,10);
    	}
    	memset(UART_Data,' ', sizeof(UART_Data));
    }
    //串口监听到数据---触发事件
    void USART_Events(unsigned char* UART_Data)
    {
    	char InsMode=0;
    	char* strTime="SetTime=";	//指令1:配置时间
    	char* strDate="SetDate=";	//指令2:配置日期
    	//判断指令模式
    	if(strncmp(UART_Data, strTime, 8)==0)
    	{
    		InsMode=1;
    	}
    	else if(strncmp(UART_Data, strDate, 8)==0)
    	{
    		InsMode=2;
    	}
    	//执行行动
    	char State;	//状态接收
    	switch(InsMode)
    	{
    		case 1:
    			//设置时间(8+2*3=14)
    			TIME_SetTime(char2_to_int(UART_Data[8],UART_Data[9]),char2_to_int(UART_Data[10],UART_Data[11]),char2_to_int(UART_Data[12],UART_Data[13]));
    			HAL_UART_Transmit(&huart1,(uint8_t *)"SetTime ",8,10);
    		break;
    		case 2:
    			//设置日期
    			TIME_SetDate(char2_to_int(UART_Data[8],UART_Data[9]),char2_to_int(UART_Data[10],UART_Data[11]),char2_to_int(UART_Data[12],UART_Data[13]));
    			HAL_UART_Transmit(&huart1,(uint8_t *)"SetDate ",8,10);
    		break;
    	}
    	//提示行动
    	if(InsMode!=0)
    	{
    		HAL_UART_Transmit(&huart1,(uint8_t *)"OK",2,100);
    	}
    	else
    	{
    		HAL_UART_Transmit(&huart1,(uint8_t *)"NO",2,100);
    	}
    	
    }
    //设置时间
    //参数:时 分 秒
    void TIME_SetTime(char Hours,char Minutes,char Seconds)
    {
    	TimeData.Hours=dec_to_bcd(Hours);
    	TimeData.Minutes=dec_to_bcd(Minutes);
    	TimeData.Seconds=dec_to_bcd(Seconds);
    	HAL_RTC_SetTime(&hrtc, &TimeData, RTC_FORMAT_BCD);
    }
    //设置时间
    //参数:年 月 日 	
    //星期自动配了
    void TIME_SetDate(char Year,char Month,char Date)
    {
    	TimeDate.Year=dec_to_bcd(Year);
    	TimeDate.Month=dec_to_bcd(Month);
    	//TimeDate.WeekDay=dec_to_bcd(WeekDay);//char WeekDay
    	TimeDate.Date=dec_to_bcd(Date);
    	HAL_RTC_SetDate(&hrtc, &TimeDate, RTC_FORMAT_BCD);
    }
    /* USER CODE END 0 */
    
    /**
      * @brief  The application entry point.
      * @retval int
      */
    int main(void)
    {
      /* USER CODE BEGIN 1 */
    
      /* USER CODE END 1 */
    
      /* MCU Configuration--------------------------------------------------------*/
    
      /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
      HAL_Init();
    
      /* USER CODE BEGIN Init */
    
      /* USER CODE END Init */
    
      /* Configure the system clock */
      SystemClock_Config();
    
      /* USER CODE BEGIN SysInit */
    
      /* USER CODE END SysInit */
    
      /* Initialize all configured peripherals */
      MX_GPIO_Init();
      MX_RTC_Init();
      MX_USART1_UART_Init();
      /* USER CODE BEGIN 2 */
      OLED_Init();
      OLED_Clear();
      /* USER CODE END 2 */
    
      /* Infinite loop */
      /* USER CODE BEGIN WHILE */
      OLEDPageArray_Set(OLEDPage0_Mian,0);
      //TIME_SetTime(0,0,0);
      //TIME_SetDate(22,10,8);
      while (1)
      {
    	HAL_RTC_GetTime(&hrtc, &TimeData, RTC_FORMAT_BCD);	//RTC时间获取
    	HAL_RTC_GetDate(&hrtc, &TimeDate, RTC_FORMAT_BCD);	//RTC日期获取
    	OLEDPageShow(0);	//OLED页数显示
    	USART_Listen();		//串口监听
        /* USER CODE END WHILE */
    
        /* USER CODE BEGIN 3 */
      }
      /* USER CODE END 3 */
    }
    
    /**
      * @brief System Clock Configuration
      * @retval None
      */
    void SystemClock_Config(void)
    {
      RCC_OscInitTypeDef RCC_OscInitStruct = {0};
      RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
      RCC_PeriphCLKInitTypeDef PeriphClkInit = {0};
    
      /** Initializes the RCC Oscillators according to the specified parameters
      * in the RCC_OscInitTypeDef structure.
      */
      RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE;
      RCC_OscInitStruct.HSEState = RCC_HSE_ON;
      RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
      RCC_OscInitStruct.HSIState = RCC_HSI_ON;
      RCC_OscInitStruct.LSIState = RCC_LSI_ON;
      RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
      RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
      RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
      if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
      {
        Error_Handler();
      }
    
      /** Initializes the CPU, AHB and APB buses clocks
      */
      RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                                  |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
      RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
      RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
      RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
      RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
    
      if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
      {
        Error_Handler();
      }
      PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_RTC;
      PeriphClkInit.RTCClockSelection = RCC_RTCCLKSOURCE_LSI;
      if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
      {
        Error_Handler();
      }
    }
    
    /* USER CODE BEGIN 4 */
    
    /* USER CODE END 4 */
    
    /**
      * @brief  This function is executed in case of error occurrence.
      * @retval None
      */
    void Error_Handler(void)
    {
      /* USER CODE BEGIN Error_Handler_Debug */
      /* User can add his own implementation to report the HAL error return state */
      __disable_irq();
      while (1)
      {
      }
      /* USER CODE END Error_Handler_Debug */
    }
    
    #ifdef  USE_FULL_ASSERT
    /**
      * @brief  Reports the name of the source file and the source line number
      *         where the assert_param error has occurred.
      * @param  file: pointer to the source file name
      * @param  line: assert_param error line source number
      * @retval None
      */
    void assert_failed(uint8_t *file, uint32_t line)
    {
      /* USER CODE BEGIN 6 */
      /* User can add his own implementation to report the file name and line number,
         ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
      /* USER CODE END 6 */
    }
    #endif /* USE_FULL_ASSERT */
    
    

功能

现阶段已有的功能:

  • 串口交互实现日期时间的修改
  • OLED实时显示时间日期

不足

  • 功能还有很多可以加的。
  • 时间显示是16x24的BMP图,现在绘制的还比较丑。
  • 目前只能使用内部低速时钟(40kHz)为RTC时钟源,外部的低速时钟(32.768kHz)好像启动不了。

工程

链接:https://pan.baidu.com/s/1qeHCLx7T8On5X6BjJKt5kw  提取码:3xpm

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值