Files
2025-12-03 11:12:34 +08:00

391 lines
11 KiB
C

#ifdef SUPPORT_MS_EXTENSIONS
#pragma bss_seg(".iic.bss")
#pragma data_seg(".iic.data")
#pragma const_seg(".iic.const")
#pragma code_seg(".iic.text")
#pragma str_literal_override(".iic.text")
#endif /* #ifdef SUPPORT_MS_EXTENSIONS */
#include "includes.h"
#include "gpio.h"
#include "iic_soft.h"
static u8 soft_iic_state[MAX_SOFT_IIC_NUM];
static struct iic_master_config soft_iic_cfg_cache[MAX_SOFT_IIC_NUM];
//input pull up
#define IIC_SCL_H(scl) \
gpio_set_direction(scl, 1)
#define IIC_SCL_L(scl) \
gpio_set_direction(scl, 0); \
gpio_set_output_value(scl, 0)
#define IIC_SDA_DIR(sda, val) \
gpio_set_direction(sda, val)
//input pull up
#define IIC_SDA_H(sda) \
gpio_set_direction(sda, 1)
#define IIC_SDA_L(sda) \
gpio_set_direction(sda, 0); \
gpio_set_output_value(sda, 0)
#define IIC_SDA_READ(sda) \
gpio_read(sda)
#define soft_iic_delay(num) \
delay_cnt= num; \
while (delay_cnt--) { \
asm("nop"); \
}
static inline u32 iic_get_delay(soft_iic_dev iic)
{
u32 hsb_clk = 16000000;//clk_get("sys");
u32 delay_num = 0;
return delay_num;
}
extern const struct iic_master_config soft_iic_cfg_const[MAX_SOFT_IIC_NUM];
struct iic_master_config *get_soft_iic_config(soft_iic_dev iic)
{
return (struct iic_master_config *)&soft_iic_cfg_const[iic];
}
enum iic_state_enum soft_iic_init(soft_iic_dev iic, struct iic_master_config *i2c_config)
{
ASSERT(iic < MAX_SOFT_IIC_NUM, "iic > MAX_SOFT_IIC_NUM");
if (i2c_config == NULL) {
printf("error: soft iic%d param error!\n", iic);
return IIC_ERROR_PARAM_ERROR;
}
if (iic >= MAX_SOFT_IIC_NUM) {
printf("error: soft iic index:%d error!\n", iic);
return IIC_ERROR_INDEX_ERROR;
}
if ((soft_iic_state[iic]&BIT(7)) != 0) {
printf("error: soft iic%d has been occupied!\n", iic);
return IIC_ERROR_INIT_FAIL;
}
soft_iic_state[iic] = BIT(7);//init ok
memcpy(&soft_iic_cfg_cache[iic], i2c_config, sizeof(struct iic_master_config));
//freq:
if (i2c_config->io_mode) {//pull up
gpio_set_pull_up(i2c_config->scl_io, 1);
gpio_set_pull_up(i2c_config->sda_io, 1);
} else {
gpio_set_pull_up(i2c_config->scl_io, 0);
gpio_set_pull_up(i2c_config->sda_io, 0);
}
gpio_set_hd(i2c_config->scl_io, i2c_config->hdrive);
gpio_set_hd(i2c_config->sda_io, i2c_config->hdrive);
IIC_SDA_H(i2c_config->sda_io);
IIC_SCL_H(i2c_config->scl_io);
gpio_set_pull_down(i2c_config->scl_io, 0);
gpio_set_pull_down(i2c_config->sda_io, 0);
gpio_set_die(i2c_config->scl_io, 1);//en 1.1v
gpio_set_die(i2c_config->sda_io, 1);//en 1.1v
/* gpio_set_dieh(i2c_config->scl_io, 1);//en 3.3v */
/* gpio_set_dieh(i2c_config->sda_io, 1);//en 3.3v */
return IIC_OK;//ok
}
enum iic_state_enum soft_iic_deinit(soft_iic_dev iic)
{
ASSERT(iic < MAX_SOFT_IIC_NUM, "iic > MAX_SOFT_IIC_NUM");
if (soft_iic_state[iic] == 0) {
printf("error: soft iic%d has been no init!\n", iic);
return IIC_ERROR_NO_INIT;
}
soft_iic_state[iic] = 0;//no init
gpio_set_direction(soft_iic_cfg_cache[iic].scl_io, 1);
gpio_set_direction(soft_iic_cfg_cache[iic].sda_io, 1);
gpio_set_pull_up(soft_iic_cfg_cache[iic].scl_io, 0);
gpio_set_pull_up(soft_iic_cfg_cache[iic].sda_io, 0);
gpio_set_die(soft_iic_cfg_cache[iic].scl_io, 0);
gpio_set_die(soft_iic_cfg_cache[iic].sda_io, 0);
/* gpio_set_dieh(i2c_config->scl_io, 0);//en 3.3v */
/* gpio_set_dieh(i2c_config->sda_io, 0);//en 3.3v */
gpio_set_hd(soft_iic_cfg_cache[iic].scl_io, PORT_DRIVE_STRENGT_2p4mA);
gpio_set_hd(soft_iic_cfg_cache[iic].sda_io, PORT_DRIVE_STRENGT_2p4mA);
return IIC_OK;//ok
}
enum iic_state_enum soft_iic_suspend(soft_iic_dev iic)
{
ASSERT(iic < MAX_SOFT_IIC_NUM, "iic > MAX_SOFT_IIC_NUM");
if ((soft_iic_state[iic] & 0xc0) != 0x80) {
printf("error: soft iic%d is no init or suspend!\n", iic);
return IIC_ERROR_SUSPEND_FAIL;
}
if ((soft_iic_state[iic] & 0x3f) != 0) {
printf("error: soft iic%d is busy!\n", iic);
return IIC_ERROR_BUSY;
}
soft_iic_state[iic] |= BIT(6);//suspend ok
gpio_set_direction(soft_iic_cfg_cache[iic].scl_io, 1);
gpio_set_direction(soft_iic_cfg_cache[iic].sda_io, 1);
gpio_set_pull_up(soft_iic_cfg_cache[iic].scl_io, 0);
gpio_set_pull_up(soft_iic_cfg_cache[iic].sda_io, 0);
gpio_set_die(soft_iic_cfg_cache[iic].scl_io, 0);
gpio_set_die(soft_iic_cfg_cache[iic].sda_io, 0);
/* gpio_set_dieh(i2c_config->scl_io, 0);//en 3.3v */
/* gpio_set_dieh(i2c_config->sda_io, 0);//en 3.3v */
gpio_set_hd(soft_iic_cfg_cache[iic].scl_io, PORT_DRIVE_STRENGT_2p4mA);
gpio_set_hd(soft_iic_cfg_cache[iic].sda_io, PORT_DRIVE_STRENGT_2p4mA);
return IIC_OK;//ok
}
enum iic_state_enum soft_iic_resume(soft_iic_dev iic)
{
ASSERT(iic < MAX_SOFT_IIC_NUM, "iic > MAX_SOFT_IIC_NUM");
if ((soft_iic_state[iic] & 0xc0) != 0xc0) {
printf("error: soft iic%d is no init or no suspend!\n", iic);
return IIC_ERROR_RESUME_FAIL;
}
soft_iic_state[iic] &= ~ BIT(6); //resume ok
if (soft_iic_cfg_cache[iic].io_mode) {//pull up
gpio_set_pull_up(soft_iic_cfg_cache[iic].scl_io, 1);
gpio_set_pull_up(soft_iic_cfg_cache[iic].sda_io, 1);
} else {
gpio_set_pull_up(soft_iic_cfg_cache[iic].scl_io, 0);
gpio_set_pull_up(soft_iic_cfg_cache[iic].sda_io, 0);
}
gpio_set_hd(soft_iic_cfg_cache[iic].scl_io, soft_iic_cfg_cache[iic].hdrive);
gpio_set_hd(soft_iic_cfg_cache[iic].sda_io, soft_iic_cfg_cache[iic].hdrive);
IIC_SDA_H(soft_iic_cfg_cache[iic].sda_io);
IIC_SCL_H(soft_iic_cfg_cache[iic].scl_io);
gpio_set_pull_down(soft_iic_cfg_cache[iic].scl_io, 0);
gpio_set_pull_down(soft_iic_cfg_cache[iic].sda_io, 0);
gpio_set_die(soft_iic_cfg_cache[iic].scl_io, 1);
gpio_set_die(soft_iic_cfg_cache[iic].sda_io, 1);
/* gpio_set_dieh(i2c_config->scl_io, 1);//en 3.3v */
/* gpio_set_dieh(i2c_config->sda_io, 1);//en 3.3v */
return IIC_OK;//ok
}
//return:0:error, 1:ok
enum iic_state_enum soft_iic_check_busy(soft_iic_dev iic)
{
ASSERT(iic < MAX_SOFT_IIC_NUM, "iic > MAX_SOFT_IIC_NUM");
if (soft_iic_state[iic] & 0x0f) {
return IIC_ERROR_BUSY;//error
}
soft_iic_state[iic]++;//busy
return IIC_OK;//ok
}
void soft_iic_idle(soft_iic_dev iic)
{
soft_iic_state[iic] &= 0xf0;//idle
}
enum iic_state_enum soft_iic_start(soft_iic_dev iic)
{
ASSERT(iic < MAX_SOFT_IIC_NUM, "iic > MAX_SOFT_IIC_NUM");
u32 delay_cnt;
u32 dly_t = iic_get_delay(iic);
/* printf("soft_iic_init hsb clock:%d, delay cnt:%d\n",clk_get("sys"),dly_t); */
IIC_SDA_H(soft_iic_cfg_cache[iic].sda_io);
soft_iic_delay(dly_t);
IIC_SCL_H(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t * 2);
IIC_SDA_L(soft_iic_cfg_cache[iic].sda_io);
soft_iic_delay(dly_t);
IIC_SCL_L(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t);
return IIC_OK;//ok
}
void soft_iic_stop(soft_iic_dev iic)
{
u32 delay_cnt;
u32 dly_t = iic_get_delay(iic);
IIC_SDA_L(soft_iic_cfg_cache[iic].sda_io);
soft_iic_delay(dly_t);
IIC_SCL_H(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t * 2);
IIC_SDA_H(soft_iic_cfg_cache[iic].sda_io);
soft_iic_delay(dly_t);
soft_iic_idle(iic);
}
void soft_iic_reset(soft_iic_dev iic)//同iic_v2
{
ASSERT(iic < MAX_SOFT_IIC_NUM, "iic > MAX_SOFT_IIC_NUM");
soft_iic_start(iic);
soft_iic_stop(iic);
soft_iic_idle(iic);
}
static u8 soft_iic_check_ack(soft_iic_dev iic)
{
u8 ack;
u32 delay_cnt;
u32 dly_t = iic_get_delay(iic);
IIC_SDA_DIR(soft_iic_cfg_cache[iic].sda_io, 1);
IIC_SCL_L(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t);
IIC_SCL_H(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t);
if (IIC_SDA_READ(soft_iic_cfg_cache[iic].sda_io) == 0) {
ack = 1;
} else {
ack = 0;
}
soft_iic_delay(dly_t);
IIC_SCL_L(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t);
IIC_SDA_DIR(soft_iic_cfg_cache[iic].sda_io, 0);
IIC_SDA_L(soft_iic_cfg_cache[iic].sda_io);
return ack;//1:有应答, 0:无
}
static void soft_iic_rx_ack(soft_iic_dev iic)
{
u32 delay_cnt;
u32 dly_t = iic_get_delay(iic);
IIC_SDA_L(soft_iic_cfg_cache[iic].sda_io);
soft_iic_delay(dly_t);
IIC_SCL_H(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t * 2);
IIC_SCL_L(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t);
}
static void soft_iic_rx_nack(soft_iic_dev iic)
{
u32 delay_cnt;
u32 dly_t = iic_get_delay(iic);
IIC_SDA_H(soft_iic_cfg_cache[iic].sda_io);
soft_iic_delay(dly_t);
IIC_SCL_H(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t * 2);
IIC_SCL_L(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t);
}
u8 soft_iic_tx_byte(soft_iic_dev iic, u8 byte)
{
u32 delay_cnt;
u32 dly_t = iic_get_delay(iic);
local_irq_disable();
IIC_SCL_L(soft_iic_cfg_cache[iic].scl_io);
for (u32 i = 0; i < 8; i++) { //MSB FIRST
if ((byte << i) & 0x80) {
IIC_SDA_H(soft_iic_cfg_cache[iic].sda_io);
} else {
IIC_SDA_L(soft_iic_cfg_cache[iic].sda_io);
}
soft_iic_delay(dly_t);
IIC_SCL_H(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t * 2);
IIC_SCL_L(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t);
}
u8 ack = soft_iic_check_ack(iic);//1:有应答, 0:无
local_irq_enable();
return ack;
}
u8 soft_iic_rx_byte(soft_iic_dev iic, u8 ack, s8 *err)
{
u32 delay_cnt;
u32 dly_t = iic_get_delay(iic);
u8 byte = 0;
local_irq_disable();
IIC_SDA_DIR(soft_iic_cfg_cache[iic].sda_io, 1);
for (u32 i = 0; i < 8; i++) {
soft_iic_delay(dly_t);
IIC_SCL_H(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t);
byte = byte << 1;
if (IIC_SDA_READ(soft_iic_cfg_cache[iic].sda_io)) {
byte |= 1;
}
soft_iic_delay(dly_t);
IIC_SCL_L(soft_iic_cfg_cache[iic].scl_io);
soft_iic_delay(dly_t);
}
IIC_SDA_DIR(soft_iic_cfg_cache[iic].sda_io, 0);
if (ack) {
soft_iic_rx_ack(iic);
} else {
soft_iic_rx_nack(iic);
}
local_irq_enable();
if (err) {
*err = IIC_OK;
}
return byte;
}
//return: =len:ok
int soft_iic_read_buf(soft_iic_dev iic, void *buf, int len)
{
int i = 0;
if (!buf || !len) {
return IIC_ERROR_PARAM_ERROR;
}
for (i = 0; i < len - 1; i++) {
((u8 *)buf)[i] = soft_iic_rx_byte(iic, 1, NULL);
}
((u8 *)buf)[len - 1] = soft_iic_rx_byte(iic, 0, NULL);
return len;
}
//return: =len:ok
int soft_iic_write_buf(soft_iic_dev iic, const void *buf, int len)
{
int i;
u8 ack;
if (!buf || !len) {
return IIC_ERROR_PARAM_ERROR;
}
for (i = 0; i < len; i++) {
ack = soft_iic_tx_byte(iic, ((u8 *)buf)[i]);
if (ack == 0) {
break;
}
}
return i;
}