I2C总线是Philips公司在八十年代初推出的一种串行、半双工的总线,主要用于近距离、低速的芯片之间的通信;I2C总线有两根双向的信号线,一根数据线SDA用于收发数据,一根时钟线SCL用于通信双方时钟的同步。连接在 I2C总线上的器件分为主机和从机,主机有权发起和结束一次通信,从机只能被动呼叫,每个连接到I2C总线上的器件都有一个唯一的地址。
开发板上引出的I2C接口是这个
不过使用了BSP后我们可以不用关心具体是哪个IO,这些都在BSP里定义好了,接下来使用CYW20829作为I2C主机点亮OLED屏幕,初始化I2C代码,配置为主机模式速度100k,初始化代码如下
cyhal_i2c_t i2c_obj;
void APP_I2CInit(void)
{
cyhal_i2c_cfg_t cfg = {
.address = 0,
.is_slave = false,
.frequencyhal_hz = 100000,
};
cyhal_i2c_init(&i2c_obj,CYBSP_I2C_SDA,CYBSP_I2C_SCL,NULL);
cyhal_i2c_configure(&i2c_obj,&cfg);
}
可以看到用了官方的BSP后初始化外设十分的简便,接下来实现OLED驱动代码,这个OLED是SSD1315驱动的,网上可以搜到具体的驱动代码,或者可以看我过往发的帖子,这里就不放完整代码了,只放上I2C通讯部分
#define YUYY_OLED_SSD1315_I2C_ADDR 0x3C
uint8_t YUYY_OLED_SSD1315_I2CSendDatas(YUYY_OLED_SSD1315_DEV_Type *oled_ssd1315_dev,uint8_t cmd,uint8_t *datas,uint8_t datalen)
{
uint8_t err = 0;
err = cyhal_i2c_master_mem_write(oled_ssd1315_dev->i2cx,YUYY_OLED_SSD1315_I2C_ADDR,cmd,1,datas,datalen,1000);
return err;
}
uint8_t YUYY_OLED_SSD1315_SendCmds(YUYY_OLED_SSD1315_DEV_Type *oled_ssd1315_dev,uint8_t *cmds,uint8_t cmdlen)
{
return YUYY_OLED_SSD1315_I2CSendDatas(oled_ssd1315_dev,0x00,cmds,cmdlen);
}
uint8_t YUYY_OLED_SSD1315_SendDatas(YUYY_OLED_SSD1315_DEV_Type *oled_ssd1315_dev,uint8_t *datas,uint8_t datalen)
{
return YUYY_OLED_SSD1315_I2CSendDatas(oled_ssd1315_dev,0x40,datas,datalen);
}
点亮OLED显示指定字符串
YUYY_OLED_SSD1315_DEV_Type oled_dev;
void APP_OledInit(void)
{
oled_dev.i2cx = &i2c_obj;
YUYY_OLED_SSD1315_Init(&oled_dev);
YUYY_OLED_SSD1315_ClearScreen(&oled_dev);
YUYY_OLED_SSD1315_DisplayString8x16(&oled_dev,0,0,0," CYW20829 TEST");
YUYY_OLED_SSD1315_DisplayString8x16(&oled_dev,0,2,0," I2C OLED");
YUYY_OLED_SSD1315_DisplayString8x16(&oled_dev,0,4,0," bbs.21ic.com");
YUYY_OLED_SSD1315_DisplayString8x16(&oled_dev,0,6,0,"Code by yuyy1989");
}
运行效果
I2C总线上可以挂载多个从设备,接下来就再增加个温湿度传感器,用I2C读取温湿度传感器的数据,并显示在OLED屏幕上
这里用的是DHTC12温湿度传感器,性能如下图
DHTC12驱动代码
#define DHTC12_I2C_ADDR 0x44
typedef struct
{
uint8_t inited;
cyhal_i2c_t *i2cx;
}YUYY_DHTC12_DEV_Type;
static uint16_t HumA,HumB;
uint8_t YUYY_DHTC12_WriteCmd(YUYY_DHTC12_DEV_Type *dhtc12_dev,uint16_t cmd)
{
uint8_t regs[2];
regs[0] = (cmd>>8)&0xFF;
regs[1] = cmd&0xFF;
return cyhal_i2c_master_write(dhtc12_dev->i2cx,DHTC12_I2C_ADDR,regs,2,1000,true);
}
uint8_t YUYY_DHTC12_ReadDatas(YUYY_DHTC12_DEV_Type *dhtc12_dev,uint8_t *rdatas,uint8_t rlen)
{
return cyhal_i2c_master_read(dhtc12_dev->i2cx,DHTC12_I2C_ADDR,rdatas,rlen,1000,true);
}
uint8_t YUYY_DHTC12_ReadCmd(YUYY_DHTC12_DEV_Type *dhtc12_dev,uint16_t cmd,uint8_t *rdatas,uint8_t rlen)
{
uint8_t regs[2];
regs[0] = (cmd>>8)&0xFF;
regs[1] = cmd&0xFF;
cyhal_i2c_master_write(dhtc12_dev->i2cx,DHTC12_I2C_ADDR,regs,2,1000,false);
return cyhal_i2c_master_read(dhtc12_dev->i2cx,DHTC12_I2C_ADDR,rdatas,rlen,1000,true);
}
//初始化,获取湿度运算系数
uint8_t YUYY_DHTC12_Init(YUYY_DHTC12_DEV_Type *dhtc12_dev)
{
uint8_t x,err = 0,retry = 3;
dhtc12_dev->inited = 0;
//复位传感器
err = YUYY_DHTC12_WriteCmd(dhtc12_dev,0x30A2);
err = YUYY_DHTC12_ReadCmd(dhtc12_dev,0xD208,&x,1);
HumA = (x<<8)&0xFF00;
err = YUYY_DHTC12_ReadCmd(dhtc12_dev,0xD209,&x,1);
HumA |= x;
err = YUYY_DHTC12_ReadCmd(dhtc12_dev,0xD20A,&x,1);
HumB = (x<<8)&0xFF00;
err = YUYY_DHTC12_ReadCmd(dhtc12_dev,0xD20B,&x,1);
HumB |= x;
//HumA=0x7168; HumB=0x2D73;
//ReadDatSH[6]={0xF3, 0xA9, 0xC0, 0x41, 0xB4, 0x87}
//27.6 48.4%
dhtc12_dev->inited = 1;
return 0;
}
const uint16_t POLYNOMIAL = 0x131; //P(x)=x^8+x^5+x^4+1 = 100110001
uint8_t YUYY_DHTC12_CheckCrc(uint8_t Data[], uint8_t nbrOfBytes)
{
uint8_t crc = 0xff; //0
uint8_t byteCtr,Bit;
//calculates 8-Bit checksum with given polynomial
for (byteCtr = 0; byteCtr < nbrOfBytes; ++byteCtr)
{
crc ^= (Data[byteCtr]);
for (Bit = 8; Bit > 0; --Bit)
{
if (crc & 0x80)
crc = (crc << 1) ^ POLYNOMIAL;
else
crc = (crc << 1);
}
}
if (crc != Data[nbrOfBytes])
{
Data[nbrOfBytes] = crc;
return 0x01;
}
else
return 0;
}
//单次触发温湿度测量
uint8_t YUYY_DHTC12_ReadHT(YUYY_DHTC12_DEV_Type *dhtc12_dev,int16_t *tem,int16_t *hum)
{
uint8_t i=0,errRe,ReadDatSH[6],CalCRC[3],errorflag=0;
int16_t TemBuf;
uint16_t CapBuf;
errRe = YUYY_DHTC12_WriteCmd(dhtc12_dev,0x2C10);
if(errRe)
errRe = YUYY_DHTC12_WriteCmd(dhtc12_dev,0x2C10);
if(errRe)
{
return 0x10|errRe; //发送命令失败
}
Cy_SysLib_Delay(100);
errRe = YUYY_DHTC12_ReadDatas(dhtc12_dev,ReadDatSH,6);
if(errRe)
{
return 0x20|errRe; //接收数据失败
}
CalCRC[0] = ReadDatSH[0];
CalCRC[1] = ReadDatSH[1];
CalCRC[2] = ReadDatSH[2];
errorflag = YUYY_DHTC12_CheckCrc(CalCRC,2);
if(errorflag==0)
{
TemBuf = (int16_t)(((ReadDatSH[0]<<8)&0xFF00)|ReadDatSH[1]);
*tem = TemBuf*10/256 + 400;
}
CalCRC[0] = ReadDatSH[3];
CalCRC[1] = ReadDatSH[4];
CalCRC[2] = ReadDatSH[5];
errorflag <<= 1;
errorflag |= YUYY_DHTC12_CheckCrc(CalCRC,2);
if(errorflag==0)
{
CapBuf = (uint16_t)(((ReadDatSH[3]<<8)&0xFF00)|ReadDatSH[4]);
*hum = (CapBuf-HumB)*600/(HumA-HumB)+300;
//20℃为5个湿度点 即1℃为0.25个湿度点 0.1℃为0.025
*hum = *hum+ 2.5*(*tem-250);
if(*hum>1000)
*hum = 999;
else if(*hum<0)
*hum = 0;
}
return errorflag;
}
初始化,读取温湿度并显示在OLED上
YUYY_DHTC12_DEV_Type dhtc12_dev;
void APP_DHTC12Init(void)
{
dhtc12_dev.i2cx = &i2c_obj;
YUYY_DHTC12_Init(&dhtc12_dev);
}
int main(void)
{
cy_rslt_t result;
uint8_t printf_count = 0;
int16_t temp,humi;
char out[20];
result = cybsp_init();
if (result != CY_RSLT_SUCCESS)
{
CY_ASSERT(0);
}
app_uart_init();
APP_I2CInit();
printf("APP Start\n");
/* Enable global interrupts */
__enable_irq();
for (;;)
{
Cy_SysLib_Delay(1000);
printf_count += 1;
if(!YUYY_DHTC12_ReadHT(&dhtc12_dev,&temp,&humi))
{
sprintf(out,"T:%03.1f H:%03.1f%%\n",temp/10.0,humi/10.0);
YUYY_OLED_SSD1315_DisplayString8x16(&oled_dev,0,4,0,out);
}
}
}
运行效果
除了以上的设备之外,还有很多其他的设备(例如气压传感器、EEPROM、LED驱动器等)可以使用I2C通讯控制,有兴趣的伙伴可以自行尝试
|
此文章已获得独家原创/原创奖标签,著作权归21ic所有,未经允许禁止转载。
|