265 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			265 lines
		
	
	
		
			7.3 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /*
 | ||
|  |  * Copyright (c) 2006-2023, RT-Thread Development Team | ||
|  |  * | ||
|  |  * SPDX-License-Identifier: Apache-2.0 | ||
|  |  * | ||
|  |  * Change Logs: | ||
|  |  * Date           Author        Notes | ||
|  |  * 2023-07-30     sp-cai        first version | ||
|  |  */ | ||
|  | 
 | ||
|  | #include <rtdevice.h>
 | ||
|  | 
 | ||
|  | #ifdef RT_USING_SOFT_I2C
 | ||
|  | #if !defined(RT_USING_SOFT_I2C1) && !defined(RT_USING_SOFT_I2C2) &&\
 | ||
|  |     !defined(RT_USING_SOFT_I2C3) && !defined(RT_USING_SOFT_I2C4) &&\ | ||
|  |     !defined(RT_USING_SOFT_I2C5) && !defined(RT_USING_SOFT_I2C6) &&\ | ||
|  |     !defined(RT_USING_SOFT_I2C7) && !defined(RT_USING_SOFT_I2C8) | ||
|  |     #error "Please define at least one RT_USING_SOFT_I2Cx"
 | ||
|  |     /*
 | ||
|  |     This driver can be disabled at: | ||
|  |     menuconfig -> RT-Thread Components -> Device Drivers -> Using I2C device drivers | ||
|  |     */ | ||
|  | #endif
 | ||
|  | 
 | ||
|  | #define DBG_ENABLE
 | ||
|  | #define DBG_TAG                         "I2C_S"
 | ||
|  | #ifdef RT_I2C_BITOPS_DEBUG
 | ||
|  |     #define DBG_LEVEL                   DBG_LOG
 | ||
|  | #endif
 | ||
|  | #include <rtdbg.h>
 | ||
|  | 
 | ||
|  | /* i2c config class */ | ||
|  | struct soft_i2c_config | ||
|  | { | ||
|  |     rt_base_t       scl_pin; | ||
|  |     rt_base_t       sda_pin; | ||
|  |     const char      *bus_name; | ||
|  |     rt_uint16_t     timing_delay;   /* scl and sda line delay */ | ||
|  |     rt_uint16_t     timing_timeout; /* in tick */ | ||
|  | }; | ||
|  | 
 | ||
|  | /* i2c dirver class */ | ||
|  | struct rt_soft_i2c | ||
|  | { | ||
|  |     struct rt_i2c_bus_device    i2c_bus; | ||
|  |     struct rt_i2c_bit_ops       ops; | ||
|  | }; | ||
|  | 
 | ||
|  | struct soft_i2c_config i2c_cfg[] = | ||
|  | { | ||
|  |     #ifdef RT_USING_SOFT_I2C1
 | ||
|  |     { | ||
|  |         .scl_pin        = RT_SOFT_I2C1_SCL_PIN, | ||
|  |         .sda_pin        = RT_SOFT_I2C1_SDA_PIN, | ||
|  |         .bus_name       = RT_SOFT_I2C1_BUS_NAME, | ||
|  |         .timing_delay   = RT_SOFT_I2C1_TIMING_DELAY, | ||
|  |         .timing_timeout = RT_SOFT_I2C1_TIMING_TIMEOUT, | ||
|  |     }, | ||
|  |     #endif  //RT_USING_SOFT_I2C1
 | ||
|  |     #ifdef RT_USING_SOFT_I2C2
 | ||
|  |     { | ||
|  |         .scl_pin        = RT_SOFT_I2C2_SCL_PIN, | ||
|  |         .sda_pin        = RT_SOFT_I2C2_SDA_PIN, | ||
|  |         .bus_name       = RT_SOFT_I2C2_BUS_NAME, | ||
|  |         .timing_delay   = RT_SOFT_I2C2_TIMING_DELAY, | ||
|  |         .timing_timeout = RT_SOFT_I2C2_TIMING_TIMEOUT, | ||
|  |     }, | ||
|  |     #endif  //RT_USING_SOFT_I2C2
 | ||
|  |     #ifdef RT_USING_SOFT_I2C3
 | ||
|  |     { | ||
|  |         .scl_pin        = RT_SOFT_I2C3_SCL_PIN, | ||
|  |         .sda_pin        = RT_SOFT_I2C3_SDA_PIN, | ||
|  |         .bus_name       = RT_SOFT_I2C3_BUS_NAME, | ||
|  |         .timing_delay   = RT_SOFT_I2C3_TIMING_DELAY, | ||
|  |         .timing_timeout = RT_SOFT_I2C3_TIMING_TIMEOUT, | ||
|  |     }, | ||
|  |     #endif  //RT_USING_SOFT_I2C3
 | ||
|  |     #ifdef RT_USING_SOFT_I2C4
 | ||
|  |     { | ||
|  |         .scl_pin        = RT_SOFT_I2C4_SCL_PIN, | ||
|  |         .sda_pin        = RT_SOFT_I2C4_SDA_PIN, | ||
|  |         .bus_name       = RT_SOFT_I2C4_BUS_NAME, | ||
|  |         .timing_delay   = RT_SOFT_I2C4_TIMING_DELAY, | ||
|  |         .timing_timeout = RT_SOFT_I2C4_TIMING_TIMEOUT, | ||
|  |     }, | ||
|  |     #endif  //RT_USING_SOFT_I2C4
 | ||
|  |     #ifdef RT_USING_SOFT_I2C5
 | ||
|  |     { | ||
|  |         .scl_pin        = RT_SOFT_I2C5_SCL_PIN, | ||
|  |         .sda_pin        = RT_SOFT_I2C5_SDA_PIN, | ||
|  |         .bus_name       = RT_SOFT_I2C5_BUS_NAME, | ||
|  |         .timing_delay   = RT_SOFT_I2C5_TIMING_DELAY, | ||
|  |         .timing_timeout = RT_SOFT_I2C5_TIMING_TIMEOUT, | ||
|  |     }, | ||
|  |     #endif  //RT_USING_SOFT_I2C5
 | ||
|  |     #ifdef RT_USING_SOFT_I2C6
 | ||
|  |     { | ||
|  |         .scl_pin        = RT_SOFT_I2C6_SCL_PIN, | ||
|  |         .sda_pin        = RT_SOFT_I2C6_SDA_PIN, | ||
|  |         .bus_name       = RT_SOFT_I2C6_BUS_NAME, | ||
|  |         .timing_delay   = RT_SOFT_I2C6_TIMING_DELAY, | ||
|  |         .timing_timeout = RT_SOFT_I2C6_TIMING_TIMEOUT, | ||
|  |     }, | ||
|  |     #endif  //RT_USING_SOFT_I2C6
 | ||
|  |     #ifdef RT_USING_SOFT_I2C7
 | ||
|  |     { | ||
|  |         .scl_pin        = RT_SOFT_I2C7_SCL_PIN, | ||
|  |         .sda_pin        = RT_SOFT_I2C7_SDA_PIN, | ||
|  |         .bus_name       = RT_SOFT_I2C7_BUS_NAME, | ||
|  |         .timing_delay   = RT_SOFT_I2C7_TIMING_DELAY, | ||
|  |         .timing_timeout = RT_SOFT_I2C7_TIMING_TIMEOUT, | ||
|  |     }, | ||
|  |     #endif  //RT_USING_SOFT_I2C7
 | ||
|  |     #ifdef RT_USING_SOFT_I2C8
 | ||
|  |     { | ||
|  |         .scl_pin        = RT_SOFT_I2C8_SCL_PIN, | ||
|  |         .sda_pin        = RT_SOFT_I2C8_SDA_PIN, | ||
|  |         .bus_name       = RT_SOFT_I2C8_BUS_NAME, | ||
|  |         .timing_delay   = RT_SOFT_I2C8_TIMING_DELAY, | ||
|  |         .timing_timeout = RT_SOFT_I2C8_TIMING_TIMEOUT, | ||
|  |     }, | ||
|  |     #endif  //RT_USING_SOFT_I2C8
 | ||
|  | }; | ||
|  | 
 | ||
|  | 
 | ||
|  | static struct rt_soft_i2c i2c_bus_obj[sizeof(i2c_cfg) / sizeof(i2c_cfg[0])] = | ||
|  | { 0 }; | ||
|  | 
 | ||
|  | /**
 | ||
|  | * This function initializes the i2c pin. | ||
|  | * @param i2c config class. | ||
|  | */ | ||
|  | static void pin_init(const struct soft_i2c_config *cfg) | ||
|  | { | ||
|  |     rt_pin_mode(cfg->scl_pin, PIN_MODE_OUTPUT_OD); | ||
|  |     rt_pin_mode(cfg->sda_pin, PIN_MODE_OUTPUT_OD); | ||
|  |     rt_pin_write(cfg->scl_pin, PIN_HIGH); | ||
|  |     rt_pin_write(cfg->sda_pin, PIN_HIGH); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | /**
 | ||
|  | * This function sets the sda pin. | ||
|  | * @param i2c config class. | ||
|  | * @param The sda pin state. | ||
|  | */ | ||
|  | static void set_sda(void *cfg, rt_int32_t value) | ||
|  | { | ||
|  |     rt_pin_write(((const struct soft_i2c_config*)cfg)->sda_pin, value); | ||
|  | } | ||
|  | 
 | ||
|  | /**
 | ||
|  | * This function sets the scl pin. | ||
|  | * @param i2c config class. | ||
|  | * @param The sda pin state. | ||
|  | */ | ||
|  | static void set_scl(void *cfg, rt_int32_t value) | ||
|  | { | ||
|  |     rt_pin_write(((const struct soft_i2c_config*)cfg)->scl_pin, value); | ||
|  | } | ||
|  | 
 | ||
|  | /**
 | ||
|  | * This function gets the sda pin state. | ||
|  | * @param i2c config class. | ||
|  | */ | ||
|  | static rt_int32_t get_sda(void *cfg) | ||
|  | { | ||
|  |     return rt_pin_read(((const struct soft_i2c_config*)cfg)->sda_pin); | ||
|  | } | ||
|  | 
 | ||
|  | /**
 | ||
|  | * This function gets the scl pin state. | ||
|  | * @param i2c config class. | ||
|  | */ | ||
|  | static rt_int32_t get_scl(void *cfg) | ||
|  | { | ||
|  |     return rt_pin_read(((const struct soft_i2c_config*)cfg)->scl_pin); | ||
|  | } | ||
|  | 
 | ||
|  | 
 | ||
|  | static const struct rt_i2c_bit_ops soft_i2c_ops = | ||
|  | { | ||
|  |     .set_sda = set_sda, | ||
|  |     .set_scl = set_scl, | ||
|  |     .get_sda = get_sda, | ||
|  |     .get_scl = get_scl, | ||
|  |     .udelay = rt_hw_us_delay, | ||
|  | }; | ||
|  | 
 | ||
|  | /**
 | ||
|  | * if i2c is locked, this function will unlock it | ||
|  | * | ||
|  | * @param i2c config class. | ||
|  | * | ||
|  | * @return RT_EOK indicates successful unlock. | ||
|  | */ | ||
|  | static rt_err_t i2c_bus_unlock(const struct soft_i2c_config *cfg) | ||
|  | { | ||
|  |     rt_ubase_t i = 0; | ||
|  | 
 | ||
|  |     if(PIN_LOW == rt_pin_read(cfg->sda_pin)) | ||
|  |     { | ||
|  |         while(i++ < 9) | ||
|  |         { | ||
|  |             rt_pin_write(cfg->scl_pin, PIN_HIGH); | ||
|  |             rt_hw_us_delay(cfg->timing_delay); | ||
|  |             rt_pin_write(cfg->scl_pin, PIN_LOW); | ||
|  |             rt_hw_us_delay(cfg->timing_delay); | ||
|  |         } | ||
|  |     } | ||
|  |     if(PIN_LOW == rt_pin_read(cfg->sda_pin)) | ||
|  |     { | ||
|  |         return -RT_ERROR; | ||
|  |     } | ||
|  | 
 | ||
|  |     return RT_EOK; | ||
|  | } | ||
|  | 
 | ||
|  | /* I2C initialization function */ | ||
|  | int rt_soft_i2c_init(void) | ||
|  | { | ||
|  |     int err = RT_EOK; | ||
|  |     struct rt_soft_i2c *obj; | ||
|  |     int i; | ||
|  | 
 | ||
|  |     for(i = 0; i < sizeof(i2c_bus_obj) / sizeof(i2c_bus_obj[0]); i++) | ||
|  |     { | ||
|  |         struct soft_i2c_config *cfg = &i2c_cfg[i]; | ||
|  | 
 | ||
|  |         pin_init(cfg); | ||
|  | 
 | ||
|  |         obj = &i2c_bus_obj[i]; | ||
|  |         obj->ops = soft_i2c_ops; | ||
|  |         obj->ops.data = cfg; | ||
|  |         obj->i2c_bus.priv = &obj->ops; | ||
|  |         obj->ops.delay_us = cfg->timing_delay; | ||
|  |         obj->ops.timeout = cfg->timing_timeout; | ||
|  |         if(rt_i2c_bit_add_bus(&obj->i2c_bus, cfg->bus_name) == RT_EOK) | ||
|  |         { | ||
|  |             i2c_bus_unlock(cfg); | ||
|  |             LOG_D("Software simulation %s init done" | ||
|  |                   ", SCL pin: 0x%02X, SDA pin: 0x%02X" | ||
|  |                   , cfg->bus_name | ||
|  |                   , cfg->scl_pin | ||
|  |                   , cfg->sda_pin | ||
|  |                  ); | ||
|  |         } | ||
|  |         else | ||
|  |         { | ||
|  |             err++; | ||
|  |             LOG_E("Software simulation %s init fail" | ||
|  |                   ", SCL pin: 0x%02X, SDA pin: 0x%02X" | ||
|  |                   , cfg->bus_name | ||
|  |                   , cfg->scl_pin | ||
|  |                   , cfg->sda_pin | ||
|  |                  ); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     return err; | ||
|  | } | ||
|  | INIT_PREV_EXPORT(rt_soft_i2c_init); | ||
|  | 
 | ||
|  | #endif // RT_USING_SOFT_I2C
 |