#include "HALI2C.h"
#include "gunit.h"
#define BIT0 0X01
#define BIT7 0X80
/IO//
/*2个脚
其中 时钟 一直都是 输出
而 数据 你TX就是 输出 你RX时候就 浮空输入 等待采集信号
并且一般是你设置为高电平在变成输入等待从机拉低*/
void SET_SDA_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C1_SDA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(I2C1_GPIO, &GPIO_InitStruct);
}
void SET_SDA_IN(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C1_SDA_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(I2C1_GPIO, &GPIO_InitStruct);
}
void SET_SCL_OUT(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C1_SCL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(I2C1_GPIO, &GPIO_InitStruct);
}
void SET_SCL_IN(void)/*基本不会使用*/
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C1_SCL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(I2C1_GPIO, &GPIO_InitStruct);
}
/IO//
/HL//
void i2c1_set_scl(void)
{//拉高时钟
HAL_GPIO_WritePin(I2C1_GPIO, I2C1_SCL_Pin, GPIO_PIN_SET);
}
void i2c1_clr_scl(void)
{//拉低时钟
HAL_GPIO_WritePin(I2C1_GPIO, I2C1_SCL_Pin, GPIO_PIN_RESET);
}
void i2c1_set_sda(void)
{//拉高数据
HAL_GPIO_WritePin(I2C1_GPIO, I2C1_SDA_Pin, GPIO_PIN_SET);
}
void i2c1_clr_sda(void)
{//拉低数据
HAL_GPIO_WritePin(I2C1_GPIO, I2C1_SDA_Pin, GPIO_PIN_RESET);
}
/HL//
/US//
void i2c1_delay_bit(void)
{
TIM7_Delay(50);//刘洋老师说的是5US 这个器件不只是为啥需要50US
}
/US//
/ 开始IIC //
/*I2C_Init--操作GPIO-需要的2个脚都是上拉的输出*/
void I2C_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
GPIO_InitStruct.Pin = I2C1_SDA_Pin|I2C1_SCL_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_PULLUP;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(I2C1_GPIO, &GPIO_InitStruct);
i2c1_set_scl();
i2c1_set_sda();
}
/*产生起始信号*/
void I2C_Start(void)
{
SET_SDA_OUT();/*必须设置OUT模式 最后找问题就是这里!!*/
i2c1_set_sda();
i2c1_set_scl();
i2c1_delay_bit();
i2c1_clr_sda();
i2c1_delay_bit();
i2c1_clr_scl();
i2c1_delay_bit();
}
/*产生停止信号 OK*/
void I2C_Stop(void)
{
SET_SDA_OUT();
i2c1_clr_scl();
i2c1_delay_bit();
i2c1_clr_sda();
i2c1_delay_bit();
i2c1_set_scl();
i2c1_delay_bit();
i2c1_set_sda();
i2c1_delay_bit();
}
/*主机产生应答信号ACK 主要是后面还有交互 比如读一串的时候 你拿到一个还没完就ACK*/
void I2C_Ack(void)
{
SET_SDA_OUT();
i2c1_clr_scl();
i2c1_delay_bit();
i2c1_clr_sda();//唯一的区别
i2c1_delay_bit();
i2c1_set_scl();
i2c1_delay_bit();
i2c1_clr_scl();
i2c1_delay_bit();
}
/*主机不产生应答信号NACK 结束的时候*/
void I2C_NAck(void)
{
SET_SDA_OUT();
i2c1_clr_scl();
i2c1_delay_bit();
i2c1_set_sda();//唯一的区别
i2c1_delay_bit();
i2c1_set_scl();
i2c1_delay_bit();
i2c1_clr_scl();
i2c1_delay_bit();
}
uint8_t i2c2_get_sda(void)
{//获得数据电平
//一般是我拉高SDA以后开始转变为浮空输入 等待从机拉低也就是1->0
//所以去读的话返回0就是从机应答了
uint8_t Ret;
SET_SDA_IN();
if (HAL_GPIO_ReadPin(I2C1_GPIO, I2C1_SDA_Pin)== GPIO_PIN_SET)
Ret= 1;
else
Ret= 0;
SET_SDA_OUT();
return Ret;
}
//等待从机应答信号
//返回值:1 接收应答失败
// 0 接收应答成功
uint8_t I2C_Wait_Ack(void)
{
uint8_t ack=0;
i2c1_set_sda();
i2c1_delay_bit();
i2c1_set_scl();
i2c1_delay_bit();
#if 1
SET_SDA_IN();
ack = HAL_GPIO_ReadPin(I2C1_GPIO,I2C1_SDA_Pin);
SET_SDA_OUT();
#else
ack = i2c2_get_sda();
#endif
i2c1_clr_scl();
i2c1_delay_bit();
return ack;
}
void I2C_Send_Byte(uint8_t val)
{
volatile uint8_t mark = BIT7;
uint8_t i=0;
i2c1_clr_scl();//拉低时钟开始数据传输
i2c1_delay_bit();
while(mark)
{
if (val & mark)
i2c1_set_sda();
else
i2c1_clr_sda();
i2c1_delay_bit();
i2c1_set_scl();
i2c1_delay_bit();
i2c1_clr_scl();
i2c1_delay_bit();
mark >>= 1;
}
}
#if 1
unsigned char I2C_Read_Byte(unsigned char ack)
{
unsigned char i=0,receive=0;
SET_SDA_IN();
for(i=0;i<8;i++)
{
i2c1_clr_scl();
i2c1_delay_bit();
i2c1_set_scl();
receive<<=1;
if(HAL_GPIO_ReadPin(I2C1_GPIO,I2C1_SDA_Pin))
receive++;
i2c1_delay_bit();
}
if(ack==0)
I2C_NAck();
else
I2C_Ack();
return receive;
}
//上面是刘洋的写法 下面是陈强的写法 都是可以的 时序前面不是很严格
#else
unsigned char I2C_Read_Byte(unsigned char ack)
{
volatile uint8_t receive = 0;
uint8_t mask = BIT7;
while(mask)
{
i2c1_set_sda();
i2c1_set_scl();
i2c1_delay_bit();
//if(HAL_GPIO_ReadPin(I2C1_GPIO,I2C1_SDA_Pin))//都可以
if(i2c2_get_sda())
receive |= mask;
i2c1_clr_scl();
i2c1_delay_bit();
mask >>= 1;
}
if (ack== TRUE)
i2c1_clr_sda();
else
i2c1_set_sda();
i2c1_delay_bit();
i2c1_set_scl();
i2c1_delay_bit();
i2c1_clr_scl();
i2c1_delay_bit();
return receive;
}
#endif
uint8_t sw_i2c_access_start(uint8_t SlaveAdr, char W0R1)
{
volatile uint8_t dummy=5;
uint8_t ack =0;
if (W0R1 == 1)
SlaveAdr |= BIT0;
else
SlaveAdr &= ~BIT0;
while (dummy--)
{
I2C_Start();
I2C_Send_Byte(SlaveAdr);
ack = I2C_Wait_Ack();printf("sw_i2c_access_start:ack=%d\r\n",ack);
if(ack==0)break;
i2c1_delay_bit();
I2C_Stop();
}
return !ack;/*如果ack=0标识TXRX成功了 我就返回1吧 反之是失败*/
}
/* I2C and EZI2C slave address defines */
#define I2C_I2C_SLAVE_ADDR_POS (0x01u) /* 7-bit address shift */
#define I2C_I2C_SLAVE_ADDR_MASK (0xFEu) /* 8-bit address mask */
/* Return 8-bit address. The input address should be 7-bits */
#define I2C_GET_I2C_8BIT_ADDRESS(addr) (((unsigned long) ((unsigned long) (addr) << I2C_I2C_SLAVE_ADDR_POS)) & I2C_I2C_SLAVE_ADDR_MASK)
uint8_t cy3116_write(uint8_t slaveAdr,uint16_t subAdr,uint8_t *buff,uint16_t bufLen)
{
volatile uint8_t dummy = 20;
uint8_t ack=0;
uint8_t slaveAddress = I2C_GET_I2C_8BIT_ADDRESS(slaveAdr);
while(dummy--)
{
//1--access_start
if (sw_i2c_access_start(slaveAddress, 0) == FALSE)
{
continue;
}
//2--执行TXRX
I2C_Send_Byte(subAdr);
ack = I2C_Wait_Ack();
if(ack!=0)continue;
//3--执行TX
for( uint16_t i =0; i < bufLen ;i++)
{
I2C_Send_Byte(buff[i]);
ack = I2C_Wait_Ack();
if(ack!=0)goto err;
}
break;
}
if( dummy == 0xFF)
{
printf("I2C Timer out\r\n");
}
I2C_Stop();
return 1;
err:
printf("I2C send err\r\n");
return 0;
}
static uint8_t cy3116_read(uint8_t slaveAdr,uint16_t subAdr,uint8_t *buff,uint16_t bufLen)
{
volatile uint8_t dummy = 20;
uint8_t ack=0;
while(dummy--)
{
//1--access_start 写0
if (sw_i2c_access_start(slaveAdr, 0) == FALSE)
{
continue;
}
//2--执行TXRX
I2C_Send_Byte(subAdr);
ack = I2C_Wait_Ack();
if(ack!=0)continue;
//3--access_start 读1
if (sw_i2c_access_start(slaveAdr, 1) == FALSE)
{
continue;
}
//4--开始RX每次收到一个我就ACK一下最后一个就不ACK了
while(bufLen--)
{
*buff = I2C_Read_Byte((bufLen>0)?1:0);
buff++;
}
break;
}
I2C_Stop();
return 1;
}
unsigned char CY8CMBR3116_config[128] = {
0xF8u, 0x7Fu, 0xF8u, 0x7Fu, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x7Fu, 0x7Fu, 0x7Fu, 0x80u,
0x80u, 0x80u, 0x80u, 0x80u, 0x80u, 0x80u, 0x80u, 0x80u,
0x80u, 0x80u, 0x80u, 0x7Fu, 0x03u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x05u, 0x00u, 0x00u, 0x02u, 0x00u, 0x02u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x21u, 0x01u, 0x01u,
0x00u, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu, 0xFFu,
0xFFu, 0x00u, 0x00u, 0x00u, 0x24u, 0x03u, 0x01u, 0x59u,
0x00u, 0x37u, 0x01u, 0x00u, 0x00u, 0x0Au, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u,
0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0x00u, 0xD9u, 0x55u
};
uint8_t saveCmd = 0x02;
uint8_t resert = 0xFF;
char __HTK_I2C_Read_optimal_touch_keyval(unsigned char *pKeyVal)
{
#define DEVICE_ADDR (0x37u)
unsigned char send;
uint8_t id;
char ack=0;
#if 1
cy3116_read((DEVICE_ADDR<<1),0x8F,&id,1);
printf("id=[%d]\r\n",id);
#else
sw_i2c_access_start((DEVICE_ADDR<<1),0);
I2C_Send_Byte(0x8F);
ack = I2C_Wait_Ack();printf("I2C_Wait_Ack ack=%d\r\n",ack);
sw_i2c_access_start((DEVICE_ADDR<<1),1);
printf("[%d]\r\n",I2C_Read_Byte(0));
I2C_Stop();
#endif
cy3116_write( DEVICE_ADDR ,0x00,CY8CMBR3116_config,128);
HAL_Delay(100);
cy3116_write( DEVICE_ADDR ,0x86,&saveCmd,1);
HAL_Delay(100);
cy3116_write( DEVICE_ADDR ,0x86,&resert,1);
HAL_Delay(100);
return 1;
}
/*整理流程是:
I2C_Start--发送器件地址【也就是(DEVICE_ADDR<<1)我们是写就是直接发 因为BIT0此时100%是0】--等待ack = I2C_Wait_Ack()--
此时ack1必须是0 也即是从机必须应答【我拉高的SDA必须从机拉低】如果失败就循环操作
从机应答以后继续发送0X8F这个就是一个文档的命令 发出去 从机一般很久应答
再去发送读芯片ID的命令也就是 I2C_Start--发送器件地址【也就是(DEVICE_ADDR<<1)这里我们是读 不是直接发 需要暴力BIT0为1】--
等待从机回答啥 这个就是ID
如果ID不对 就需要检测延时50US对不对
*/
/*
开始抽象函数 我发消息总是 I2C_Start--I2C_Send_Byte--I2C_Wait_Ack 可以总结一个新函数
OLD:
dummy=5;
while (dummy--)
{
I2C_Start();
send = (DEVICE_ADDR<<1) | BIT0;
I2C_Send_Byte( send ) ;// 发送器件地址 读
ack = I2C_Wait_Ack();printf("ack=%d\r\n",ack);
if(ack==0)break;
i2c1_delay_bit();
I2C_Stop();
}
NEW:
//参数是从机地址 后面0标识写 1标识读
uint8_t sw_i2c_access_start(uint8_t SlaveAdr, char W0R1)
{
volatile uint8_t dummy=5;
uint8_t ack =0;
if (W0R1 == 1)
SlaveAdr |= BIT0;
else
SlaveAdr &= ~BIT0;
while (dummy--)
{
I2C_Start();
I2C_Send_Byte(SlaveAdr);
ack = I2C_Wait_Ack();printf("ack=%d\r\n",ack);
if(ack==0)break;
i2c1_delay_bit();
I2C_Stop();
}
return ack;
}
*/