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  * 2022-03-04     stevetong459      first version
9  */
10 
11 #include "board.h"
12 #include <sys/time.h>
13 
14 #ifdef RT_USING_I2C
15 
16 #define DBG_TAG              "drv.i2c"
17 #define DBG_LVL               DBG_INFO
18 #include <rtdbg.h>
19 
20 #if !defined(BSP_USING_I2C1) && !defined(BSP_USING_I2C2) && !defined(BSP_USING_I2C3) && !defined(BSP_USING_I2C4)
21     #error "Please define at least one BSP_USING_I2Cx"
22 #endif
23 
24 /* apm32 i2c config class */
25 struct apm32_soft_i2c_config
26 {
27     rt_uint8_t scl_pin;
28     rt_uint8_t sda_pin;
29     const char *bus_name;
30 };
31 
32 /* apm32 i2c dirver class */
33 struct apm32_soft_i2c
34 {
35     struct rt_i2c_bit_ops ops;
36     struct rt_i2c_bus_device i2c_bus;
37 };
38 
39 static const struct apm32_soft_i2c_config soft_i2c_config[] =
40 {
41 #ifdef BSP_USING_I2C1
42     {
43         BSP_I2C1_SCL_PIN,
44         BSP_I2C1_SDA_PIN,
45         "i2c1"
46     },
47 #endif
48 #ifdef BSP_USING_I2C2
49     {
50         BSP_I2C2_SCL_PIN,
51         BSP_I2C2_SDA_PIN,
52         "i2c2"
53     },
54 #endif
55 #ifdef BSP_USING_I2C3
56     {
57         BSP_I2C3_SCL_PIN,
58         BSP_I2C3_SDA_PIN,
59         "i2c3"
60     },
61 #endif
62 #ifdef BSP_USING_I2C4
63     {
64         BSP_I2C4_SCL_PIN,
65         BSP_I2C4_SDA_PIN,
66         "i2c4"
67     },
68 #endif
69 };
70 
71 static struct apm32_soft_i2c i2c_obj[sizeof(soft_i2c_config) / sizeof(soft_i2c_config[0])];
72 
73 /**
74  * @brief    This function will config gpio of soft i2c.
75  *
76  * @param    i2c is a pointer to the object of soft i2c.
77  */
apm32_soft_i2c_gpio_init(struct apm32_soft_i2c * i2c)78 static void apm32_soft_i2c_gpio_init(struct apm32_soft_i2c *i2c)
79 {
80     struct apm32_soft_i2c_config *cfg = (struct apm32_soft_i2c_config *)i2c->ops.data;
81 
82     rt_pin_mode(cfg->scl_pin, PIN_MODE_OUTPUT_OD);
83     rt_pin_mode(cfg->sda_pin, PIN_MODE_OUTPUT_OD);
84 
85     rt_pin_write(cfg->scl_pin, PIN_HIGH);
86     rt_pin_write(cfg->sda_pin, PIN_HIGH);
87 }
88 
apm32_i2c_pin_init(void)89 static void apm32_i2c_pin_init(void)
90 {
91     rt_size_t obj_num = sizeof(i2c_obj) / sizeof(struct apm32_soft_i2c);
92 
93     for(rt_size_t i = 0; i < obj_num; i++)
94     {
95         apm32_soft_i2c_gpio_init(&i2c_obj[i]);
96     }
97 }
98 
99 /**
100  * @brief    This function sets the sda pin.
101  *
102  * @param    data is a pointer to the i2c config class.
103  *
104  * @param    state is the level of sda pin.
105  */
apm32_soft_i2c_set_sda(void * data,rt_int32_t state)106 static void apm32_soft_i2c_set_sda(void *data, rt_int32_t state)
107 {
108     struct apm32_soft_i2c_config *cfg = (struct apm32_soft_i2c_config *)data;
109 
110     rt_pin_write(cfg->sda_pin, state ? PIN_HIGH : PIN_LOW);
111 }
112 
113 /**
114  * @brief    This function sets the scl pin.
115  *
116  * @param    data is a pointer to the i2c config class.
117  *
118  * @param    state is the level of scl pin.
119  */
apm32_soft_i2c_set_scl(void * data,rt_int32_t state)120 static void apm32_soft_i2c_set_scl(void *data, rt_int32_t state)
121 {
122     struct apm32_soft_i2c_config *cfg = (struct apm32_soft_i2c_config *)data;
123 
124     rt_pin_write(cfg->scl_pin, state ? PIN_HIGH : PIN_LOW);
125 }
126 
127 /**
128  * @brief    This function gets the sda pin state.
129  *
130  * @param    data is a pointer to the i2c config class.
131  */
apm32_soft_i2c_get_sda(void * data)132 static rt_int32_t apm32_soft_i2c_get_sda(void *data)
133 {
134     struct apm32_soft_i2c_config *cfg = (struct apm32_soft_i2c_config *)data;
135     return rt_pin_read(cfg->sda_pin);
136 }
137 
138 /**
139  * @brief    This function gets the scl pin state.
140  *
141  * @param    data is a pointer to the i2c config class.
142  */
apm32_soft_i2c_get_scl(void * data)143 static rt_int32_t apm32_soft_i2c_get_scl(void *data)
144 {
145     struct apm32_soft_i2c_config *cfg = (struct apm32_soft_i2c_config *)data;
146     return rt_pin_read(cfg->scl_pin);
147 }
148 
149 /**
150  * @brief    The time delay function in microseconds.
151  *
152  * @param    us is the microseconds to delay.
153  */
apm32_soft_i2c_udelay(rt_uint32_t us)154 static void apm32_soft_i2c_udelay(rt_uint32_t us)
155 {
156     rt_uint32_t count_old = SysTick->VAL;
157     rt_uint32_t count_now;
158     rt_uint32_t count = 0;
159     rt_uint32_t reload = SysTick->LOAD;
160     rt_uint32_t count_pre_us = (reload * RT_TICK_PER_SECOND) / 1000000;
161 
162     while (count_pre_us * us > count)
163     {
164         count_now = SysTick->VAL;
165         if (count_now != count_old)
166         {
167             if (count_now < count_old)
168             {
169                 count += count_old - count_now;
170             }
171             else
172             {
173                 count += reload - count_now + count_old;
174             }
175             count_old = count_now;
176         }
177     }
178 }
179 
180 /**
181  * @brief    This function will unlock i2c, if it is locked.
182  *
183  * @param    cfg is a pointer to i2c config class.
184  *
185  * @return   RT_EOK indicates successful unlock, other value indicates failed.
186  */
apm32_i2c_bus_unlock(const struct apm32_soft_i2c_config * cfg)187 static rt_err_t apm32_i2c_bus_unlock(const struct apm32_soft_i2c_config *cfg)
188 {
189     rt_int32_t i = 0;
190 
191     if (PIN_LOW == rt_pin_read(cfg->sda_pin))
192     {
193         while (i++ < 9)
194         {
195             rt_pin_write(cfg->scl_pin, PIN_HIGH);
196             apm32_soft_i2c_udelay(100);
197             rt_pin_write(cfg->scl_pin, PIN_LOW);
198             apm32_soft_i2c_udelay(100);
199         }
200     }
201     if (PIN_LOW == rt_pin_read(cfg->sda_pin))
202     {
203         return -RT_ERROR;
204     }
205 
206     return RT_EOK;
207 }
208 
209 static const struct rt_i2c_bit_ops apm32_bit_ops_default =
210 {
211     .data     = RT_NULL,
212     .pin_init = apm32_i2c_pin_init,
213     .set_sda  = apm32_soft_i2c_set_sda,
214     .set_scl  = apm32_soft_i2c_set_scl,
215     .get_sda  = apm32_soft_i2c_get_sda,
216     .get_scl  = apm32_soft_i2c_get_scl,
217     .udelay   = apm32_soft_i2c_udelay,
218     .delay_us = 1,
219     .timeout  = 100,
220     .i2c_pin_init_flag = RT_FALSE
221 };
222 
223 /**
224  * @brief    I2C initialization function.
225  *
226  * @return   RT_EOK indicates successful initialization, other value indicates failed;
227  */
rt_hw_i2c_init(void)228 int rt_hw_i2c_init(void)
229 {
230     rt_size_t obj_num = sizeof(i2c_obj) / sizeof(struct apm32_soft_i2c);
231     rt_err_t result;
232 
233     for (rt_size_t i = 0; i < obj_num; i++)
234     {
235         i2c_obj[i].ops = apm32_bit_ops_default;
236         i2c_obj[i].ops.data = (void *)&soft_i2c_config[i];
237         i2c_obj[i].i2c_bus.priv = &i2c_obj[i].ops;
238 
239         result = rt_i2c_bit_add_bus(&i2c_obj[i].i2c_bus, soft_i2c_config[i].bus_name);
240 
241         RT_ASSERT(result == RT_EOK);
242 
243         apm32_i2c_bus_unlock(&soft_i2c_config[i]);
244 
245         LOG_D("software simulation %s init done, pin scl: %d, pin sda: %d",
246               soft_i2c_config[i].bus_name,
247               soft_i2c_config[i].scl_pin,
248               soft_i2c_config[i].sda_pin);
249     }
250 
251     return RT_EOK;
252 }
253 INIT_BOARD_EXPORT(rt_hw_i2c_init);
254 
255 #endif /* RT_USING_I2C */
256