
STM32F103基于HAL库I2C/SPI硬件接口+DMA驱动 SSD1306 Oled
- ✨由于手上只有I2C接口的SSD1306 OLED屏幕,仅测试了硬件I2C驱动显示功能,实际测试的FPS帧率在37或38变化。
- 📢本项目从Github开源项目中移植过来,开源地址:
https://github.com/lamik/OLED_SSD1306_STM32_HAL
- 🎞 I2C + DMA实测效果;

📑硬件接口说明
- 🌿采用的是STM32F103VC,配置的是:I2C1和SPI1
PB8 ------> I2C1_SCLPB9 ------> I2C1_SDA
PA4 ------> SPI1_NSSPA5 ------> SPI1_SCKPA7 ------> SPI1_MOSI
🛠STM32cubemx配置参数
- 🌿I2C接口配置参数:
Fast-mode
,比特速率最高400 kbit/s.

- 🌿添加I2C DMA数据发送选项

- 🌿SPI接口配置

- 🌿SPI DMA配置

- 🌿NVIC配置

- 🌿相关引脚配置和定义



⛳业务代码注意事项
- 🌿原工程是使用了抵达定时器中断的,在STM32cubemx中该接口函数是没有进行初始化的。需要手动添加,如果没有手动添加,OLED显示会卡在一个固定的显示画面上。

- 🧲调用关系:
SysTick_Handler()
--> HAL_SYSTICK_IRQHandler()
--> HAL_SYSTICK_Callback()
- 🌿在
stm32f1xx_it.c
文件中找到下面的函数,并在对应位置添加相应的函数:
void SysTick_Handler(void)
{HAL_IncTick();HAL_SYSTICK_IRQHandler();
}
🌿I2C/SPI接口切换说明
- 🔰在OLED_SSD1306.h头文件中启用和注释相对应的宏,即可实现接口切换。
#define SSD1306_I2C_CONTROL #ifdef SSD1306_I2C_CONTROL
#define SSD1306_I2C_DMA_ENABLE
#define SSD1306_I2C_ADDRESS 0x78
#endif
#ifdef SSD1306_SPI_CONTROL
#define SSD1306_RESET_USE
#define SSD1306_SPI_DMA_ENABLE
#define SPI_CS_HARDWARE_CONTROL
#endif
📝main主程序代码
#include "main.h"
#include "dma.h"
#include "i2c.h"
#include "spi.h"
#include "gpio.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "OLED_SSD1306.h"
#include "GFX_BW.h"
#include "fonts/fonts.h"
#include "picture.h"
volatile uint16_t timer_1s;
void SystemClock_Config(void);
void HAL_SYSTICK_Callback(void)
{if(timer_1s){--timer_1s;}
}void I2C_DMAError(void)
{while(1){
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);}
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef* hspi)
{
#if defined(SSD1306_SPI_CONTROL) && !defined(SSD1306_SPI_DMA_ENABLE)SSD1306_DmaEndCallback(hspi);
#endif
}
int main(void)
{HAL_Init();SystemClock_Config();MX_GPIO_Init();MX_DMA_Init();MX_I2C1_Init();MX_SPI1_Init();
#ifdef SSD1306_I2C_CONTROLSSD1306_I2cInit(&hi2c1);
#endif
#ifdef SSD1306_SPI_CONTROLSSD1306_SpiInit(&hspi1);
#endif
HAL_Delay(1000);GFX_SetFont(font_8x5);GFX_SetFontSize(1);
uint16_t frames = 0, fps = 0;uint32_t loops = 0, loops_overal = 0;char fps_c[20];while(1){if(!timer_1s){timer_1s = 1000;fps = frames;frames = 0;loops_overal = loops;loops = 0;}#ifdef SSD1306_I2C_DMA_ENABLEif(hi2c1.hdmatx->State == HAL_DMA_STATE_READY){
#endif
#ifdef SSD1306_SPI_DMA_ENABLEif(hspi1.hdmatx->State == HAL_DMA_STATE_READY){
#endifsprintf(fps_c, "loops: %02d", (int)loops_overal);SSD1306_Clear(BLACK);GFX_DrawString(10, 10, fps_c, WHITE, BLACK);sprintf(fps_c, "FPS: %02d", fps);GFX_DrawString(10, 20, fps_c, WHITE, BLACK);GFX_DrawString(19, 40, "www.msalamon.pl", WHITE, BLACK);frames++;HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);SSD1306_Display();
#if defined(SSD1306_I2C_DMA_ENABLE) || defined(SSD1306_SPI_DMA_ENABLE)}
#endif
HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_RESET);
loops++;}
}
void SystemClock_Config(void)
{RCC_OscInitTypeDef RCC_OscInitStruct = {0};RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};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();}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();}
}
void Error_Handler(void)
{__disable_irq();while(1){}
}#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
}
#endif
📚工程源码
链接: https:
提取码: syjh