前言
基于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