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