通过STM32CubeMX配置I2C。
复制相关文件到工程文件夹
保留对应的显示屏型号,如SSD1306,就保留u8x8_d_ssd1306_128x64_noname.c,其它u8x8_d_XXXX.c都删掉。
GPIO和延迟回调该函数用于设置和复位 GPIO(如果是软件实现的接口),例如软件 I2C、SPI、8080 或 6800 接口。此外,此函数用于实现引脚时序的忙等待延迟。
HAL 发送的消息分为三类。
-
“U8X8_MSG_DELAY_”形式的延迟消息。这些消息用于为 I2C、SPI 等的软件实现提供延迟。
为了使软件(又名 bit-banged)接口工作,您需要实现 MCU 特定的忙等待循环以提供正确的延迟量.
对于示例实现,我使用了 CyDelay* 形式的赛普拉斯 PSoC 特定延迟函数 -
“U8X*_MSG_GPIO”形式的GPIO 消息。这些消息用于将 1 和 0 写入用于连接设备的 GPIO。即 SCL/SDA 或 Reset 或 CS 等。
对于示例实现,我使用了 Cypress 引脚写入函数,这些函数都采用“pinname_Write()”的形式。 -
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_spi | 9 位“三针”通信(SCK、MOSI、CS) |
u8x8_byte_8bit_6800mode | 并行接口,6800格式 |
u8x8_byte_8bit_8080mode | 并行接口,8080格式 |
u8x8_byte_sw_i2c | 两线制,I2C 通信 |
u8x8_byte_ks0108 | KS0108控制器专用接口 |
上述函数使用了你定义的 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_ptr ,arg_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 */
}
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)