IIC协议-HAL库
设置为开漏输出的好处是防止因为SDA是半双工,在没有同步好时同时输出出现的问题,CPU输出高电平,被控IC输出低电平时导致短路
SDA和SCL的上拉电阻非常重要!!!!!(在释放SDA/SCL,输出高阻态时自动拉到高电平)
时序:
接收就是发送的反过程,不过接收要释放SDA,SCL仍由主机控制
如果主机不想后续读了,就发非应答即可
每个IIC设备在出厂前都会给一个7位的地址,有些IIC设备的地址是可以修改某些位的的有对应的A0/A1/A2..引脚即可
指定地址写:
MPU6050为例
从机地址+读写位(用来确定主机是要读还是写)共八位
当前地址读:
(只能读当前地址指针下的地址,默认是0,如果你之前写过,当前地址指针会自增1)
(这个用的不多,因为不能指定读哪个地址)
那么聪明的你肯定猜到了怎么指定地址读了吧
那肯定是指定写哪个寄存器然后重新开始Sr(start repeat),然后开始读,那不就完成了指定地址读了吗
软件模拟代码及OLED使用:
#include "sys.h"
#include "delay.h"
void iic2590_w_scl(int value)
{
if (value == 0) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);
else HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);
}
void iic2590_w_sda(int value)
{
if (value == 0) HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
else HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);
}
uint8_t iic2590_r_sda(void)
{
uint8_t BitValue;
BitValue = HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);
return BitValue;
}
void iic2590_init(void)
{
//GPIOB1 SDA
//GPIOB0 SCL
__HAL_RCC_GPIOA_CLK_ENABLE();
GPIO_InitTypeDef GPIOBInit;
GPIOBInit.Mode = GPIO_MODE_OUTPUT_OD;
GPIOBInit.Pin = GPIO_PIN_0 | GPIO_PIN_1;
GPIOBInit.Pull = GPIO_PULLUP;
GPIOBInit.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(GPIOB,&GPIOBInit);
}
void iic2590_start(void)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);
}
void iic2590_stop(void)
{
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_SET);
HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET);
}
void iic2590_sendbyte(uint8_t Byte)
{
for (int i =0;i<8;i++)
{
iic2590_w_sda(Byte & (0x80 >> i));
iic2590_w_scl(1);
iic2590_w_scl(0);
}
}
uint8_t iic2590_receivebyte(void)
{
uint8_t Byte = 0x00;
iic2590_w_sda(1);
for (int i=0;i<8;i++)
{
iic2590_w_scl(1);
if (iic2590_r_sda() == 1) Byte |= (0x80 >> i);
iic2590_w_scl(0);
}
return Byte;
}
void iic2590_sendack(uint8_t ackbit)
{
iic2590_w_sda(ackbit);
iic2590_w_scl(1);
iic2590_w_scl(0);
}
uint8_t iic2590_receiveack(void)
{
uint8_t ackbit;
iic2590_w_sda(1);
iic2590_w_scl(1);
ackbit = iic2590_r_sda();
iic2590_w_scl(0);
return ackbit;
}
void Oled_Write_Cmd(char dataCmd)
{
// 1. start()
iic2590_start();
// 2. 写入从机地址 b0111 1000 0x78
iic2590_sendbyte(0x78);
// 3. ACK
iic2590_receiveack();
// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
iic2590_sendbyte(0x00);
// 5. ACK
iic2590_receiveack();
//6. 写入指令/数据
iic2590_sendbyte(dataCmd);
//7. ACK
iic2590_receiveack();
//8. STOP
iic2590_stop();
}
void Oled_Write_Data(char dataData)
{
// 1. start()
iic2590_start();
// 2. 写入从机地址 b0111 1000 0x78
iic2590_sendbyte(0x78);
// 3. ACK
iic2590_receiveack();
// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据
iic2590_sendbyte(0x40);
// 5. ACK
iic2590_receiveack();
//6. 写入指令/数据
iic2590_sendbyte(dataData);
//7. ACK
iic2590_receiveack();
//8. STOP
iic2590_stop();
}
void Oled_Init(void)
{
iic2590_init();
Oled_Write_Cmd(0xAE);//--display off
Oled_Write_Cmd(0x00);//---set low column address
Oled_Write_Cmd(0x10);//---set high column address
Oled_Write_Cmd(0x40);//--set start line address
Oled_Write_Cmd(0xB0);//--set page address
Oled_Write_Cmd(0x81); // contract control
Oled_Write_Cmd(0xFF);//--128
Oled_Write_Cmd(0xA1);//set segment remap
Oled_Write_Cmd(0xA6);//--normal / reverse
Oled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)
Oled_Write_Cmd(0x3F);//--1/32 duty
Oled_Write_Cmd(0xC8);//Com scan direction
Oled_Write_Cmd(0xD3);//-set display offset
Oled_Write_Cmd(0x00);//
Oled_Write_Cmd(0xD5);//set osc division
Oled_Write_Cmd(0x80);//
Oled_Write_Cmd(0xD8);//set area color mode off
Oled_Write_Cmd(0x05);//
Oled_Write_Cmd(0xD9);//Set Pre-Charge Period
Oled_Write_Cmd(0xF1);//
Oled_Write_Cmd(0xDA);//set com pin configuartion
Oled_Write_Cmd(0x12);//
Oled_Write_Cmd(0xDB);//set Vcomh
Oled_Write_Cmd(0x30);//
Oled_Write_Cmd(0x8D);//set charge pump enable
Oled_Write_Cmd(0x14);//
Oled_Write_Cmd(0xAF);//--turn on oled panel
}
void Oled_Clear(void)
{
unsigned char i,j; //-128 --- 127
for(i=0;i<8;i++)
{
Oled_Write_Cmd(0xB0 + i);
//page0--page7,哪一页1011 0xxx只有后三位代表页,前面都为固定格式
//每个page从0列
Oled_Write_Cmd(0x00);//0000 (0000),列地址的低4位是括号内的
Oled_Write_Cmd(0x10);//0001 (0000),列地址的高4位是括号内的
//0到127列,依次写入0,每写入数据,列地址自动偏移
for(j = 0;j<128;j++)
{
Oled_Write_Data(0);
}
}
}
void Oled_show(char value)
{
if (value == 'A')
{
Oled_Clear();
/*-- 文字: A --*/
/*-- 宋体12; 此字体下对应的点阵为:宽x高=8x16 --*/
char A1[8] = {0x00,0x00,0xC0,0x38,0xE0,0x00,0x00,0x00};
char A2[8] = {0x20,0x3C,0x23,0x02,0x02,0x27,0x38,0x20};
//2.2 选择PAGE0 1011 0000
// 0xB0
Oled_Write_Cmd(0xB0);
Oled_Write_Cmd(0x00);
Oled_Write_Cmd(0x10);
for(int i=0;i<8;i++)
{
Oled_Write_Data(A1[i]);
}
//2.2 选择PAGE1 1011 0001
// 0xB1
Oled_Write_Cmd(0xB1);
Oled_Write_Cmd(0x00);
Oled_Write_Cmd(0x10);
for(int i=0;i<8;i++)
{
Oled_Write_Data(A2[i]);
}
}
}