stm32电子钟
基于stm32f103c8t6制作一个电子钟。
设计一个电子钟。计时最大值为23:59:59。
要求:(8分)
1、初始值为0:0:0,每秒加1,直到23:59:59,然后清0停止。
2、设计电子钟启动按键,启动后才开始计时;设计清零按键,可随时清0。
3、设计按键调整时间,时分秒均可调整,加法按键:分、秒加到59后清0,时加到23后清0;减法按键:分、秒减到0继续减则为59,时减到0继续减则为23。
4、设计一个初始闹钟时间为0:0:10,可使用串口设置闹钟时间,可响应新设置的闹钟,闹钟时蜂鸣器发声5秒提示。
5、增加光敏电阻,低于阈值发光。
一、仿真设计:
二、初始化:
1、定时器中断计时:配置时钟周期为1s,内部时钟,更新中断
2、外部中断设置:中断少时可忽略优先级设置
3、USART开启:用于闹钟设置
三、代码实现:
1、LCD初始化:chatgpt编写,添加lcd.c库
#include "lcd.h"
LCD_Init(); // 初始化LCD
LCD_Clear(); // 清屏
添加lcd.c文件
#include "gpio.h"
#include "stdio.h"
#define LCD_RS_PIN GPIO_PIN_8
#define LCD_EN_PIN GPIO_PIN_9
#define LCD_D4_PIN GPIO_PIN_4
#define LCD_D5_PIN GPIO_PIN_5
#define LCD_D6_PIN GPIO_PIN_6
#define LCD_D7_PIN GPIO_PIN_7
#define LCD_PORT GPIOB // 假设LCD接到GPIOB端口
// LCD延时函数
void LCD_Delay(uint32_t delay) {
HAL_Delay(delay);
}
// 发送4位数据
void LCD_Send4Bit(uint8_t data) {
HAL_GPIO_WritePin(LCD_PORT, LCD_D4_PIN, (data & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_PORT, LCD_D5_PIN, (data & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_PORT, LCD_D6_PIN, (data & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET);
HAL_GPIO_WritePin(LCD_PORT, LCD_D7_PIN, (data & 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET);
// 触发EN
HAL_GPIO_WritePin(LCD_PORT, LCD_EN_PIN, GPIO_PIN_SET);
LCD_Delay(1);
HAL_GPIO_WritePin(LCD_PORT, LCD_EN_PIN, GPIO_PIN_RESET);
LCD_Delay(1);
}
// 发送命令
void LCD_SendCommand(uint8_t cmd) {
HAL_GPIO_WritePin(LCD_PORT, LCD_RS_PIN, GPIO_PIN_RESET); // RS = 0 选择命令寄存器
LCD_Send4Bit(cmd >> 4); // 发送高4位
LCD_Send4Bit(cmd & 0x0F); // 发送低4位
}
// 发送数据
void LCD_SendData(uint8_t data) {
HAL_GPIO_WritePin(LCD_PORT, LCD_RS_PIN, GPIO_PIN_SET); // RS = 1 选择数据寄存器
LCD_Send4Bit(data >> 4); // 发送高4位
LCD_Send4Bit(data & 0x0F); // 发送低4位
}
// LCD初始化函数
void LCD_Init() {
LCD_Send4Bit(0x03);
LCD_Send4Bit(0x03);
LCD_Send4Bit(0x03);
LCD_Send4Bit(0x02); // 4位模式
LCD_SendCommand(0x28); // 4位模式,2行,5x7点阵
LCD_SendCommand(0x0C); // 显示开,光标关
LCD_SendCommand(0x06); // 自动右移
LCD_SendCommand(0x01); // 清屏
LCD_Delay(2);
}
// 清屏
void LCD_Clear() {
LCD_SendCommand(0x01); // 清屏
LCD_Delay(2);
}
// 设置光标位置
void LCD_SetCursor(uint8_t row, uint8_t col) {
uint8_t address = (row == 0) ? 0x80 + col : 0xC0 + col;
LCD_SendCommand(address);
}
// 显示字符串
void LCD_Print(char* str) {
while (*str) {
LCD_SendData(*str++);
}
}
void Display_clock(uint8_t hour,uint8_t minute,uint8_t second) {
char buffer[16];
// 格式化时间为 "HH:MM:SS"
snprintf(buffer, sizeof(buffer), "%02d:%02d:%02d", hour, minute, second);
// 在LCD第一行显示时间
LCD_SetCursor(0, 3); // 假设在第一行第4列显示
LCD_Print(buffer);
}
2、中断回调函数:stm32外部中断后会统一调用下图函数清除标志位,随后进入回调函数,从回调函数中判断中断端口,执行回调函数。(stm32cubemx生成虚函数weak,需用户自行编写)
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_PIN)
{
if(GPIO_PIN==GPIO_PIN_0)
{
increase_hour();
}
if(GPIO_PIN==GPIO_PIN_1)
{
decrease_hour();
}
if(GPIO_PIN==GPIO_PIN_2)
{
increase_minute();
}
if(GPIO_PIN==GPIO_PIN_3)
{
decrease_minute();
}
if(GPIO_PIN==GPIO_PIN_4)
{
increase_second();
}
if(GPIO_PIN==GPIO_PIN_5)
{
decrease_second();
}
if(GPIO_PIN==GPIO_PIN_7)
{
clear();
}
if(GPIO_PIN==GPIO_PIN_14)
{
reset();
}
}//外部中断,增加减少时间
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart) {
if (huart->Instance == USART1) {
int h, m, s;
sscanf((char*)rx_buffer, "%2d%2d%2d", &h, &m, &s);
if (h >= 0 && h < 24 && m >= 0 && m < 60 && s >= 0 && s < 60) {
alarm_hour = h;
alarm_minute = m;
alarm_second = s;
}
HAL_UART_Receive_IT(&huart1, (uint8_t *)rx_buffer, 6); // 重新启动串口接收
}
}//串口中断回调函数
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim == &htim1) //判断中断是否来自于定时器1
{
increase_second();
}
}//定时器中断回调
3、其余代码
volatile uint8_t second = 0, minute = 0, hour = 0,time=0,start=0;
#define LCD_RS_PIN GPIO_PIN_8
#define LCD_EN_PIN GPIO_PIN_9
#define LCD_D4_PIN GPIO_PIN_4
#define LCD_D5_PIN GPIO_PIN_5
#define LCD_D6_PIN GPIO_PIN_6
#define LCD_D7_PIN GPIO_PIN_7
#define LCD_PORT GPIOB // 假设LCD接到GPIOB端口
volatile uint8_t alarm_hour = 0, alarm_minute = 0, alarm_second = 10;
volatile uint8_t alarm_triggered = 1;
char rx_buffer[8]; // 用于接收串口输入数据
HAL_TIM_Base_Start_IT(&htim1);
LCD_Init(); // 初始化LCD
LCD_Clear(); // 清屏
HAL_UART_Receive_IT(&huart1, (uint8_t *)rx_buffer, 6); // 串口接收中断
void Check_Alarm();
HAL_TIM_PWM_Start(&htim2,TIM_CHANNEL_1);
while (1)//主函数
{
Display_clock(hour,minute,second);
Check_Alarm();
HAL_ADC_Start(&hadc1);
adc_value=HAL_ADC_GetValue(&hadc1);
if(adc_value>100)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0 , GPIO_PIN_SET);
}else{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0 , GPIO_PIN_RESET);
}
}
}
void increase_second()//秒增
{
start=HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_11);
if(time==0&&start==1)
{
second++;
if (second >= 60)
{
second = 0;
minute++;
if (minute >= 60)
{
minute = 0;
hour++;
if (hour >= 24)
{
time = 1;
hour = 0;
}
}
}
}
}
void decrease_second()//按键功能
{
second = (second == 0) ? 59 : second - 1; //
}
void increase_hour()
{
hour = (hour + 1) % 24; //
}
void decrease_hour()
{
hour = (hour == 0) ? 23 : hour - 1; //
}
void increase_minute()
{
minute = (minute + 1) % 60;//
}
void decrease_minute()
{
minute = (minute == 0) ? 59 : minute - 1; //
}
void reset()//重启
{
second=0;hour=0;minute=0;time=0;start=0;
}
void clear()//清零
{
second=0;hour=0;minute=0;
}
void Check_Alarm(void) //蜂鸣器
{
if (hour == alarm_hour && minute == alarm_minute && second == alarm_second)
{
alarm_triggered = 1; // 设置闹钟触发标志
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET); // 打开蜂鸣?
//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); // 关闭蜂鸣?
}
if((second-alarm_second+60*(minute-alarm_minute))>=5)
{
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET); // 关闭蜂鸣?
}
}
四、结尾
vscode与keli或者stm32cubemx可能注释文本格式不兼容,generate数次后文本会逐步乱码。若使用六位七段数码管,则利用视觉停留,先输出一位,并延时几毫秒后轮换下一位。
作者:2301_77140380