1 /*
2  * Copyright (c) 2006-2023, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author        Notes
8  * 2023-07-30     sp-cai        first version
9  */
10 
11 #include <rtdevice.h>
12 
13 #ifdef RT_USING_SOFT_I2C
14 #if !defined(RT_USING_SOFT_I2C0) &&\
15     !defined(RT_USING_SOFT_I2C1) && !defined(RT_USING_SOFT_I2C2) &&\
16     !defined(RT_USING_SOFT_I2C3) && !defined(RT_USING_SOFT_I2C4) &&\
17     !defined(RT_USING_SOFT_I2C5) && !defined(RT_USING_SOFT_I2C6) &&\
18     !defined(RT_USING_SOFT_I2C7) && !defined(RT_USING_SOFT_I2C8)
19     #error "Please define at least one RT_USING_SOFT_I2Cx"
20     /*
21     This driver can be disabled at:
22     menuconfig -> RT-Thread Components -> Device Drivers -> Using I2C device drivers
23     */
24 #endif
25 
26 #define DBG_ENABLE
27 #define DBG_TAG                         "I2C_S"
28 #ifdef RT_I2C_BITOPS_DEBUG
29     #define DBG_LEVEL                   DBG_LOG
30 #endif
31 #include <rtdbg.h>
32 
33 /* i2c config class */
34 struct soft_i2c_config
35 {
36     rt_base_t       scl_pin;
37     rt_base_t       sda_pin;
38     const char      *bus_name;
39     rt_uint16_t     timing_delay;   /* scl and sda line delay */
40     rt_uint16_t     timing_timeout; /* in tick */
41 };
42 
43 /* i2c dirver class */
44 struct rt_soft_i2c
45 {
46     struct rt_i2c_bus_device    i2c_bus;
47     struct rt_i2c_bit_ops       ops;
48 };
49 
50 struct soft_i2c_config i2c_cfg[] =
51 {
52     #ifdef RT_USING_SOFT_I2C0
53     {
54         .scl_pin        = RT_SOFT_I2C0_SCL_PIN,
55         .sda_pin        = RT_SOFT_I2C0_SDA_PIN,
56         .bus_name       = RT_SOFT_I2C0_BUS_NAME,
57         .timing_delay   = RT_SOFT_I2C0_TIMING_DELAY,
58         .timing_timeout = RT_SOFT_I2C0_TIMING_TIMEOUT,
59     },
60     #endif  //RT_USING_SOFT_I2C0
61     #ifdef RT_USING_SOFT_I2C1
62     {
63         .scl_pin        = RT_SOFT_I2C1_SCL_PIN,
64         .sda_pin        = RT_SOFT_I2C1_SDA_PIN,
65         .bus_name       = RT_SOFT_I2C1_BUS_NAME,
66         .timing_delay   = RT_SOFT_I2C1_TIMING_DELAY,
67         .timing_timeout = RT_SOFT_I2C1_TIMING_TIMEOUT,
68     },
69     #endif  //RT_USING_SOFT_I2C1
70     #ifdef RT_USING_SOFT_I2C2
71     {
72         .scl_pin        = RT_SOFT_I2C2_SCL_PIN,
73         .sda_pin        = RT_SOFT_I2C2_SDA_PIN,
74         .bus_name       = RT_SOFT_I2C2_BUS_NAME,
75         .timing_delay   = RT_SOFT_I2C2_TIMING_DELAY,
76         .timing_timeout = RT_SOFT_I2C2_TIMING_TIMEOUT,
77     },
78     #endif  //RT_USING_SOFT_I2C2
79     #ifdef RT_USING_SOFT_I2C3
80     {
81         .scl_pin        = RT_SOFT_I2C3_SCL_PIN,
82         .sda_pin        = RT_SOFT_I2C3_SDA_PIN,
83         .bus_name       = RT_SOFT_I2C3_BUS_NAME,
84         .timing_delay   = RT_SOFT_I2C3_TIMING_DELAY,
85         .timing_timeout = RT_SOFT_I2C3_TIMING_TIMEOUT,
86     },
87     #endif  //RT_USING_SOFT_I2C3
88     #ifdef RT_USING_SOFT_I2C4
89     {
90         .scl_pin        = RT_SOFT_I2C4_SCL_PIN,
91         .sda_pin        = RT_SOFT_I2C4_SDA_PIN,
92         .bus_name       = RT_SOFT_I2C4_BUS_NAME,
93         .timing_delay   = RT_SOFT_I2C4_TIMING_DELAY,
94         .timing_timeout = RT_SOFT_I2C4_TIMING_TIMEOUT,
95     },
96     #endif  //RT_USING_SOFT_I2C4
97     #ifdef RT_USING_SOFT_I2C5
98     {
99         .scl_pin        = RT_SOFT_I2C5_SCL_PIN,
100         .sda_pin        = RT_SOFT_I2C5_SDA_PIN,
101         .bus_name       = RT_SOFT_I2C5_BUS_NAME,
102         .timing_delay   = RT_SOFT_I2C5_TIMING_DELAY,
103         .timing_timeout = RT_SOFT_I2C5_TIMING_TIMEOUT,
104     },
105     #endif  //RT_USING_SOFT_I2C5
106     #ifdef RT_USING_SOFT_I2C6
107     {
108         .scl_pin        = RT_SOFT_I2C6_SCL_PIN,
109         .sda_pin        = RT_SOFT_I2C6_SDA_PIN,
110         .bus_name       = RT_SOFT_I2C6_BUS_NAME,
111         .timing_delay   = RT_SOFT_I2C6_TIMING_DELAY,
112         .timing_timeout = RT_SOFT_I2C6_TIMING_TIMEOUT,
113     },
114     #endif  //RT_USING_SOFT_I2C6
115     #ifdef RT_USING_SOFT_I2C7
116     {
117         .scl_pin        = RT_SOFT_I2C7_SCL_PIN,
118         .sda_pin        = RT_SOFT_I2C7_SDA_PIN,
119         .bus_name       = RT_SOFT_I2C7_BUS_NAME,
120         .timing_delay   = RT_SOFT_I2C7_TIMING_DELAY,
121         .timing_timeout = RT_SOFT_I2C7_TIMING_TIMEOUT,
122     },
123     #endif  //RT_USING_SOFT_I2C7
124     #ifdef RT_USING_SOFT_I2C8
125     {
126         .scl_pin        = RT_SOFT_I2C8_SCL_PIN,
127         .sda_pin        = RT_SOFT_I2C8_SDA_PIN,
128         .bus_name       = RT_SOFT_I2C8_BUS_NAME,
129         .timing_delay   = RT_SOFT_I2C8_TIMING_DELAY,
130         .timing_timeout = RT_SOFT_I2C8_TIMING_TIMEOUT,
131     },
132     #endif  //RT_USING_SOFT_I2C8
133 };
134 
135 
136 static struct rt_soft_i2c i2c_bus_obj[sizeof(i2c_cfg) / sizeof(i2c_cfg[0])] =
137 { 0 };
138 
139 /**
140 * This function initializes the i2c pin.
141 * @param i2c config class.
142 */
pin_init(const struct soft_i2c_config * cfg)143 static void pin_init(const struct soft_i2c_config *cfg)
144 {
145     rt_pin_mode(cfg->scl_pin, PIN_MODE_OUTPUT_OD);
146     rt_pin_mode(cfg->sda_pin, PIN_MODE_OUTPUT_OD);
147     rt_pin_write(cfg->scl_pin, PIN_HIGH);
148     rt_pin_write(cfg->sda_pin, PIN_HIGH);
149 }
150 
151 
152 /**
153 * This function sets the sda pin.
154 * @param i2c config class.
155 * @param The sda pin state.
156 */
set_sda(void * cfg,rt_int32_t value)157 static void set_sda(void *cfg, rt_int32_t value)
158 {
159     rt_pin_write(((const struct soft_i2c_config*)cfg)->sda_pin, value);
160 }
161 
162 /**
163 * This function sets the scl pin.
164 * @param i2c config class.
165 * @param The sda pin state.
166 */
set_scl(void * cfg,rt_int32_t value)167 static void set_scl(void *cfg, rt_int32_t value)
168 {
169     rt_pin_write(((const struct soft_i2c_config*)cfg)->scl_pin, value);
170 }
171 
172 /**
173 * This function gets the sda pin state.
174 * @param i2c config class.
175 */
get_sda(void * cfg)176 static rt_int32_t get_sda(void *cfg)
177 {
178     return rt_pin_read(((const struct soft_i2c_config*)cfg)->sda_pin);
179 }
180 
181 /**
182 * This function gets the scl pin state.
183 * @param i2c config class.
184 */
get_scl(void * cfg)185 static rt_int32_t get_scl(void *cfg)
186 {
187     return rt_pin_read(((const struct soft_i2c_config*)cfg)->scl_pin);
188 }
189 
190 
191 static const struct rt_i2c_bit_ops soft_i2c_ops =
192 {
193     .set_sda = set_sda,
194     .set_scl = set_scl,
195     .get_sda = get_sda,
196     .get_scl = get_scl,
197     .udelay = rt_hw_us_delay,
198 };
199 
200 /**
201 * if i2c is locked, this function will unlock it
202 *
203 * @param i2c config class.
204 *
205 * @return RT_EOK indicates successful unlock.
206 */
i2c_bus_unlock(const struct soft_i2c_config * cfg)207 static rt_err_t i2c_bus_unlock(const struct soft_i2c_config *cfg)
208 {
209     rt_ubase_t i = 0;
210 
211     if(PIN_LOW == rt_pin_read(cfg->sda_pin))
212     {
213         while(i++ < 9)
214         {
215             rt_pin_write(cfg->scl_pin, PIN_HIGH);
216             rt_hw_us_delay(cfg->timing_delay);
217             rt_pin_write(cfg->scl_pin, PIN_LOW);
218             rt_hw_us_delay(cfg->timing_delay);
219         }
220     }
221     if(PIN_LOW == rt_pin_read(cfg->sda_pin))
222     {
223         return -RT_ERROR;
224     }
225 
226     return RT_EOK;
227 }
228 
229 /* I2C initialization function */
rt_soft_i2c_init(void)230 int rt_soft_i2c_init(void)
231 {
232     int err = RT_EOK;
233     struct rt_soft_i2c *obj;
234     int i;
235 
236     for(i = 0; i < sizeof(i2c_bus_obj) / sizeof(i2c_bus_obj[0]); i++)
237     {
238         struct soft_i2c_config *cfg = &i2c_cfg[i];
239 
240         pin_init(cfg);
241 
242         obj = &i2c_bus_obj[i];
243         obj->ops = soft_i2c_ops;
244         obj->ops.data = cfg;
245         obj->i2c_bus.priv = &obj->ops;
246         obj->ops.delay_us = cfg->timing_delay;
247         obj->ops.timeout = cfg->timing_timeout;
248         if(rt_i2c_bit_add_bus(&obj->i2c_bus, cfg->bus_name) == RT_EOK)
249         {
250             i2c_bus_unlock(cfg);
251             LOG_D("Software simulation %s init done"
252                   ", SCL pin: 0x%02X, SDA pin: 0x%02X"
253                   , cfg->bus_name
254                   , cfg->scl_pin
255                   , cfg->sda_pin
256                  );
257         }
258         else
259         {
260             err++;
261             LOG_E("Software simulation %s init fail"
262                   ", SCL pin: 0x%02X, SDA pin: 0x%02X"
263                   , cfg->bus_name
264                   , cfg->scl_pin
265                   , cfg->sda_pin
266                  );
267         }
268     }
269 
270     return err;
271 }
272 INIT_PREV_EXPORT(rt_soft_i2c_init);
273 
274 #endif // RT_USING_SOFT_I2C
275