354 lines
9.8 KiB
C
354 lines
9.8 KiB
C
#ifdef SUPPORT_MS_EXTENSIONS
|
|
#pragma bss_seg(".iic_soft.data.bss")
|
|
#pragma data_seg(".iic_soft.data")
|
|
#pragma const_seg(".iic_soft.text.const")
|
|
#pragma code_seg(".iic_soft.text")
|
|
#endif
|
|
#include "iic_soft.h"
|
|
#include "clock.h"
|
|
|
|
#define LOG_TAG_CONST IIC
|
|
#define LOG_TAG "[iic_soft]"
|
|
#define LOG_ERROR_ENABLE
|
|
#define LOG_DEBUG_ENABLE
|
|
#define LOG_INFO_ENABLE
|
|
#define LOG_CLI_ENABLE
|
|
#include "debug.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_mode(scl/16, BIT(scl%16), soft_iic_cfg_cache[iic].io_mode)
|
|
|
|
#define IIC_SCL_L(scl) \
|
|
gpio_set_mode(scl/16, BIT(scl%16), PORT_OUTPUT_LOW)
|
|
|
|
|
|
#define IIC_SDA_DIR(sda, val) \
|
|
if (val){ \
|
|
gpio_set_mode(IO_PORT_SPILT(sda), soft_iic_cfg_cache[iic].io_mode); \
|
|
} else { \
|
|
gpio_set_mode(IO_PORT_SPILT(sda), PORT_OUTPUT_LOW); \
|
|
}
|
|
|
|
//input pull up
|
|
#define IIC_SDA_H(sda) \
|
|
gpio_set_mode(sda/16, BIT(sda%16), soft_iic_cfg_cache[iic].io_mode)
|
|
|
|
#define IIC_SDA_L(sda) \
|
|
gpio_set_mode(sda/16, BIT(sda%16), PORT_OUTPUT_LOW)
|
|
|
|
#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 = 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) {
|
|
log_error("soft iic%d param error!\n", iic);
|
|
return IIC_ERROR_PARAM_ERROR;
|
|
}
|
|
if (iic >= MAX_SOFT_IIC_NUM) {
|
|
log_error("soft iic index:%d error!\n", iic);
|
|
return IIC_ERROR_INDEX_ERROR;
|
|
}
|
|
if ((soft_iic_state[iic]&BIT(7)) != 0) {
|
|
log_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:
|
|
|
|
gpio_set_mode(IO_PORT_SPILT(i2c_config->scl_io), i2c_config->io_mode); //默认10k
|
|
gpio_set_mode(IO_PORT_SPILT(i2c_config->sda_io), i2c_config->io_mode); //默认10k
|
|
gpio_set_drive_strength(IO_PORT_SPILT(i2c_config->scl_io), i2c_config->hdrive);
|
|
gpio_set_drive_strength(IO_PORT_SPILT(i2c_config->sda_io), i2c_config->hdrive);
|
|
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) {
|
|
log_error("soft iic%d has been no init!\n", iic);
|
|
return IIC_ERROR_NO_INIT;
|
|
}
|
|
soft_iic_state[iic] = 0;//no init
|
|
|
|
gpio_set_mode(IO_PORT_SPILT(soft_iic_cfg_cache[iic].scl_io), PORT_HIGHZ);
|
|
gpio_set_mode(IO_PORT_SPILT(soft_iic_cfg_cache[iic].sda_io), PORT_HIGHZ);
|
|
gpio_set_drive_strength(IO_PORT_SPILT(soft_iic_cfg_cache[iic].scl_io), PORT_DRIVE_STRENGT_2p4mA);
|
|
gpio_set_drive_strength(IO_PORT_SPILT(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) {
|
|
log_error("soft iic%d is no init or suspend!\n", iic);
|
|
return IIC_ERROR_SUSPEND_FAIL;
|
|
}
|
|
if ((soft_iic_state[iic] & 0x3f) != 0) {
|
|
log_error("soft iic%d is busy!\n", iic);
|
|
return IIC_ERROR_BUSY;
|
|
}
|
|
soft_iic_state[iic] |= BIT(6);//suspend ok
|
|
gpio_set_mode(IO_PORT_SPILT(soft_iic_cfg_cache[iic].scl_io), PORT_HIGHZ);
|
|
gpio_set_mode(IO_PORT_SPILT(soft_iic_cfg_cache[iic].sda_io), PORT_HIGHZ);
|
|
gpio_set_drive_strength(IO_PORT_SPILT(soft_iic_cfg_cache[iic].scl_io), PORT_DRIVE_STRENGT_2p4mA);
|
|
gpio_set_drive_strength(IO_PORT_SPILT(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) {
|
|
log_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
|
|
|
|
gpio_set_mode(IO_PORT_SPILT(soft_iic_cfg_cache[iic].scl_io), soft_iic_cfg_cache[iic].io_mode); //默认10k
|
|
gpio_set_mode(IO_PORT_SPILT(soft_iic_cfg_cache[iic].sda_io), soft_iic_cfg_cache[iic].io_mode); //默认10k
|
|
gpio_set_drive_strength(IO_PORT_SPILT(soft_iic_cfg_cache[iic].scl_io), soft_iic_cfg_cache[iic].hdrive);
|
|
gpio_set_drive_strength(IO_PORT_SPILT(soft_iic_cfg_cache[iic].sda_io), soft_iic_cfg_cache[iic].hdrive);
|
|
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;
|
|
}
|
|
|
|
|