嵌入式的实验求具体工程

proteus + keil +stm32cubemx

二、实验内容:

  1. 利用Proteus实现一个闹钟,闹钟在OLED上显示的时间仅有时分秒,OLED上显示两个时间,上面是两行是运行时间: “Now Time:
    05:07:00”,
    下面两行显示闹钟设置时间:“Set Time:
    05:08:00”
    运行时间==闹钟时间,OLED显示警告信:
    “Alarm!”
    然后黑屏,再显示上面的提醒信息,如此循环四至五次,时间间隔为1秒,然后继续显示运行时间和设置时间。
  2. 利用通用定时器的中断,实现运行时间的改变,并在OLED中显示时间信息。
  3. 通过按键更改当前时间和设置闹钟。
    三、实验设备:
  4. STM32F1教学实验板、Proteus;
  5. PC操作系统WINDOWS,Keil MDK集成开发环境,STM32CubeMX。
    四、实验步骤:
  6. 连线:将教学实验板的电源线连接好,并检测J-link仿真器与试验箱CPU板和电脑连接好;
  7. 读懂原理图,本次试验使用电路图中的KEY2、KEY3、KEY4三个按键,它们分别接PA1、PA2、PA3。在实验板上三个按键标注为KEY1、KEY2、KEY3,连接的IO口不变。
  8. 完成按键的初始化,初始化按键时钟,将IO口配置成上拉输入。键盘去抖动一般要延时10毫秒,建议用SysTick完成一个10毫秒的延时程序,这样完成键盘的扫描。
  9. 完成OLED的初始化,先完成OLED的IO口配置,再进行寄存器的配置,并试着在屏幕中显示一个字符,确认通讯的正确。
  10. 定时器中断的初始化,由于定时器内部计数次数最大为2^16,在此推荐将时钟72分频后计数1000此产生一个1ms的中断,在中断中再计数1000次获得1s的时间。
  11. 在main函数中完成各种初始化,包括:
    时钟的初始化(参考课本P102的RCC_Configurantion()函数);
    GPIO初始化,初始化PA1、PA2、PA3;
    SysTick初始化(参考课本P126页,10毫秒去抖动用);
    OLED初始化,直接调用OLED_Init()函数;
    通用定时器2或3的初始化和定时器优先级设置。
  12. 按键的扫描参考PPT的程序,也可自己写,GPIO初始化参考PPT。
    键盘去抖动一般要延时10毫秒,建议用SysTick完成一个10毫秒的延时程序,实现去抖动的延时。

参考程序:

以下是基于Proteus+Keil+STM32CubeMX的嵌入式闹钟实验的参考程序,供参考学习:

1. main.c

```c
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "OLED.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* Private variables ---------------------------------------------------------*/
RTC_HandleTypeDef hrtc;
TIM_HandleTypeDef htim3;

/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM3_Init(void);
static void MX_RTC_Init(void);

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);
void get_time(uint8_t time[]);
void update_time(void);
void update_alarm(void);
void alarm(void);
void RTC_IRQHandler(void);

/* Private variables ---------------------------------------------------------*/
char buffer[20];
uint8_t time_now[6] = {0};
uint8_t time_set[6] = {0};
uint8_t i = 0, j = 0;
uint8_t curr_val = 0, prev_val = 0;

/* Private function definitions ----------------------------------------------*/
/**
  * @brief  The application entry point.
  * @retval int
  */
int main(void)
{
  /* MCU Configuration--------------------------------------------------------*/
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM3_Init();
  MX_RTC_Init();
  OLED_Init();
  
  /* Start the TIM3 timer */
  HAL_TIM_Base_Start_IT(&htim3);

  /* Infinite loop */
  while (1)
  {
    get_time(time_now); // 获取当前时间
    update_time();      // 更新显示时间
    update_alarm();     // 更新闹钟时间

    curr_val = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1);
    if(curr_val == GPIO_PIN_RESET && prev_val == GPIO_PIN_SET) { // PA1键按下
        HAL_Delay(10);
        curr_val = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1);
        if(curr_val == GPIO_PIN_RESET) {
            i = (i + 1) % 6; // 逐个修改时分秒
            time_now[i] = (time_now[i] + 1) % ((i == 0 ? 24 : 60));
        }
    }
    prev_val = curr_val;

    curr_val = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2);
    if(curr_val == GPIO_PIN_RESET && prev_val == GPIO_PIN_SET) { // PA2键按下
        memcpy(time_set, time_now, sizeof(time_set)); // 闹钟时间设为当前时间
    }
    prev_val = curr_val;

    curr_val = HAL_GPIO_ReadPin

该回答引用ChatGPT
本实验涉及到的软件工具有Proteus、Keil、STM32CubeMX。为了简化实验步骤,可以使用STM32CubeMX生成基础代码,再结合Keil进行编写和调试。下面是具体的实验步骤和代码:

一、Proteus实现闹钟并在OLED上显示时间

1.1 连线:
- 将JLink仿真器连接到STM32F1教学实验板上
- 将OLED显示屏连接到对应的IIC接口

1.2 完成OLED的初始化
- 在Keil中创建一个新的工程
- 在STM32CubeMX中配置GPIO用于连接OLED显示屏
- 选择正确的引脚和模式
- 选择正确的时钟和中断配置
- 生成代码并打开main.c文件
- 在main函数中调用MX_I2C1_Init()和MX_GPIO_Init()函数
- 在main函数中调用OLED_Init()函数进行OLED的初始化

OLED_Init()函数如下:

C
void OLED_Init(void)
{
OLED_WR_Byte(0xAE,OLED_CMD);//--display off
OLED_WR_Byte(0x00,OLED_CMD);//---set low column address
OLED_WR_Byte(0x10,OLED_CMD);//---set high column address
OLED_WR_Byte(0x40,OLED_CMD);//--set start line address Set Mapping RAM Display Start Line (0x00~0x3F)
OLED_WR_Byte(0x81,OLED_CMD);//--set contrast control register
OLED_WR_Byte(0xCF,OLED_CMD); // Set SEG Output Current Brightness
OLED_WR_Byte(0xA1,OLED_CMD);//--Set SEG/Column Mapping
OLED_WR_Byte(0xC8,OLED_CMD);//Set COM/Row Scan Direction
OLED_WR_Byte(0xA6,OLED_CMD);//--set normal display
OLED_WR_Byte(0xA8,OLED_CMD);//--set multiplex ratio(1 to 64)
OLED_WR_Byte(0x3f,OLED_CMD);//--1/64 duty
OLED_WR_Byte(0xD3,OLED_CMD);//-set display offset Shift Mapping RAM Counter (0x00~0x3F)
OLED_WR_Byte(0x00,OLED_CMD);//-not offset
OLED_WR_Byte(0xd5,OLED_CMD);//--set display clock divide ratio/oscillator frequency
OLED_WR_Byte(0x80,OLED_CMD);//--set divide ratio, Set Clock as 100 Frames/Sec
OLED_WR_Byte(0xD9,OLED_CMD);//--set pre-charge period
OLED_WR_Byte(0xF1,OLED_CMD);//Set Pre-Charge as 15 Clocks & Discharge as 1 Clock
OLED_WR_Byte(0xDA,OLED_CMD);//--set com pins hardware configuration
OLED_WR_Byte(0x12,OLED_CMD);
OLED_WR_Byte(0xDB,OLED_CMD);//--set vcomh
OLED_WR_Byte(0x40,OLED_CMD);//Set VCOM Deselect Level
OLED_WR_Byte(0x20,OLED_CMD);//-Set Page Addressing Mode (0x00/0x01/0x02)
OLED_WR_Byte(0x02,OLED_CMD);//
OLED_WR_Byte(0x8D,OLED_CMD);//--set Charge Pump enable/disable
OLED_WR_Byte(0x14,OLED_CMD);//--set(0x10) disable
OLED_WR_Byte(0xA4,OLED_CMD);// Disable Entire Display On (0xa4/0xa5)
OLED_WR_Byte(0xA6,OLED_CMD);// Disable Inverse Display On (0xa6/a7)
OLED_Clear();
OLED_WR_Byte(0xAF,OLED_CMD);//--turn on oled panel
}


1.3 利用通用定时器的中断,实现运行时间的改变,并在OLED中显示时间信息
- 在STM32CubeMX中配置通用定时器用于产生1ms时钟中断
- 选择正确的TIM和时钟配置
- 选择正确的Prescaler、Period、Clock Division和中断配置
- 生成代码并打开main.c文件
- 在头文件中引入stm32f1xx_hal.h和stm32f1xx_it.h文件
- 在main函数中调用MX_TIM3_Init()函数,并在HAL_TIM_PeriodElapsedCallback中写入时钟计数逻辑和更新OLED显示逻辑

MX_TIM3_Init()函数如下:

C
static void MX_TIM3_Init(void)
{
htim3.Instance = TIM3;
htim3.Init.Prescaler = 72-1; // 1MHz 的计数频率 (PCLK1 = HCLK / 2 = 72MHz / 2 = 36MHz)
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 1000-1; // 1kHz 的计数频率 (时钟周期为 1ms)
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim3);

HAL_NVIC_SetPriority(TIM3_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM3_IRQn);
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3) {
msTicks++;
if (msTicks>=1000) {
msTicks=0;
seconds++;
if (seconds>=60) {
seconds=0;
minutes++;
if (minutes>=60) {
minutes=0;
hours++;
if (hours>=24) {
hours=0;
}
}
}
}
char buf[20];
sprintf(buf, "Now Time:
%02d:%02d:%02d", hours, minutes, seconds);
OLED_ShowString(0, 0, buf);
}
}


1.4 通过按键更改当前时间和设置闹钟
- 在STM32CubeMX中配置GPIO用于连接按键
- 选择正确的引脚和模式
- 选择正确的时钟和中断配置
- 生成代码并打开main.c文件
- 在头文件中引入stm32f1xx_hal.h和stm32f1xx_it.h文件
- 在main函数中调用MX_GPIO_Init()函数和MX_TIM2_Init()函数
- 在while循环中调用readButton()函数实现按键扫描和处理

MX_GPIO_Init()函数如下:

C
/* GPIO Initialization Function */
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};

/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOA_CLK_ENABLE();

/*Configure GPIO pin : PA1 */
GPIO_InitStruct.Pin = GPIO_PIN_1;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/*Configure GPIO pin : PA2 */
GPIO_InitStruct.Pin = GPIO_PIN_2;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/*Configure GPIO pin : PA3 */
GPIO_InitStruct.Pin = GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI1_IRQn);
HAL_NVIC_SetPriority(EXTI2_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI2_IRQn);
HAL_NVIC_SetPriority(EXTI3_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(EXTI3_IRQn);
}


MX_TIM2_Init()函数如下:

C
/* TIM2 init function */
static void MX_TIM2_Init(void)
{
htim2.Instance = TIM2;
htim2.Init.Prescaler = 7200-1; // 每 200ms 扫描一次按键 (PCLK1 = HCLK / 2 = 72MHz / 2 / 7200 = 5kHz)
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 200-1; // 200ms 的计数频率 (时钟周期为 200ms)
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_Base_Init(&htim2);

HAL_NVIC_SetPriority(TIM2_IRQn, 1, 0);
HAL_NVIC_EnableIRQ(TIM2_IRQn);
}


readButton()函数如下:

C
void readButton()
{
if (button1_pressed) {
button1_pressed = 0;
hours = (hours + 1) % 24;
}
if (button2_pressed) {
button2_pressed = 0;
minutes = (minutes + 1) % 60;
}
if (button3_pressed) {
button3_pressed = 0;
alarm_hours = (alarm_hours + 1) % 24;
}
}


1.5 运行时间==闹钟时间时,OLED显示警告信息“Alarm!”并循环显示

循环次数和显示频率可以根据实验需求进行调整,以下实现循环四至五次,每个循环间隔为1秒:

C
void checkAlarm()
{
if (hours == alarm_hours && minutes == alarm_minutes && seconds == 0) {
alarm = 1;
for (int i = 0; i < 5; i++) {
OLED_Clear();
OLED_ShowString(25, 2, "Alarm!");
HAL_Delay(1000);
OLED_ShowString(0, 0, "Now Time:
00:00:00");
OLED_ShowString(0, 2, "Set Time:
00:00:00");
HAL_Delay(500);
}
alarm = 0;
}
}


二、完整代码

C
#include "main.h"
#include "oled.h"
#include

TIM_HandleTypeDef htim2;
TIM_HandleTypeDef htim3;

volatile int msTicks = 0;
volatile int seconds = 0;
volatile int minutes = 0;
volatile int hours = 0;

volatile int button1_pressed = 0;
volatile int button2_pressed = 0;
volatile int button3_pressed = 0;

volatile int alarm = 0;
volatile int alarm_hours = 0;
volatile int alarm_minutes = 0;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);
static void MX_TIM3_Init(void);
void readButton();
void checkAlarm();

int main(void)
{
HAL_Init();

SystemClock_Config();

MX_GPIO_Init();
MX_TIM2_Init();
MX_TIM3_Init();

OLED_Init();

while (1)
{
readButton();

char buf[20];
sprintf(buf, "Now Time:
%02d:%02d:%02d", hours, minutes, seconds);
OLED_ShowString(0, 0, buf);

sprintf(buf, "Set Time:
%02d:%02d:%02d", alarm_hours, alarm_minutes, 0);
OLED_ShowString(0, 2, buf);

checkAlarm();
}
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM3) {
msTicks++;
if (msTicks>=1000) {
msTicks=0;
seconds++;
if (seconds>=60) {
seconds=0;
minutes++;
if (minutes>=60) {
minutes=0;
hours++;
if (hours>=24) {
hours=0;
}
}
}
}
char buf[20];
sprintf(buf, "Now Time:
%02d:%02d:%02d", hours, minutes, seconds);
OLED_ShowString(0, 0, buf);
}
else if (htim->Instance == TIM2) {
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_1) == GPIO_PIN_RESET) {
button1_pressed = 1;
}
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_2) == GPIO_PIN_RESET) {
button2_pressed = 1;
}
if (HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_3) == GPIO_PIN_RESET) {
button3_pressed = 1;
}
}
}

void readButton()
{
if (button1_pressed && !alarm) {
button1_pressed = 0;
hours = (hours + 1) % 24;
}
if (button2_pressed && !alarm) {
button2_pressed = 0;
minutes = (minutes + 1) % 60;
}
if (button3_pressed) {
button3_pressed = 0;
alarm_hours = (alarm_hours + 1) % 24;
alarm_minutes = (alarm_minutes + 1) % 60;
}
}

void checkAlarm()
{
if (hours == alarm_hours && minutes == alarm_minutes && seconds == 0) {
alarm = 1;
for (int i = 0; i < 5; i++) {
OLED_Clear();
OLED_ShowString(25, 2, "Alarm!");
HAL_Delay(1000);
OLED_ShowString(0, 0, "Now Time:
00:00:00");
OLED_ShowString(0, 2, "Set Time:
00:00:00");
HAL_Delay(500);
}
alarm = 0;
}
}

void SystemClock_Config(void)
{
RCC_OscInit
该回答引用ChatGPT
这是一个实验要求,需要使用Proteus、Keil和STM32CubeMX,实现一个闹钟,具体要求如下:

1. 实现一个闹钟,OLED 上显示当前时间和设置时间,运行时间与设置时间相同时,OLED 显示“Alarm!”并黑屏,然后再显示以上信息,循环 4 至 5 次,时间间隔为 1 秒,然后继续显示运行时间和设置时间。
2. 利用通用定时器的中断,实现运行时间的改变,并在 OLED 中显示时间信息。
3. 通过按键更改当前时间和设置闹钟。

下面是实现这个要求的具体步骤和代码:

1. 实现闹钟功能

使用 STM32CubeMX 配置时钟和 IO 端口,设置通用定时器中断和 SysTick 去抖动。在 Keil 中编写代码,实现时钟和 OLED 的显示功能。

c
// 定义全局变量
uint8_t time[6] = {20, 8, 21, 10, 30, 0}; // 当前时间,初始值为 20:08:21
uint8_t alarm[6] = {20, 8, 21, 10, 31, 0}; // 闹钟时间,初始值为 20:08:31

// 中断服务函数 TIM3
void TIM3_IRQHandler(void) {
HAL_TIM_IRQHandler(&htim3); // 中断处理
uint8_t temp[6];
for (int i = 0; i < 6; i++) {
temp[i] = time[i];
}
// 每秒加 1
time[5]++;
if (time[5] >= 60) {
time[5] = 0;
time[4]++;
}
if (time[4] >= 60) {
time[4] = 0;
time[3]++;
}
if (time[3] >= 24) {
time[3] = 0;
}
if (time[0] == alarm[0] && time[1] == alarm[1] && time[2] == alarm[2] &&
time[3] == alarm[3] && time[4] == alarm[4] && time[5] == alarm[5]) {
// 时间相同,触发闹钟
OLED_Clear();
OLED_ShowString(30, 10, " Alarm!");
OLED_ShowString(20, 30, "Set Time:");
OLED_ShowNum(75, 30, alarm[3], 2, 16);
OLED_ShowString(85, 30, ":");
OLED_ShowNum(95, 30, alarm[4], 2, 16);
OLED_ShowString(105, 30, ":");
OLED_ShowNum(115, 30, alarm[5], 2, 16);
HAL_Delay(1000);
OLED_Clear();
for (int i = 0; i < 6; i++) {
time[i] = temp[i];
}
} else {
// 时间不同,显示当前时间和设置时间
OLED_Clear();
OLED_ShowString(20, 10, "Now Time:");
OLED_ShowNum(75, 10, time[3], 2, 16);
OLED_ShowString(85, 10, ":");
OLED_ShowNum(95, 10, time[4], 2, 16);
OLED_ShowString(105, 10, ":");
OLED_ShowNum(115, 10, time[5], 2, 16);
OLED_ShowString(20, 30, "Set Time:");
OLED_ShowNum(75, 30, alarm[3], 2, 16);
OLED_ShowString(85, 30, ":");
OLED_ShowNum(95, 30, alarm[4], 2, 16);
OLED_ShowString(105, 30, ":");
OLED_ShowNum(115, 30, alarm[5], 2, 16);
HAL_Delay(1000);
}
}

// SysTick 去抖动
void HAL_SYSTICK_Callback(void) {
static uint16_t key1_count = 0;
static uint16_t key2_count = 0;
static uint16_t key3_count = 0;

if (KEY1 == 1 && ++key1_count >= 50) { // 按键1
key1_count = 0;
if (++time[5] >= 60) {
time[5] = 0;
if (++time[4] >= 60) {
time[4] = 0;
if (++time[3] >= 24) {
time[3] = 0;
}
}
}
}
if (KEY2 == 1 && ++key2_count >= 50) { // 按键2
key2_count = 0;
if (++alarm[5] >= 60) {
alarm[5] = 0;
if (++alarm[4] >= 60) {
alarm[4] = 0;
if (++alarm[3] >= 24) {
alarm[3] = 0;
}
}
}
}
if (KEY3 == 1 && ++key3_count >= 50) { // 按键3
key3_count = 0;
// 清零时间和闹钟
time[3] = 0;
time[4] = 0;
time[5] = 0;
alarm[3] = 0;
alarm[4] = 0;
alarm[5] = 0;
}
}

int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
OLED_Init();
MX_TIM3_Init();
HAL_TIM_Base_Start_IT(&htim3);
SysTick_Config(SystemCoreClock / 100); // 10 毫秒

while (1) {
}
}


2. 定时器中断和 SysTick 去抖动

使用 STM32CubeMX 配置通用定时器中断,在 Keil 中编写代码,实现定时器中断和 SysTick 去抖动功能。

c
// 定时器中断服务函数 TIM3
void TIM3_IRQHandler(void) {
HAL_TIM_IRQHandler(&htim3); // 中断处理
static uint16_t count = 0; // 定时器计数器
if (++count >= 1000) { // 1 秒
count = 0;
/* 时间加 1 */
}
}

// SysTick 去抖动
void HAL_SYSTICK_Callback(void) {
static uint16_t key1_count = 0;
static uint16_t key2_count = 0;
static uint16_t key3_count = 0;

if (KEY1 == 1 && ++key1_count >= 50) { // 按键1
key1_count = 0;
// 时间加 1
}
if (KEY2 == 1 && ++key2_count >= 50) { // 按键2
key2_count = 0;
// 设置时间加 1
}
if (KEY3 == 1 && ++key3_count >= 50) { // 按键3
key3_count = 0;
// 清零时间和设置时间
}
}

int main(void) {
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_TIM3_Init();
HAL_TIM_Base_Start_IT(&htim3);
SysTick_Config(SystemCoreClock / 100); // 10 毫秒

while (1) {
// 循环等待定时器中断和按键中断的触发
}
}


需要根据实际情况修改时间加减、设置时间加减和清零时间和设置时间的代码。

3. 具体代码实现过程中需要根据实际情况进行修改,代码中涉及的函数可以自行编写或查找相关资料使用。建议在代码中添加注释,方便代码的阅读和修改。

希望这个回答可以帮到您。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
由于此问题需要具体代码实现,我将尽可能提供详细的概述和思路,但无法提供完整的代码。

  1. 连接和硬件初始化
    连接STM32F1实验板和PC,确保J-link仿真器与试验箱CPU板和电脑连接正常。完整连接后,读取并理解原理图,此次试验使用三个按键KEY2、KEY3和KEY4,它们分别接到PA1、PA2和PA3。同时在实验板上,这三个按键标注为KEY1、KEY2、KEY3。在初始化按键之前,需初始化按键时钟,并将IO口配置成上拉输入。

  2. 延时和键盘去抖动
    建议使用SysTick完成一个10毫秒的延时程序,以完成键盘的扫描。键盘去抖动一般要延时一定的时间,因此在进行按键扫描之前,建议先进行去抖动延时处理。

  3. OLED初始化
    在OLED初始化阶段,先完成OLED的IO口配置,然后进行寄存器的配置,并试着在屏幕中显示一个字符以确认通讯的正确。

  4. 定时器中断的初始化
    定时器内部计数次数最大为2的16次方,因此在此推荐将时钟72分频后计数1000次,从而产生一个1ms的中断。在中断中再计数1000次,获得1s的时间。在此阶段,需要使用通用定时器2或3的初始化,并设置定时器优先级。

  5. 实现闹钟功能
    利用Proteus实现一个闹钟,闹钟在OLED上显示的时间仅有时分秒,OLED上显示两个时间,上面是两行是运行时间: “Now Time:05:07:00”,下面两行显示闹钟设置时间:“Set Time:05:08:00”。当运行时间等于设定时间时,OLED将显示警告信:“Alarm!”,然后黑屏,再显示上面的提醒信息,如此循环四至五次,时间间隔为1秒,然后继续显示运行时间和设置时间。通过按键更改当前时间和设置闹钟。

以上是实现此闹钟功能的基本思路和步骤。详细的代码实现可以参考相关的STM32F1开发文档和示例代码。
如果我的回答解决了您的问题,请采纳!

针对你的提问,我在我的使用环境中,实现了oled的基本显示,以下是基于Proteus+Keil+STM32CubeMX的oled实验的参考程序,供你参考学习:
main.c中的代码


/* USER CODE BEGIN Header */
/**
  ******************************************************************************
  * @file           : main.c
  * @brief          : Main program body
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2023 STMicroelectronics.
  * All rights reserved.</center></h2>
  *
  * This software component is licensed by ST under BSD 3-Clause license,
  * the "License"; You may not use this file except in compliance with the
  * License. You may obtain a copy of the License at:
  *                        opensource.org/licenses/BSD-3-Clause
  *
  ******************************************************************************
  */
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"

/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
#include "stdio.h"
#include "oled.h"
/* USER CODE END Includes */

/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */

/* USER CODE END PTD */

/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* 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 */

/* USER CODE END PFP */

/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */

/* 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_USART1_UART_Init();
  MX_TIM2_Init();
  /* USER CODE BEGIN 2 */
  OLED_Init();
  OLED_ShowString(16,0,"Now Time:",16);
  OLED_ShowString(14,16,"05:07:00",16);
  OLED_ShowString(16,32,"Set Time:",16);
  OLED_ShowString(14,48,"05:08:00",16);
  OLED_Refresh(); // 刷新屏幕
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
    /* 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};

  /** Initializes the RCC Oscillators according to the specified parameters
  * in the RCC_OscInitTypeDef structure.
  */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  RCC_OscInitStruct.HSIState = RCC_HSI_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();
  }
}

/* USER CODE BEGIN 4 */
#ifdef __GNUC__
/* With GCC, small printf (option LD Linker->Libraries->Small printf
   set to 'Yes') calls __io_putchar() */
#define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif /* __GNUC__ */
/**
  * @brief  Retargets the C library printf function to the USART.
  * @param  None
  * @retval None
  */
PUTCHAR_PROTOTYPE
{
  /* Place your implementation of fputc here */
  /* e.g. write a character to the USART2 and Loop until the end of transmission */
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xFFFF);

  return ch;
}
/* 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 */

/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

实验现象能正常的显示字符:

img


给你个参考工程:
https://download.csdn.net/download/weixin_44317448/87654794

img

以下内容部分参考ChatGPT模型:
首先,需要在Keil MDK中创建一个新的STM32CubeMX项目,并进行配置。在配置过程中,需要选择使用通用定时器2或3作为计时器,并配置中断。同时,需要配置GPIO用于连接OLED和按键。

接下来,需要编写代码实现按键扫描、OLED显示和定时器中断。代码示例如下:

#include "stm32f1xx_hal.h"
#include "ssd1306.h"

#define KEY1 GPIO_PIN_0
#define KEY2 GPIO_PIN_1
#define KEY3 GPIO_PIN_2

TIM_HandleTypeDef htim2;

void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_TIM2_Init(void);

int main(void)
{
  HAL_Init();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_TIM2_Init();
  ssd1306_Init();
  ssd1306_Fill(Black);
  ssd1306_SetCursor(0, 0);
  ssd1306_WriteString("Now Time: 00:00:00", Font_11x18, White);
  ssd1306_SetCursor(0, 22);
  ssd1306_WriteString("Set Time: 00:00:00", Font_11x18, White);
  ssd1306_UpdateScreen();
  HAL_TIM_Base_Start_IT(&htim2);
  while (1)
  {
    if (HAL_GPIO_ReadPin(GPIOA, KEY1) == GPIO_PIN_RESET)
    {
      // change current time
    }
    if (HAL_GPIO_ReadPin(GPIOA, KEY2) == GPIO_PIN_RESET)
    {
      // change alarm time
    }
    if (HAL_GPIO_ReadPin(GPIOA, KEY3) == GPIO_PIN_RESET)
    {
      // clear alarm
    }
  }
}

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  static int cnt = 0;
  if (htim->Instance == TIM2)
  {
    cnt++;
    if (cnt >= 1000)
    {
      cnt = 0;
      // update time
      ssd1306_SetCursor(0, 0);
      ssd1306_WriteString("Now Time: 00:00:00", Font_11x18, White);
      ssd1306_SetCursor(0, 22);
      ssd1306_WriteString("Set Time: 00:00:00", Font_11x18, White);
      ssd1306_UpdateScreen();
      // check alarm
    }
  }
}

void SystemClock_Config(void)
{
  RCC_OscInitTypeDef RCC_OscInitStruct = {0};
  RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};

  __HAL_RCC_PWR_CLK_ENABLE();
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);

  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_ON;
  RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
  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();
  }

  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();
  }
}

static void MX_TIM2_Init(void)
{
  TIM_ClockConfigTypeDef sClockSourceConfig = {0};
  TIM_MasterConfigTypeDef sMasterConfig = {0};

  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 71999;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 999;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }

  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }

  sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
}

static void MX_GPIO_Init(void)
{
  GPIO_InitTypeDef GPIO_InitStruct = {0};

  __HAL_RCC_GPIOA_CLK_ENABLE();

  HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2, GPIO_PIN_SET);

  GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2;
  GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
}

需要注意的是,这只是一个示例代码,需要根据具体情况进行修改和完善。

最后,在Proteus中创建一个仿真电路,在其中添加STM32F1教学实验板、OLED和按键,并进行连线。运行仿真即可看到OLED上的显示效果。
如果我的建议对您有帮助、请点击采纳、祝您生活愉快

不知道你这个问题是否已经解决, 如果还没有解决的话:

如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^