网站建设服务器维护内容百度应用商店app
设置为开漏输出的好处是防止因为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 0x78iic2590_sendbyte(0x78);// 3. ACKiic2590_receiveack();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据iic2590_sendbyte(0x00);// 5. ACKiic2590_receiveack();//6. 写入指令/数据iic2590_sendbyte(dataCmd);//7. ACKiic2590_receiveack();//8. STOPiic2590_stop();
}
void Oled_Write_Data(char dataData)
{// 1. start()iic2590_start();// 2. 写入从机地址 b0111 1000 0x78iic2590_sendbyte(0x78);// 3. ACKiic2590_receiveack();// 4. cotrol byte: (0)(0)000000 写入命令 (0)(1)000000写入数据iic2590_sendbyte(0x40);// 5. ACKiic2590_receiveack();//6. 写入指令/数据iic2590_sendbyte(dataData);//7. ACKiic2590_receiveack();//8. STOPiic2590_stop();
}
void Oled_Init(void)
{iic2590_init();Oled_Write_Cmd(0xAE);//--display offOled_Write_Cmd(0x00);//---set low column addressOled_Write_Cmd(0x10);//---set high column addressOled_Write_Cmd(0x40);//--set start line address Oled_Write_Cmd(0xB0);//--set page addressOled_Write_Cmd(0x81); // contract controlOled_Write_Cmd(0xFF);//--128 Oled_Write_Cmd(0xA1);//set segment remap Oled_Write_Cmd(0xA6);//--normal / reverseOled_Write_Cmd(0xA8);//--set multiplex ratio(1 to 64)Oled_Write_Cmd(0x3F);//--1/32 dutyOled_Write_Cmd(0xC8);//Com scan directionOled_Write_Cmd(0xD3);//-set display offsetOled_Write_Cmd(0x00);//Oled_Write_Cmd(0xD5);//set osc divisionOled_Write_Cmd(0x80);//Oled_Write_Cmd(0xD8);//set area color mode offOled_Write_Cmd(0x05);//Oled_Write_Cmd(0xD9);//Set Pre-Charge PeriodOled_Write_Cmd(0xF1);//Oled_Write_Cmd(0xDA);//set com pin configuartionOled_Write_Cmd(0x12);//Oled_Write_Cmd(0xDB);//set VcomhOled_Write_Cmd(0x30);//Oled_Write_Cmd(0x8D);//set charge pump enableOled_Write_Cmd(0x14);//Oled_Write_Cmd(0xAF);//--turn on oled panel
}
void Oled_Clear(void)
{unsigned char i,j; //-128 --- 127for(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// 0xB0Oled_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// 0xB1Oled_Write_Cmd(0xB1);Oled_Write_Cmd(0x00);Oled_Write_Cmd(0x10);for(int i=0;i<8;i++){Oled_Write_Data(A2[i]);}}
}