1. 硬件IIC 实在是太难用了,各种卡死,各种发不出来数据,没那么多时间折腾了,还是用软件IIC 先吧,初始化
void OLED_Software_IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
RCC_AHBPeriphClockCmd(OLED_SOFTWARE_IIC_PORT_B | OLED_SOFTWARE_IIC_AF_CLOCK, ENABLE);
GPIO_InitStruct.GPIO_Pin = OLED_IIC_SDA_PIN_B11 | OLED_IIC_SCL_PIN_B10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_40MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct); // PB11 - SDA, PB10 - SCL
GPIO_SetBits(GPIOB, OLED_IIC_SDA_PIN_B11 | OLED_IIC_SCL_PIN_B10);
OLED_IIC_Stop();
}
2. 一些宏定义,带FreeRTOS 实时操作系统,系统节拍设置为1ms,系统主频设置为32Mhz
#define OLED_USE_SOFTWARE_IIC
#define OLED_SOFTWARE_IIC_PORT_B RCC_AHBPeriph_GPIOB // SCL - PB10, SDA -PB11
#define OLED_SOFTWARE_IIC_AF_CLOCK RCC_APB1Periph_I2C2
#define IIC_TIMEOUT_COUNTER 0x2000 // iic transmit timeout
#define OLED_IIC_SCL_PIN_B10 GPIO_Pin_10
#define OLED_IIC_SDA_PIN_B11 GPIO_Pin_11
#define OLED_SCL_H() GPIO_SetBits(GPIOB, OLED_IIC_SCL_PIN_B10)
#define OLED_SCL_L() GPIO_ResetBits(GPIOB, OLED_IIC_SCL_PIN_B10)
#define OLED_ADDRESS 0x78 // 0x78: device address + write, 0x79 - device address + read
#define IIC_CMD_LEN 2
#define OLED_SDA_H() GPIO_SetBits(GPIOB, OLED_IIC_SDA_PIN_B11)
#define OLED_SDA_L() GPIO_ResetBits(GPIOB, OLED_IIC_SDA_PIN_B11)
#define OLED_SDA_READ() GPIO_ReadInputDataBit(GPIOB, OLED_IIC_SDA_PIN_B11)
3. 延时函数,CPU 阻塞,空转
static void IIC_Delay(void)
{
uint8_t temp;
for (temp = 0; temp < 10; temp++)
{
// do nothing
}
}
4. 起始信号
static void OLED_IIC_Start(void)
{
OLED_SDA_H();
OLED_SCL_H();
IIC_Delay();
OLED_SDA_L();
IIC_Delay();
OLED_SCL_L();
IIC_Delay();
}
5. 停止信号
static void OLED_IIC_Stop(void)
{
OLED_SDA_L();
OLED_SCL_H();
IIC_Delay();
OLED_SDA_H();
IIC_Delay();
}
6. 发送一个字节的数据
static void OLED_IIC_SendByte(uint8_t data)
{
uint8_t index;
GPIO_InitTypeDef GPIO_InitStruct;
for (index = 0; index < 8; index++) // send one byte data, first send MSB (bit[7]), send LSB at the last (bit[0])
{
if (data & 0x80)
{
OLED_SDA_H(); // send 1
}
else
{
OLED_SDA_L(); // send 0
}
IIC_Delay();
OLED_SCL_H();
IIC_Delay();
OLED_SCL_L();
data <<= 1; // send next bit (MSB -> LSB)
IIC_Delay();
}
// OLED_SDA_H();
GPIO_InitStruct.GPIO_Pin = OLED_IIC_SDA_PIN_B11;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOB, &GPIO_InitStruct);
IIC_Delay();
}
7. 发送一个字节数据后等待从机回复一个ACK 信号
uint8_t OLED_IIC_WaitAck(void)
{
uint8_t ret;
OLED_SCL_H();
if (OLED_SDA_READ())
{
ret = 1; // not received ACK signal, wait fail
}
else
{
ret = 0; // received ACK signal, wait success
}
IIC_Delay();
OLED_SCL_L();
IIC_Delay();
return ret;
}
8. GPIO 重新初始化
static void OLED_SoftwareIIC_SDA_Resume(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = OLED_IIC_SDA_PIN_B11;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT;
GPIO_InitStruct.GPIO_OType = GPIO_OType_PP;
GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_40MHz;
GPIO_Init(GPIOB, &GPIO_InitStruct); // PB11 - SDA
}
9. 模仿HAL库写的一个自己的函数,实现功能,发送设备地址+读写控制,再发送0x00,表示发的是命令,再发一个字节数据,具体的内容,数据,共发了三个字节的数据,加了超时退出机制
/*
* imitate the stm32 HAL library, achieve myself's software i2c send function
* deviceAddr: 0x78 - device address and write operation, 0x79 - device address and read operation
*/
static void HAL_I2C_Master_Transmit(I2C_TypeDef *I2Cx, uint8_t deviceAddr, uint8_t buff[], uint16_t buffLen, uint32_t timeout)
{
uint32_t temp;
uint16_t index;
temp = timeout;
OLED_IIC_Start();
IIC_Delay();
OLED_IIC_SendByte(deviceAddr); // device address and write bit
while (OLED_IIC_WaitAck())
{
temp--;
if (0 == temp)
{
return;
}
IIC_Delay();
}
OLED_SoftwareIIC_SDA_Resume();
for (index = 0; index < buffLen; index++)
{
OLED_IIC_SendByte(buff[index]);
IIC_Delay();
while (OLED_IIC_WaitAck())
{
temp--;
if (0 == temp)
{
return;
}
IIC_Delay();
}
OLED_SoftwareIIC_SDA_Resume();
IIC_Delay();
}
OLED_IIC_Stop();
}
10. 向OLED 发送指令函数
static void OLED_SendCmd(uint8_t cmd)
{
uint8_t sendBuff[2];
sendBuff[0] = 0x00;
sendBuff[1] = cmd;
HAL_I2C_Master_Transmit(I2C2, OLED_ADDRESS, sendBuff, IIC_CMD_LEN, IIC_TIMEOUT_COUNTER);
}
11. OLED 初始化函数,OLED 用的龙科显示公式的产品,控制IC 用的是CH1116G,他们给我发是资料却是SH1106的
void OLED_Init(void)
{
delay_xms(200); // oled startup slowly than stm32l151c8t6
INFO_LOG("[OLED_Init] init start\r\n");
OLED_SendCmd(0xAE); // display off
OLED_SendCmd(0x02); // set colum start address, low 4-bits
OLED_SendCmd(0x10); // set colum end address, high 4-bits
OLED_SendCmd(0x40); // set start line (first row)
OLED_SendCmd(0xB0); // set page address
delay_xms(5);
OLED_SendCmd(0x81); // set contrast ratio (对比度)
delay_xms(5);
OLED_SendCmd(0xCF); // 128
delay_xms(5);
OLED_SendCmd(0xA1); // set segment remapping, from right to left
OLED_SendCmd(0xA6); // forward display, normal or reverse
OLED_SendCmd(0xA8); // multiple reuse rate, multiple ratio (多路复用率)
OLED_SendCmd(0x3F); // duty = 1 / 64
OLED_SendCmd(0xAD); // set charge pump enable
OLED_SendCmd(0x8B); // enable DC-DC
OLED_SendCmd(0x33); // set VPP = 10V
OLED_SendCmd(0xC8); // set output scan direction, COM[N - 1] to COM[0], COM scan direction
OLED_SendCmd(0xD3); // set display offset
OLED_SendCmd(0x00); // 0x00
OLED_SendCmd(0xD5); // set internal clock frequence, set osc frequency
OLED_SendCmd(0xC0);
OLED_SendCmd(0xD9); // set pre-charge period
OLED_SendCmd(0x1F); // 0x22
OLED_SendCmd(0xDA); // set COM pins, pin layout
OLED_SendCmd(0x12);
OLED_SendCmd(0xDB); // set electrical level, set VCOMH
OLED_SendCmd(0x40);
OLED_SendCmd(0xAF); // enable display, display on
INFO_LOG("[OLED_Init][alfred01] init complete\r\n");
INFO_LOG("[OLED_Init] init complete\r\n");
}
12. OLED 测试函数
void OLED_Test(void)
{
OLED_SendCmd(0xB0); // page 0
OLED_SendCmd(0x02); // colume 0, low 4-bits
OLED_SendCmd(0x10); // colume 0, high 4-bits
uint8_t sendBuff[] = {0x40, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA};
HAL_I2C_Master_Transmit(I2C2, OLED_ADDRESS, sendBuff, sizeof(sendBuff), IIC_TIMEOUT_COUNTER);
}
13. OLED 初步显示效果,表示控制驱动是OK的了,其它的就是慢慢细调了
14. OLED 规格书,控制芯片用的CH1116G,这是硬件发给我,和厂家给的不一致,厂家说是兼容的,那我就直接上手调了
15. 看这三个引脚接的是高电平还是低电平,怎么组合的,就知道用的什么接口,需要看原理图了
需要看原理图了
16. 逻分仪抓的OLED 初始化时序图