| 本帖最后由 jinglixixi 于 2025-7-15 21:20 编辑 
 
 前面介绍了以软件模拟的方式来驱动OLED屏,有了其成功的基础再来尝试硬件的驱动方式就方便了许多,因为硬件驱动的主要工作就是配置I2C的初始化函数及变更I2C发送字节数据的发送,其他的基本无需进行调整。 
 
 1. OLED显示屏驱动 为了驱动OLED显示屏,这里使用的接口I2C2,相应的引脚连接关系为: 
 OLED_SCL -----I2C2_SCL----- PB13      OLED_SDA------I2C2_SDA------PB14  
 
 对I2C2的初始化函数为: void MX_I2C2_Init(void)
{
  hi2c2.Instance = I2C2;
  hi2c2.Init.Timing = 0x009032AE;
  hi2c2.Init.OwnAddress1 = 0;
  hi2c2.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c2.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c2.Init.OwnAddress2 = 0;
  hi2c2.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
  hi2c2.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c2.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c2) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c2, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
  {
    Error_Handler();
  }
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c2, 0) != HAL_OK)
  {
    Error_Handler();
  }
}
 实现I2C发送字节数据的函数为: #define OLED_ADDRESS   0x78 
void I2C_WriteByte(uint8_t addr, uint8_t dat)
{
        uint8_t temp[2];
        temp[0] = dat;
        temp[1] = dat;
        HAL_I2C_Mem_Write(&hi2c2,OLED_ADDRESS,addr,I2C_MEMADD_SIZE_8BIT,temp,1,10);
}
 实现指令发送的函数为: void WriteCmd(unsigned char I2C_Command)
{
    I2C_WriteByte(0x00, I2C_Command);
}
 对所用显示屏的初始化函数为: void OLED_Init(void)
{
        HAL_Delay(100);
        WriteCmd(0xAE);  //display off
        WriteCmd(0x40);
        WriteCmd(0xb0);        //Set Page Start Address for Page Addressing Mode,0-7
        WriteCmd(0xc8);        //Set COM Output Scan Direction
        WriteCmd(0x81);  //--set contrast control register
        WriteCmd(0xff);   //亮度调节 0x00~0xff
        WriteCmd(0xa1);  //--set segment re-map 0 to 127
        WriteCmd(0xa6);  //--set normal display
        WriteCmd(0xa8);  //--set multiplex ratio(1 to 64)
        WriteCmd(0x1F);
        WriteCmd(0xd3);  //-set display offset
        WriteCmd(0x00);  //-not offset
        WriteCmd(0xd5);  //--set display clock divide ratio/oscillator frequency
        WriteCmd(0xf0);  //--set divide ratio
        WriteCmd(0xd9);  //--set pre-charge period
        WriteCmd(0x22); 
        WriteCmd(0xda);  //--set com pins hardware configuration
        WriteCmd(0x02);
        WriteCmd(0xdb);  //--set vcomh
        WriteCmd(0x49); 
        WriteCmd(0x8d);  //--set DC-DC enable
        WriteCmd(0x14); 
        WriteCmd(0xaf);  //--turn on oled panel
}
 实现数据填充全屏的函数为: void OLED_Fill(unsigned char fill_Data)
{
        unsigned char m,n;
        for(m=0;m<8;m++)
        {
                  WriteCmd(0xb0+m);                //page0-page1
                  WriteCmd(0x00);                    //low column start address
                  WriteCmd(0x10);                    //high column start address
                  for(n=0;n<128;n++)
                  {
                                WriteDat(fill_Data);
                  }
        }
}
 实现字符串显示的函数为: void OLED_ShowStr(unsigned char x, unsigned char y, unsigned char ch[], unsigned char TextSize)
{
        unsigned char c = 0,i = 0,j = 0;
        switch(TextSize)
        {
                case 1:
                {
                        while(ch[j] != '\0')
                        {
                                c = ch[j] - 32;
                                if(x > 126)
                                {
                                        x = 0;
                                        y++;
                                }
                                OLED_SetPos(x,y);
                                for(i=0;i<6;i++)
                                        WriteDat(F6x8[c][i]);
                                x += 6;
                                j++;
                        }
                }
                break;
                case 2:
                {
                        while(ch[j] != '\0')
                        {
                                c = ch[j] - 32;
                                if(x > 120)
                                {
                                        x = 0;
                                        y++;
                                }
                                OLED_SetPos(x,y);
                                for(i=0;i<8;i++)
                                        WriteDat(F8X16[c*16+i]);
                                OLED_SetPos(x,y+1);
                                for(i=0;i<8;i++)
                                        WriteDat(F8X16[c*16+i+8]);
                                x += 8;
                                j++;
                        }
                }
                break;
        }
}
 测试以硬件方式驱动OLED显示屏并显示字符串信息的主程序为: int main(void)
{
        HAL_Init();
        SystemClock_Config();
        MX_GPIO_Init();
        MX_I2C1_Init();
        MX_ICACHE_Init();
        BSP_LED_Init(LD2);
        BSP_PB_Init(BUTTON_USER, BUTTON_MODE_GPIO);        
        MX_I2C2_Init();
        OLED_Init();
        OLED_Fill(0x00); 
        OLED_ShowStr(0,0,"STM32U385",2);
        OLED_ShowStr(0,2,"I2C OLED TEST",2);        
        while (BSP_PB_GetState(BUTTON_USER) != GPIO_PIN_SET);
}
 经程序的编译和下载,其测试效果如图1和图2所示。 图1 驱动0.91寸屏 
 图2 驱动0.96寸屏  
 
 2. RTC时钟 
 要使用片内的RTC实现电子时钟功能,需要先对RTC进行初始化设置,其对应的函数为: static void MX_RTC_Init(void)
{
  RTC_PrivilegeStateTypeDef privilegeState = {0};
  RTC_TimeTypeDef sTime = {0};
  RTC_DateTypeDef sDate = {0};
  RTC_AlarmTypeDef sAlarm = {0};
  hrtc.Instance = RTC;
  hrtc.Init.HourFormat = RTC_HOURFORMAT_24;
  hrtc.Init.AsynchPrediv = 127;
  hrtc.Init.SynchPrediv = 255;
  hrtc.Init.OutPut = RTC_OUTPUT_DISABLE;
  hrtc.Init.OutPutRemap = RTC_OUTPUT_REMAP_NONE;
  hrtc.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
  hrtc.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
  hrtc.Init.OutPutPullUp = RTC_OUTPUT_PULLUP_NONE;
  hrtc.Init.BinMode = RTC_BINARY_NONE;
  if (HAL_RTC_Init(&hrtc) != HAL_OK)
  {
    Error_Handler();
  }
  privilegeState.rtcPrivilegeFull = RTC_PRIVILEGE_FULL_NO;
  privilegeState.backupRegisterPrivZone = RTC_PRIVILEGE_BKUP_ZONE_NONE;
  privilegeState.backupRegisterStartZone2 = RTC_BKP_DR0;
  privilegeState.backupRegisterStartZone3 = RTC_BKP_DR0;
  if (HAL_RTCEx_PrivilegeModeSet(&hrtc, &privilegeState) != HAL_OK)
  {
    Error_Handler();
  }
  sTime.Hours = 0x2;
  sTime.Minutes = 0x20;
  sTime.Seconds = 0x0;
  sTime.DayLightSaving = RTC_DAYLIGHTSAVING_NONE;
  sTime.StoreOperation = RTC_STOREOPERATION_RESET;
  if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BCD) != HAL_OK)
  {
    Error_Handler();
  }
  sDate.WeekDay = RTC_WEEKDAY_TUESDAY;
  sDate.Month = RTC_MONTH_JULY ;
  sDate.Date = 0x15;
  sDate.Year = 0x25;
  if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BCD) != HAL_OK)
  {
    Error_Handler();
  }
  sAlarm.AlarmTime.Hours = 0x2;
  sAlarm.AlarmTime.Minutes = 0x20;
  sAlarm.AlarmTime.Seconds = 0x30;
  sAlarm.AlarmTime.SubSeconds = 0x56;
  sAlarm.AlarmMask = RTC_ALARMMASK_NONE;
  sAlarm.AlarmSubSecondMask = RTC_ALARMSUBSECONDMASK_ALL;
  sAlarm.AlarmDateWeekDaySel = RTC_ALARMDATEWEEKDAYSEL_WEEKDAY;
  sAlarm.AlarmDateWeekDay = RTC_WEEKDAY_MONDAY;
  sAlarm.Alarm = RTC_ALARM_A;
  if (HAL_RTC_SetAlarm_IT(&hrtc, &sAlarm, RTC_FORMAT_BCD) != HAL_OK)
  {
    Error_Handler();
  }
}
 实现时钟显示功能的函数为: static void RTC_TimeShow(uint8_t *showtime)
{
   RTC_DateTypeDef sdatestructureget;
   RTC_TimeTypeDef stimestructureget;
   HAL_RTC_GetTime(&hrtc, &stimestructureget, RTC_FORMAT_BIN);
   HAL_RTC_GetDate(&hrtc, &sdatestructureget, RTC_FORMAT_BIN);
   sprintf((char *)showtime, "%02d:%02d:%02d", stimestructureget.Hours, stimestructureget.Minutes, stimestructureget.Seconds);
   OLED_ShowString(0,2,showtime,16);        
}
 测试电子时钟计时与显示功能的主程序为: int main(void)
{
  HAL_Init();
  HAL_PWR_EnableBkUpAccess();
  SystemClock_Config();
  MX_GPIO_Init();
  MX_RTC_Init();
  MX_ICACHE_Init();
  BSP_LED_Init(LD2);
  RTCStatus = 1;        
  I2C_CONFIG();
  HAL_Delay(50);
  OLED_Init();
  OLED_Clear();
  OLED_ShowString(0,0,"STM32U385 RTC",16);        
  while (1)
  {
       RTC_TimeShow(aShowTime);
  }
}
 经程序的编译和下载,其测试效果如图3所示。 图3 电子时钟计时效果 
 
 演示视频: 
 
 |