1:硬件设计
![image-20240602150812067](https://boreyun.oss-cn-shanghai.aliyuncs.com/image-20240602150812067.png)
我们使用4针,IIC接口的OLED 来驱动设备,
| 引脚编号 | 引脚名称 | 芯片IO | 说明 |
| -------- | -------- | -------- | --------- |
| 1 | GND | 电源负极 | 电源地 |
| 2 | VCC | 电源正极 | 3.3伏电压 |
| 3 | SCL | PB9 | 时钟引脚 |
| 4 | SDA | PB8 | 数据引脚 |
2:软件设计
使用cubemx配置引脚状态
![image-20240602151040421](https://boreyun.oss-cn-shanghai.aliyuncs.com/image-20240602151040421.png)
编写代码,使用模拟IIC来实现:
```C
/**
*
@brief OLED引脚初始化
* @param 无
* @retval 无
* @note 当上层函数需要初始化时,此函数会被调用,
* 用户需要将SCL和SDA引脚初始化为开漏模式,并释放引脚
*/
void OLED_GPIO_Init(void)
{
uint32_t i, j;
/*在初始化前,加入适量延时,待OLED供电稳定*/
for (i = 0; i < 1000; i++) {
for (j = 0; j < 1000; j++)
;
}
#ifdef OLED_USE_SW_I2C
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOC时钟
GPIO_InitTypeDef GPIO_InitStruct = {0}; // 定义结构体配置GPIO
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 设置GPIO模式为开漏输出模式
GPIO_InitStruct.Pull = GPIO_PULLUP; // 内部上拉电阻
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // 设置GPIO速度为高速
GPIO_InitStruct.Pin = OLED_SDA; // 设置引脚
HAL_GPIO_Init(OLED_SCL_GPIO_Port, &GPIO_InitStruct);// 初始化GPIO
GPIO_InitStruct.Pin = OLED_SCL;
HAL_GPIO_Init(OLED_SDA_GPIO_Port, &GPIO_InitStruct);
/*释放SCL和SDA*/
OLED_W_SCL(1);
OLED_W_SDA(1);
#endif
}
static void delay_us(__IO uint32_t nus)
{
while(nus--)
{
__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();__nop();
__nop();__nop();__nop();__nop();__nop();__nop();
}
}
static void delay_ms(__IO uint32_t nms)
{
while(nms--)
{
delay_us(996);
}
}
void OLED_SDA_OUT(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOC时钟
GPIO_InitTypeDef GPIO_InitStruct = {0}; // 定义结构体配置GPIO
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_OD; // 设置GPIO模式为开漏输出模式
GPIO_InitStruct.Pull = GPIO_PULLUP; // 内部上拉电阻
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // 设置GPIO速度为高速
GPIO_InitStruct.Pin = OLED_SDA; // 设置引脚
HAL_GPIO_Init(OLED_SCL_GPIO_Port, &GPIO_InitStruct);// 初始化GPIO
}
void OLED_SDA_IN(void)
{
__HAL_RCC_GPIOB_CLK_ENABLE(); // 使能GPIOC时钟
GPIO_InitTypeDef GPIO_InitStruct = {0}; // 定义结构体配置GPIO
GPIO_InitStruct.Mode = GPIO_MODE_INPUT; // 设置GPIO模式为开漏输出模式
GPIO_InitStruct.Pull = GPIO_PULLUP; // 内部上拉电阻
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; // 设置GPIO速度为高速
GPIO_InitStruct.Pin = OLED_SDA; // 设置引脚
HAL_GPIO_Init(OLED_SCL_GPIO_Port, &GPIO_InitStruct);// 初始化GPIO
}
void OLED_IIC_SDA(uint8_t BitValue)
{
OLED_W_SDA(BitValue);
}
void OLED_IIC_SCL(uint8_t BitValue)
{
OLED_W_SCL(BitValue);
}
//产生IIC起始信号
void OLED_IIC_Start(void)
{
OLED_SDA_OUT(); //sda线输出
OLED_IIC_SDA(1);
OLED_IIC_SCL(1);
delay_us(4);
OLED_IIC_SDA(0);//START:when CLK is high,DATA change form high to low
delay_us(4);
OLED_IIC_SCL(0);//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void OLED_IIC_Stop(void)
{
OLED_SDA_OUT();//sda线输出
OLED_IIC_SCL(0);
OLED_IIC_SDA(0);//STOP:when CLK is high DATA change form low to high
delay_us(4);
OLED_IIC_SCL(1);
OLED_IIC_SDA(1);//发送I2C总线结束信号
delay_us(4);
}
#define OLED_READ_SDA HAL_GPIO_ReadPin(GPIOB, OLED_SDA)
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
unsigned char OLED_IIC_Wait_Ack(void)
{
unsigned char ucErrTime=0;
OLED_SDA_IN(); //SDA设置为输入
OLED_IIC_SDA(1);delay_us(1);
OLED_IIC_SCL(1);delay_us(1);
while(OLED_READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
OLED_IIC_Stop();
return 1;
}
}
OLED_IIC_SCL(0);//时钟输出0
return 0;
}
//产生ACK应答
void OLED_IIC_Ack(void)
{
OLED_IIC_SCL(0);
OLED_SDA_OUT();
OLED_IIC_SDA(0);
delay_us(2);
OLED_IIC_SCL(1);
delay_us(2);
OLED_IIC_SCL(0);
}
//不产生ACK应答
void OLED_IIC_NAck(void)
{
OLED_IIC_SCL(0);
OLED_SDA_OUT();
OLED_IIC_SDA(1);
delay_us(2);
OLED_IIC_SCL(1);
delay_us(2);
OLED_IIC_SCL(0);
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void OLED_IIC_Send_Byte(unsigned char txd)
{
unsigned char t;
OLED_SDA_OUT();
OLED_IIC_SCL(0);//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
OLED_IIC_SDA((txd&0x80)>>7);
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的
OLED_IIC_SCL(1);
delay_us(2);
OLED_IIC_SCL(0);
delay_us(2);
}
OLED_IIC_Wait_Ack();
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
unsigned char OLED_IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
OLED_SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
OLED_IIC_SCL(0);
delay_us(2);
OLED_IIC_SCL(1);
receive<<=1;
if(OLED_READ_SDA)receive++;
delay_us(1);
}
if (!ack)
OLED_IIC_NAck();//发送nACK
else
OLED_IIC_Ack(); //发送ACK
return receive;
}
/*********************OLED写数据************************************/
void OLED_WrDat(unsigned char IIC_Data)
{
OLED_IIC_Start();
OLED_IIC_Send_Byte(OLED_ADDRESS);
OLED_IIC_Send_Byte(0x40); // write data
OLED_IIC_Send_Byte(IIC_Data);
OLED_IIC_Stop();
}
/*********************OLED写命令************************************/
void OLED_WrCmd(unsigned char IIC_Command)
{
OLED_IIC_Start();
OLED_IIC_Send_Byte(OLED_ADDRESS);
OLED_IIC_Send_Byte(0x00); // write command
OLED_IIC_Send_Byte(IIC_Command);
OLED_IIC_Stop();
}
/*********************OLED 设置坐标************************************/
void OLED_Set_Pos(unsigned char x, unsigned char y)
{
OLED_WrCmd(0xb0+y);
OLED_WrCmd(((x&0xf0)>>4)|0x10);
OLED_WrCmd((x&0x0f)|0x01);
}
/*********************OLED全屏************************************/
void OLED_Fill(unsigned char bmp_dat)
{
unsigned char y,x;
for(y=0;y<8;y++)
{
OLED_WrCmd(0xb0+y);
OLED_WrCmd(0x01);
OLED_WrCmd(0x10);
for(x=0;x