STM32 HAL移植u8g2库(I2C)

通过STM32CubeMX配置I2C。

复制相关文件到工程文件夹

保留对应的显示屏型号,如SSD1306,就保留u8x8_d_ssd1306_128x64_noname.c,其它u8x8_d_XXXX.c都删掉。

GPIO和延迟回调

该函数用于设置和复位 GPIO(如果是软件实现的接口),例如软件 I2C、SPI、8080 或 6800 接口。此外,此函数用于实现引脚时序的忙等待延迟。

HAL 发送的消息分为三类。

  1. “U8X8_MSG_DELAY_”形式的延迟消息。这些消息用于为 I2C、SPI 等的软件实现提供延迟。
    为了使软件(又名 bit-banged)接口工作,您需要实现 MCU 特定的忙等待循环以提供正确的延迟量.
    对于示例实现,我使用了 CyDelay* 形式的赛普拉斯 PSoC 特定延迟函数

  2. “U8X*_MSG_GPIO”形式的GPIO 消息。这些消息用于将 1 和 0 写入用于连接设备的 GPIO。即 SCL/SDA 或 Reset 或 CS 等。
    对于示例实现,我使用了 Cypress 引脚写入函数,这些函数都采用“pinname_Write()”的形式。

  3. GPIO 菜单引脚用于获取输入引脚的状态。这些消息只有内置菜单功能需要,如果不使用 U8G2/U8X8 菜单功能,可以忽略。

GPIO和延迟回调模板

uint8_t u8x8_gpio_and_delay_template(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  switch(msg)
  {
    case U8X8_MSG_GPIO_AND_DELAY_INIT:	// called once during init phase of u8g2/u8x8
      break;							// can be used to setup pins
    case U8X8_MSG_DELAY_NANO:			// delay arg_int * 1 nano second
      break;    
    case U8X8_MSG_DELAY_100NANO:		// delay arg_int * 100 nano seconds
      break;
    case U8X8_MSG_DELAY_10MICRO:		// delay arg_int * 10 micro seconds
      break;
    case U8X8_MSG_DELAY_MILLI:			// delay arg_int * 1 milli second
      break;
    case U8X8_MSG_DELAY_I2C:				// arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
      break;							// arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
    case U8X8_MSG_GPIO_D0:				// D0 or SPI clock pin: Output level in arg_int
    //case U8X8_MSG_GPIO_SPI_CLOCK:
      break;
    case U8X8_MSG_GPIO_D1:				// D1 or SPI data pin: Output level in arg_int
    //case U8X8_MSG_GPIO_SPI_DATA:
      break;
    case U8X8_MSG_GPIO_D2:				// D2 pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_D3:				// D3 pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_D4:				// D4 pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_D5:				// D5 pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_D6:				// D6 pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_D7:				// D7 pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_E:				// E/WR pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_CS:				// CS (chip select) pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_DC:				// DC (data/cmd, A0, register select) pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_RESET:			// Reset pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_CS1:				// CS1 (chip select) pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_CS2:				// CS2 (chip select) pin: Output level in arg_int
      break;
    case U8X8_MSG_GPIO_I2C_CLOCK:		// arg_int=0: Output low at I2C clock pin
      break;							// arg_int=1: Input dir with pullup high for I2C clock pin
    case U8X8_MSG_GPIO_I2C_DATA:			// arg_int=0: Output low at I2C data pin
      break;							// arg_int=1: Input dir with pullup high for I2C data pin
    case U8X8_MSG_GPIO_MENU_SELECT:
      u8x8_SetGPIOResult(u8x8, /* get menu select pin state */ 0);
      break;
    case U8X8_MSG_GPIO_MENU_NEXT:
      u8x8_SetGPIOResult(u8x8, /* get menu next pin state */ 0);
      break;
    case U8X8_MSG_GPIO_MENU_PREV:
      u8x8_SetGPIOResult(u8x8, /* get menu prev pin state */ 0);
      break;
    case U8X8_MSG_GPIO_MENU_HOME:
      u8x8_SetGPIOResult(u8x8, /* get menu home pin state */ 0);
      break;
    default:
      u8x8_SetGPIOResult(u8x8, 1);			// default return value
      break;
  }
  return 1;
}

 信号回调

为了连接到显示控制器的通信端口,您需要有一个面向字节的接口,即 SPI、I2C 等。该接口可以实现为 bit-banged 软件接口或使用 MCU 特定硬件。作为 u8x8_byte.c 中 U8X8 库的一部分,提供了几个软件 bit-banged 接口:

字节程序描述
u8x8_byte_4wire_sw_spi标准 8 位 SPI 通信,带“四针”(SCK、MOSI、DC、CS)
u8x8_byte_3wire_sw_spi9 位“三针”通信(SCK、MOSI、CS)
u8x8_byte_8bit_6800mode并行接口,6800格式
u8x8_byte_8bit_8080mode并行接口,8080格式
u8x8_byte_sw_i2c两线制,I2C 通信
u8x8_byte_ks0108KS0108控制器专用接口

上述函数使用了你定义的 uC 特定的 gpio 和 delay 函数。

硬件接口函数需要处理来自系统其余部分的消息。您需要实现的消息是:

信息描述
U8X8_MSG_BYTE_INIT在显示的初始化阶段发送一次。
U8X8_MSG_BYTE_SET_DC设置数据/命令引脚的电平。arg_int包含预期的输出水平。用于u8x8_gpio_SetDC(u8x8, arg_int)向 GPIO 过程发送消息。
U8X8_MSG_BYTE_START_TRANSFER在此处设置片选线。 u8x8->display_info->chip_enable_level包含预期水平。用于u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_enable_level)调用 GPIO 过程。
U8X8_MSG_BYTE_SEND发送一个或多个字节,位于arg_ptrarg_int包含字节数。
U8X8_MSG_BYTE_END_TRANSFER取消选择设备。从这里使用 CS 级别:u8x8->display_info->chip_disable_level.

 硬件IPS通讯模板

下面的代码列出了一个典型的 SPI 实现。这些消息被转换为对 Arduino SPI 库的调用。

extern "C" uint8_t u8x8_byte_arduino_hw_spi(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr) {
  uint8_t *data;
  uint8_t internal_spi_mode; 
  switch(msg) {
    case U8X8_MSG_BYTE_SEND:
      data = (uint8_t *)arg_ptr;
      while( arg_int > 0 ) {
        SPI.transfer((uint8_t)*data);
        data++;
        arg_int--;
      }  
      break;
    case U8X8_MSG_BYTE_INIT:
      u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);
      SPI.begin();
      break;
    case U8X8_MSG_BYTE_SET_DC:
      u8x8_gpio_SetDC(u8x8, arg_int);
      break;
    case U8X8_MSG_BYTE_START_TRANSFER:
      /* SPI mode has to be mapped to the mode of the current controller, at least Uno, Due, 101 have different SPI_MODEx values */
      internal_spi_mode =  0;
      switch(u8x8->display_info->spi_mode) {
        case 0: internal_spi_mode = SPI_MODE0; break;
        case 1: internal_spi_mode = SPI_MODE1; break;
        case 2: internal_spi_mode = SPI_MODE2; break;
        case 3: internal_spi_mode = SPI_MODE3; break;
      }
      SPI.beginTransaction(SPISettings(u8x8->display_info->sck_clock_hz, MSBFIRST, internal_spi_mode));
      u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_enable_level);  
      u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->post_chip_enable_wait_ns, NULL);
      break;
    case U8X8_MSG_BYTE_END_TRANSFER:      
      u8x8->gpio_and_delay_cb(u8x8, U8X8_MSG_DELAY_NANO, u8x8->display_info->pre_chip_disable_wait_ns, NULL);
      u8x8_gpio_SetCS(u8x8, u8x8->display_info->chip_disable_level);
      SPI.endTransaction();
      break;
    default:
      return 0;
  }  
  return 1;
}

硬件I2C通讯模板

uint8_t u8x8_byte_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
  static uint8_t buffer[32];		/* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
  static uint8_t buf_idx;
  uint8_t *data;
 
  switch(msg)
  {
    case U8X8_MSG_BYTE_SEND:
      data = (uint8_t *)arg_ptr;      
      while( arg_int > 0 )
      {
	buffer[buf_idx++] = *data;
	data++;
	arg_int--;
      }      
      break;
    case U8X8_MSG_BYTE_INIT:
      /* add your custom code to init i2c subsystem */
      break;
    case U8X8_MSG_BYTE_SET_DC:
      /* ignored for i2c */
      break;
    case U8X8_MSG_BYTE_START_TRANSFER:
      buf_idx = 0;
      break;
    case U8X8_MSG_BYTE_END_TRANSFER:
      i2c_transfer(u8x8_GetI2CAddress(u8x8) >> 1, buf_idx, buffer);
      break;
    default:
      return 0;
  }
  return 1;
}

 开始移植

根据使用的显示屏型号,在u8g2_d_setup.c  保留 void u8g2_Setup_ssd1306_i2c_128x64_noname_f。

/* u8g2_d_setup.c */
/* generated code, codebuild, u8g2 project */

#include "u8g2.h"

/* ssd1306 f */
void u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2_t *u8g2, const u8g2_cb_t *rotation, u8x8_msg_cb byte_cb, u8x8_msg_cb gpio_and_delay_cb)
{
  uint8_t tile_buf_height;
  uint8_t *buf;
  u8g2_SetupDisplay(u8g2, u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_fast_i2c, byte_cb, gpio_and_delay_cb);
  buf = u8g2_m_16_8_f(&tile_buf_height);
  u8g2_SetupBuffer(u8g2, buf, tile_buf_height, u8g2_ll_hvline_vertical_top_lsb, rotation);
}
/* end of generated code */

 void u8g2_Setup_ssd1306_i2c_128x64_noname_f 会使用在uint8_t *u8g2_m_16_8_f,在u8g2_d_memory.c 保留 uint8_t *u8g2_m_16_8_f。

/* u8g2_d_memory.c */
/* generated code, codebuild, u8g2 project */

#include "u8g2.h"

uint8_t *u8g2_m_16_8_f(uint8_t *page_cnt)
{
  #ifdef U8G2_USE_DYNAMIC_ALLOC
  *page_cnt = 8;
  return 0;
  #else
  static uint8_t buf[1024];
  *page_cnt = 8;
  return buf;
  #endif
}
/* end of generated code */

编写oled.h。

#ifndef __oled_H
#define __oled_H
#ifdef __cplusplus
 extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "u8g2.h"
/* USER CODE BEGIN Includes */

/* USER CODE END Includes */



/* USER CODE BEGIN Private defines */

/* USER CODE END Private defines */
#define u8         unsigned char  // ?unsigned char ????
#define MAX_LEN    128  //
#define OLED_ADDRESS  0x78 // oled模块从机地址
#define OLED_CMD   0x00  // 写命令
#define OLED_DATA  0x40  // 写数据
 
/* USER CODE BEGIN Prototypes */
 uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
 uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr);
 void u8g2Init(u8g2_t *u8g2);
 #ifdef __cplusplus
}
#endif
#endif /*__ i2c_H */
/* USER CODE END Prototypes */

编写oled.c。

u8x8_gpio_and_delay 和 u8x8_byte_hw_i2c 两个回调函数,最终会添加到 void u8g2Init 初始化函数内。

#include "u8g2_oled.h"
#include "i2c.h"

uint8_t u8x8_byte_hw_i2c(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    /* u8g2/u8x8 will never send more than 32 bytes between START_TRANSFER and END_TRANSFER */
    static uint8_t buffer[128];
    static uint8_t buf_idx;
    uint8_t *data;

    switch (msg)
    {
    case U8X8_MSG_BYTE_INIT:
    {
        /* add your custom code to init i2c subsystem */
        MX_I2C2_Init(); //I2C初始化
    }
    break;

    case U8X8_MSG_BYTE_START_TRANSFER:
    {
        buf_idx = 0;
    }
    break;

    case U8X8_MSG_BYTE_SEND:
    {
        data = (uint8_t *)arg_ptr;

        while (arg_int > 0)
        {
            buffer[buf_idx++] = *data;
            data++;
            arg_int--;
        }
    }
    break;

    case U8X8_MSG_BYTE_END_TRANSFER:
    {
        if (HAL_I2C_Master_Transmit(&hi2c2, OLED_ADDRESS, buffer, buf_idx, 1000) != HAL_OK)
            return 0;
    }
    break;

    case U8X8_MSG_BYTE_SET_DC:
        break;

    default:
        return 0;
    }

    return 1;
}

void delay_us(uint32_t time)
{
    uint32_t i = 8 * time;
    while (i--)
        ;
}

uint8_t u8x8_gpio_and_delay(u8x8_t *u8x8, uint8_t msg, uint8_t arg_int, void *arg_ptr)
{
    switch (msg)
    {
    case U8X8_MSG_DELAY_100NANO: // delay arg_int * 100 nano seconds
        __NOP();
        break;
    case U8X8_MSG_DELAY_10MICRO: // delay arg_int * 10 micro seconds
        for (uint16_t n = 0; n < 320; n++)
        {
            __NOP();
        }
        break;
    case U8X8_MSG_DELAY_MILLI: // delay arg_int * 1 milli second
        HAL_Delay(1);
        break;
    case U8X8_MSG_DELAY_I2C: // arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
        delay_us(5);
        break;                    // arg_int=1: delay by 5us, arg_int = 4: delay by 1.25us
    case U8X8_MSG_GPIO_I2C_CLOCK: // arg_int=0: Output low at I2C clock pin
        break;                    // arg_int=1: Input dir with pullup high for I2C clock pin
    case U8X8_MSG_GPIO_I2C_DATA:  // arg_int=0: Output low at I2C data pin
        break;                    // arg_int=1: Input dir with pullup high for I2C data pin
    case U8X8_MSG_GPIO_MENU_SELECT:
        u8x8_SetGPIOResult(u8x8, /* get menu select pin state */ 0);
        break;
    case U8X8_MSG_GPIO_MENU_NEXT:
        u8x8_SetGPIOResult(u8x8, /* get menu next pin state */ 0);
        break;
    case U8X8_MSG_GPIO_MENU_PREV:
        u8x8_SetGPIOResult(u8x8, /* get menu prev pin state */ 0);
        break;
    case U8X8_MSG_GPIO_MENU_HOME:
        u8x8_SetGPIOResult(u8x8, /* get menu home pin state */ 0);
        break;
    default:
        u8x8_SetGPIOResult(u8x8, 1); // default return value
        break;
    }
    return 1;
}
void u8g2Init(u8g2_t *u8g2)
{
	u8g2_Setup_ssd1306_i2c_128x64_noname_f(u8g2, U8G2_R0, u8x8_byte_hw_i2c, u8x8_gpio_and_delay); // 初始化 u8g2 结构体
	u8g2_InitDisplay(u8g2);                                                                       // 根据所选的芯片进行初始化工作,初始化完成后,显示器处于关闭状态
	u8g2_SetPowerSave(u8g2, 0);                                                                   // 打开显示器
	u8g2_ClearBuffer(u8g2);
}

在main函数添加测试代码。

  while (1)
  {
    u8g2_ClearBuffer(&u8g2);
    u8g2_DrawBox(&u8g2,0,0,20,20);
    u8g2_DrawBox(&u8g2,20,20,20,20);
    u8g2_DrawFrame(&u8g2,10,40,20,20);
    u8g2_SendBuffer(&u8g2);
      
    
    HAL_Delay(100);
    /* USER CODE END WHILE */

    /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
}

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: STM32 HAL I2C是一种用于STM32微控制器的集合,用于实现I2C总线通信协议。该集成了STM32微控制器所有的I2C控制器寄存器,提供了更为简单易用的API接口,方便开发人员进行I2C总线通信开发。 STM32 HAL I2C的主要特点包括以下几点: 1. 支持多种I2C模式:包括标准模式(100Kbps)、快速模式(400Kbps)和高速模式(1Mbps)等。 2. 支持多个I2C从机设备:可以连接多个从机设备,并分别进行读写操作。 3. 提供了简单易懂的API接口:开发人员可以使用简单的函数调用实现I2C总线设备的初始化、读写操作等。 4. 提供了中断和DMA两种数据传输方式:可以根据实际应用场景选择适合的数据传输方式。 5. 支持主机和从机模式切换:可以在运行过程中切换主机和从机模式。 总之,STM32 HAL I2C是一种非常实用的工具,可以帮助开发人员快速方便地进行STM32微控制器的I2C总线通信开发。无论是在工业自动化、智能家居、医疗设备等领域,都有着广泛的应用。 ### 回答2: STM32 HAL I2C是为STM32微控制器设计的一个硬件抽象层,可以方便地实现I2C总线的读写操作。I2C(Inter-Integrated Circuit)总线是一种串行通信总线,常用于连接微控制器、传感器、模拟转换器等设备。在使用I2C通信时,我们需要设置一些参数,如设备地址、传输模式、传输速率等。STM32 HAL I2C封装了这些设置,在使用前,只需要初始化相关参数即可。 在STM32 HAL I2C中,我们可以使用一些常用的函数,如I2C_Init()、I2C_Mem_Write()、I2C_Mem_Read()等。其中,I2C_Init()函数用于初始化I2C总线,设置传输模式、速率等参数;I2C_Mem_Write()和I2C_Mem_Read()函数用于在指定的设备地址下,读写指定的寄存器。 此外,STM32 HAL I2C还提供了一些高级函数,如I2C_Master_Transmit()、I2C_Master_Receive()、I2C_Slave_Transmit()、I2C_Slave_Receive()等,可以方便地实现主从模式的通信。 总之,STM32 HAL I2C提供了方便快捷的API接口,简化了I2C总线操作过程,使得开发者可以更加专注于应用程序的开发。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值