【STM32CubeMX项目】小时钟V1.0

news2025/1/9 0:59:31

前言

  基于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/1bT4G7U0jZbVvHSDShAjjfQ 提取码:mo81

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/515297.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用JS手动实现SementicUI的分页,解决页数过多的问题.

自己使用SemanticUI官网的分页组件&#xff0c; 但是有很多细节问题并没有解决方案&#xff0c;如果只是需要而分页&#xff0c;将页数渲染在分页组件上的话&#xff0c;那倒很简单&#xff0c;但是页面有很多的话&#xff0c;假如100页&#xff0c;那么整个tbody会随着tfoot…

C# PDF附件生成

最近项目上需要生成行业某证件&#xff0c;查阅了一下&#xff0c;大体有两个思路可以实现 1.图片格式 2.PDF格式 使用图片格式&#xff0c;GDI绘图的形式&#xff0c;GDI绘图相关库资料比较多&#xff0c;难度也还好&#xff0c;问题是生成的证不是很好看&#xff0c;看上去…

ESP32学习笔记 -- ESP32-S3使用自定义分区表

什么是分区表? 分区表(Partition Table),主要是用来对ESP32-S3外挂的SPI FLASH进行区域划分的一个表格,通过这个表格,可以根据多个不同的功能,将SPI FLASH划分为不同的区域,方便开发者存储数据。 分区表主要有两种格式,一种是方便开发人员进行更改的.csv格式,一种是…

【Spring框架全系列】初识Spring MVC(上)

&#x1f303;哈喽&#xff0c;大家好&#xff0c;我是小浪。接着上篇博客&#xff0c;我们学习了springboot配置日志文件&#xff0c;到此&#xff0c;那么springboot的内容也更新的差不多了&#xff0c;由于是初学阶段&#xff0c;所以不学习那么深入&#xff0c;那么我们学习…

数据结构:单链表增、删、查、改的实现

1.概念 链表是一种 物理存储结构上非连续 、非顺序的存储结构&#xff0c;数据元素的 逻辑顺序 是通过链表 中的 指针链接 次序实现的 。 2.形式 我们使用链表一般都是创建一个结构体。 typedef int SLTDataType; typedef struct SListNode {SLTDataType data;struct SListN…

USB2.0 HUB的MTT与STT

HUB&#xff1a;集线器 以前呀&#xff0c;我也不清楚MTT与STT的区别&#xff0c;记得有一次选型的时候&#xff0c;很懵逼&#xff0c;结果还是按照以前同事的方案去选了&#xff0c;到后面想起来才抽空整明白了。 所以今天就来说下USB HUB里面的MTT与STT&#xff0c;并在文…

【GO】31. websocket实现

一. 引入gin、gorilla websocket包 go get github.com/gin-gonic/gin go get github.com/gorilla/websocket 二. 服务端代码 package mainimport ("bytes""fmt""github.com/gin-gonic/gin""github.com/gorilla/websocket""log&…

从字节出来的测试总监,让我们用这份《测试用例规范》,再也没加班过。

经常看到无论是刚入职场的新人&#xff0c;还是工作了一段时间的老人&#xff0c;都会对编写测试用例感到困扰&#xff1f;例如&#xff1a; 固然&#xff0c;编写一份好的测试用例需要&#xff1a;充分的需求分析能力 理论及经验加持&#xff0c;作为测试职场摸爬打滚的老人&…

RobotStudio教程:ABB机器人TCP路径轨迹跟踪功能介绍与使用方法

目录 功能介绍 机器人工作站创建 TCP路径轨迹全局跟踪 基于事件管理器的TCP路径轨迹局部跟踪 基于Smart组件的TCP路径轨迹局部跟踪 仿真运行 功能介绍 干涉检查是虚拟仿真工作中非常重要的一个步骤&#xff0c;尤其是机器人工具与工件、工装夹具之间的碰撞干涉&#xff…

Java高阶数据结构 图的最短路径问题

图的最短路径问题&#xff01; 文章目录 Java高阶数据结构 & 图的最短路径问题1. Dijkstra算法【单源最短路径】1.1 Dijkstra算法证明1.2 Dijkstra算法代码实现1.3 堆优化的Dijkstra算法1.4 堆优化Dijkstra算法代码实现 2. Bellman-Ford算法【单源最短路径】2.1 BF算法证明…

常见的基础模块电路,你都能看懂吗?

文章开始前&#xff0c;先来考考大家~ 下面的五副电路图&#xff0c;你能看懂几个&#xff1f; 目录 01.电源电路 02.运算放大器电路 03.信号产生电路 04.信号处理电路 05.传感器及其应用电路 06.显示电路 TDA2030电路图 34063电路图 555电路 TDA2030电路图 三极管分立元…

音视频八股文(11)-- ffmpeg 音频重采样

1重采样 1.1 什么是重采样 所谓的重采样&#xff0c;就是改变⾳频的采样率、sample format、声道数等参数&#xff0c;使之按照我们期望的参数输出。 1.2 为什么要重采样 为什么要重采样&#xff1f;当然是原有的⾳频参数不满⾜我们的需求&#xff0c;⽐如在FFmpeg解码⾳频…

从头开始学习Python接口自动化测试:编写测试用例,执行测试以及生成测试报告

Python接口自动化测试详解 随着Web服务和移动应用不断增多&#xff0c;以及对API和微服务的需求不断增加&#xff0c;API已成为现代应用程序中必不可少的组件。自动化测试框架可以大大简化API测试的过程&#xff0c;并确保其正确性和稳定性。Python是一种非常流行的编程语言&a…

洛谷B2100 同行列对角线的格

同行列对角线的格 题目描述 输入三个自然数 N N N&#xff0c; i i i&#xff0c; j j j&#xff08; 1 ≤ i ≤ n 1 \le i \le n 1≤i≤n&#xff0c; 1 ≤ j ≤ n 1 \le j \le n 1≤j≤n&#xff09;&#xff0c;输出在一个 N N N \times N NN 格的棋盘中&#xff08;行…

西门子1200PLC如何在威纶通HMI上进行时间显示

先生成定时器DB&#xff0c;然后在引脚绑定变量&#xff0c;在西门子PLC中&#xff0c;DINT和TIME之间可以隐含转化。 第一种方法&#xff1a;触摸屏元件设置成DINT类型 数值元件资料格式为32-bit Signed&#xff0c;对应PLC中即为DINT类型。小数点以下没有位数。这是我们测试…

Android RecyclerView实现侧滑删除,附 Demo

距上次写博客有半年多了&#xff0c;回忆起来都觉得不可思议&#xff0c;中间也想憋俩大招&#xff0c;总是被耽误&#xff0c;这俩月忙完之后&#xff0c;终于空下来了&#xff0c;恰好新项目我和UI俩人商量一下&#xff0c;用MD来实现app。中间有个需求是RecyclerView中侧滑显…

ch06-Pytorch的正则化与归一化

ch06-Pytorch的正则化与归一化 0.引言1.weight decay 和 dropout1.1.Regularization1.2.Dropout 2.Normalization2.1.Batch Normalization2.2.Batch Normalization in PyTorch2.2.1.nn.BatchNorm1d()2.2.2.nn.BatchNorm2d()2.2.3.nn.BatchNorm3d() 2.3.其他常见的Normalization…

java servlet jsp 农产品价格信息搜集系统 python开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 jsp 农产品价格信息搜集系统 python是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助 系统采用 serlvetdaobean 模式开发 利用python 进行网站爬取 &#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开…

Redis常见命令有哪些?怎么使用?

一、概述&#xff1a; 在了解Redis命令之前&#xff0c;我们要先了解Redis的数据结构&#xff0c;Redis是NoSQL数据库&#xff0c;采用了json存储模式&#xff0c;比MySQL等关系数据库更易于扩展&#xff0c;拥有丰富的数据类型&#xff0c;分基本类型与特殊类型。基本类型包括…

【Linux】网络---->套接字编程(TCP)

套接字编程TCP TCP的编程流程TCP的接口TCP的代码&#xff08;单线程、多进程、多线程代码&#xff09;单线程多进程多线程 TCP的编程流程 TCP的编程流程&#xff1a;大致可以分为五个过程&#xff0c;分别是准备过程、连接建立过程、获取新连接过程、消息收发过程和断开过程。 …