跟I2C和OLED杠上了,之前的两篇笔记也是I2C和oled
硬件配置
- 这个就不多说了,和之前的一样,一个主控,一个oled
软件编程
oled有很多库,,这次使用u8g2库来驱动oled,像这样类似的库有很多,比如Adafruit_SSD1306,Adafruit_SH1106,SSD1306Ascii,OLED_I2C等等
模拟I2C移植说明
- GPIO模拟I2C移植说明
最好再下载一份stm32对应是例程:地址,例程用的是SPI,只需要看main函数执行过程就可以了
- 包含头文件,创建实例对象,,进行宏定义
/*把下载的库文件中csrc放到U8g2文件夹下,工程中也要进行包含*/
#include "U8g2/u8g2.h"
u8g2_t u8g2;
/* 定义I2C引脚 */
#define OLED_SCL_Port GPIOB
#define OLED_SDA_Port GPIOB
#define OLED_SCL_Pin GPIO_PIN_6
#define OLED_SDA_Pin GPIO_PIN_7
- 初始化oled的时候要调用接口函数,还要配置GPIO端口
例如:在初始化OLED的时候要调用u8x8_Setup(&u8x8, u8x8_d_ssd1306_128x64_noname, u8x8_cad_ssd13xx_i2c, u8x8_byte_sw_i2c, psoc_gpio_and_delay_cb);
其中的u8x8_Setup
是u8g2的接口,u8x8
是创建的一个oled实例对象,u8x8_d_ssd1306_128x64_noname
是所使用的驱动函数,这个在官方文件里的csrc中有,需要哪个就用哪个,u8x8_byte_sw_i2c
这个是u8g2中u8x8_byte中的函数,psoc_gpio_and_delay_cb
这个是GPIO和延时的回调函数。
void OLED_INIT(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
__HAL_RCC_GPIOB_CLK_ENABLE();
//GPIO引脚初始化设置
GPIO_InitStructure.Pin = GPIO_PIN_6|GPIO_PIN_7;
GPIO_InitStructure.Mode = GPIO_MODE_OUTPUT_OD;
GPIO_InitStructure.Pull = GPIO_PULLUP;
GPIO_InitStructure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOB, &GPIO_InitStructure);
u8g2_Setup_ssd1306_i2c_128x64_noname_1(&u8g2, U8G2_R0, u8x8_byte_sw_i2c, \
u8g2_gpio_and_delay_stm32);
u8g2_InitDisplay(&u8g2); // send init sequence to the display, display is in sleep mode after this,
u8g2_SetPowerSave(&u8g2, 0); // wake up display
/******************** 显示一个对角线 ********************/
u8g2_ClearBuffer(&u8g2);
u8g2_FirstPage(&u8g2);
do
{
u8g2_DrawLine(&u8g2, 0,0, 128, 64);
} while( u8g2_NextPage(&u8g2) );
u8g2_SendBuffer(&u8g2);
u8g2_ClearBuffer(&u8g2);
- 需要自己编写一个GPIO口配置和延时的回调函数
/* 延时和gpio定义 */
uint8_t u8g2_gpio_and_delay_stm32(U8X8_UNUSED u8x8_t *u8x8, \
U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int, \
U8X8_UNUSED void *arg_ptr)
{
switch(msg){
//Initialize SPI peripheral
case U8X8_MSG_GPIO_AND_DELAY_INIT:
{
/* HAL initialization contains all what we need so we can skip this part. */
}
break;
//Function which implements a delay, arg_int contains the amount of ms
case U8X8_MSG_DELAY_MILLI:
__NOP();
break;
//Function which delays 10us
case U8X8_MSG_DELAY_10MICRO:
for (uint16_t n = 0; n < 320; n++)
{
__NOP();
}
break;
//Function which delays 100ns
case U8X8_MSG_DELAY_100NANO:
__NOP();
break;
case U8X8_MSG_DELAY_I2C:// arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
for (uint16_t n = 0; n < 100; n++)
{
__NOP();
}
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
arg_int ? HAL_GPIO_WritePin(OLED_SCL_Port,OLED_SCL_Pin,GPIO_PIN_SET)\
: HAL_GPIO_WritePin(OLED_SCL_Port,OLED_SCL_Pin,GPIO_PIN_RESET);
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
arg_int ? HAL_GPIO_WritePin(OLED_SDA_Port,OLED_SDA_Pin,GPIO_PIN_SET)\
: HAL_GPIO_WritePin(OLED_SDA_Port,OLED_SDA_Pin,GPIO_PIN_RESET);
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; //A message was received which is not implemented, return 0 to indicate an error
}
return 1; // command processed successfully.
}
- 显示LOGO
/* 显示logo */
void draw(u8g2_t *u8g2)
{
u8g2_SetFontMode(u8g2, 1); // Transparent
u8g2_SetFontDirection(u8g2, 0);
u8g2_SetFont(u8g2, u8g2_font_inb21_mf);
u8g2_DrawStr(u8g2, 0, 30, "U");
u8g2_SetFontDirection(u8g2, 1);
u8g2_SetFont(u8g2, u8g2_font_crox3h_tr);
u8g2_DrawStr(u8g2, 21,8,"9");
u8g2_SetFontDirection(u8g2, 0);
u8g2_SetFont(u8g2, u8g2_font_inb21_mf);
u8g2_DrawStr(u8g2, 51,30,"g");
u8g2_DrawStr(u8g2, 67,30,"\xb2");
u8g2_SetFont(u8g2, u8g2_font_6x10_tf);
u8g2_DrawStr(u8g2, 1,54,"github.com/olikraus/u8g2");
}
- 剩下的就简单多了,只需要在主函数初始化oled之后,就随便调用u8g2库中的所有绘画函数
u8g2_FirstPage(&u8g2);
do
{
draw(&u8g2);
} while( u8g2_NextPage(&u8g2) );
硬件I2C移植
硬件的移植不需要配置GPIO,但是需要配置I2C,这里用的是I2C1
0. 包含头文件,创建实例
#include "U8g2/u8g2.h"
u8g2_t u8g2;
- GPIO和延时函数
感觉这个可以不写任何内容,但是不能没有
/* 延时和gpio定义 */
uint8_t u8g2_gpio_and_delay_stm32(U8X8_UNUSED u8x8_t *u8x8, \
U8X8_UNUSED uint8_t msg, U8X8_UNUSED uint8_t arg_int, \
U8X8_UNUSED void *arg_ptr)
{
switch(msg){
//Initialize SPI peripheral
case U8X8_MSG_GPIO_AND_DELAY_INIT:
{
/* HAL initialization contains all what we need so we can skip this part. */
}
break;
//Function which implements a delay, arg_int contains the amount of ms
case U8X8_MSG_DELAY_MILLI:
__NOP();
break;
//Function which delays 10us
case U8X8_MSG_DELAY_10MICRO:
for (uint16_t n = 0; n < 320; n++)
{
__NOP();
}
break;
//Function which delays 100ns
case U8X8_MSG_DELAY_100NANO:
__NOP();
break;
case U8X8_MSG_DELAY_I2C:// arg_int is the I2C speed in 100KHz, e.g. 4 = 400 KHz
for (uint16_t n = 0; n < 100; n++)
{
__NOP();
}
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; //A message was received which is not implemented, return 0 to indicate an error
}
return 1; // command processed successfully.
- 硬件I2C写入操作
uint8_t u8x8_byte_i2c_hw(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:
HAL_I2C_Master_Transmit(&hi2c1,u8x8_GetI2CAddress(u8x8), buffer, \
buf_idx, 10);/*transmit function of HAL library*/
break;
default:
return 0;
}
return 1;
}
- 画图函数
/* 显示logo */
void draw(u8g2_t *u8g2)
{
u8g2_SetFontMode(u8g2, 1); // Transparent
u8g2_SetFontDirection(u8g2, 0);
u8g2_SetFont(u8g2, u8g2_font_inb21_mf);
u8g2_DrawStr(u8g2, 0, 30, "U");
u8g2_SetFontDirection(u8g2, 1);
u8g2_SetFont(u8g2, u8g2_font_crox3h_tr);
u8g2_DrawStr(u8g2, 21,8,"9");
u8g2_SetFontDirection(u8g2, 0);
u8g2_SetFont(u8g2, u8g2_font_inb21_mf);
u8g2_DrawStr(u8g2, 51,30,"g");
u8g2_DrawStr(u8g2, 67,30,"\xb2");
u8g2_SetFont(u8g2, u8g2_font_6x10_tf);
u8g2_DrawStr(u8g2, 1,54,"github.com/olikraus/u8g2");
}
- 主函数
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_I2C1_Init();
OLED_INIT();
while (1)
{
u8g2_FirstPage(&u8g2);
do
{
draw(&u8g2);
} while( u8g2_NextPage(&u8g2) );
}
}
比较
- 模拟的延时比硬件的长,并且可控,通过延时的回调函数中的
U8X8_MSG_DELAY_I2C
进行配置 - 现在u8g2库中的所有函数我都包含进来了,只是去除了一些字库,文件也挺大的,以后想办法精简一下
结语
oled使用库文件可以减少很多步骤,对于初学者有很好的帮助,但是缺少了用寄存器读取的快乐,但是这种快乐也是因人而异吧