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  * 2020-01-09     shelton      first version
9  */
10 
11 #include <board.h>
12 #include "drv_soft_i2c.h"
13 
14 #ifdef RT_USING_I2C
15 
16 #define LOG_TAG              "drv.i2c"
17 #include <drv_log.h>
18 
19 #if !defined(BSP_USING_I2C1) && !defined(BSP_USING_I2C2) && !defined(BSP_USING_I2C3) && !defined(BSP_USING_I2C4)
20     #error "Please define at least one BSP_USING_I2Cx"
21     /* this driver can be disabled at menuconfig -> RT-Thread Components -> Device Drivers */
22 #endif
23 
24 static const struct n32_soft_i2c_config soft_i2c_config[] =
25 {
26 #ifdef BSP_USING_I2C1
27     I2C1_BUS_CONFIG,
28 #endif
29 #ifdef BSP_USING_I2C2
30     I2C2_BUS_CONFIG,
31 #endif
32 #ifdef BSP_USING_I2C3
33     I2C3_BUS_CONFIG,
34 #endif
35 #ifdef BSP_USING_I2C4
36     I2C4_BUS_CONFIG,
37 #endif
38 };
39 
40 static struct n32_i2c i2c_obj[sizeof(soft_i2c_config) / sizeof(soft_i2c_config[0])];
41 
42 /**
43  * This function initializes the i2c pin.
44  *
45  * @param n32 i2c dirver class.
46  */
n32_i2c_gpio_init(struct n32_i2c * i2c)47 static void n32_i2c_gpio_init(struct n32_i2c *i2c)
48 {
49     struct n32_soft_i2c_config *cfg = (struct n32_soft_i2c_config *)i2c->ops.data;
50 
51     rt_pin_mode(cfg->scl, PIN_MODE_OUTPUT_OD);
52     rt_pin_mode(cfg->sda, PIN_MODE_OUTPUT_OD);
53 
54     rt_pin_write(cfg->scl, PIN_HIGH);
55     rt_pin_write(cfg->sda, PIN_HIGH);
56 }
57 
n32_i2c_pin_init(void)58 static void n32_i2c_pin_init(void)
59 {
60     rt_size_t obj_num = sizeof(i2c_obj) / sizeof(struct n32_i2c);
61 
62     for(rt_size_t i = 0; i < obj_num; i++)
63     {
64         n32_i2c_gpio_init(&i2c_obj[i]);
65     }
66 }
67 
68 /**
69  * This function sets the sda pin.
70  *
71  * @param n32 config class.
72  * @param The sda pin state.
73  */
n32_set_sda(void * data,rt_int32_t state)74 static void n32_set_sda(void *data, rt_int32_t state)
75 {
76     struct n32_soft_i2c_config *cfg = (struct n32_soft_i2c_config *)data;
77     if (state)
78     {
79         rt_pin_write(cfg->sda, PIN_HIGH);
80     }
81     else
82     {
83         rt_pin_write(cfg->sda, PIN_LOW);
84     }
85 }
86 
87 /**
88  * This function sets the scl pin.
89  *
90  * @param n32 config class.
91  * @param The scl pin state.
92  */
n32_set_scl(void * data,rt_int32_t state)93 static void n32_set_scl(void *data, rt_int32_t state)
94 {
95     struct n32_soft_i2c_config *cfg = (struct n32_soft_i2c_config *)data;
96     if (state)
97     {
98         rt_pin_write(cfg->scl, PIN_HIGH);
99     }
100     else
101     {
102         rt_pin_write(cfg->scl, PIN_LOW);
103     }
104 }
105 
106 /**
107  * This function gets the sda pin state.
108  *
109  * @param The sda pin state.
110  */
n32_get_sda(void * data)111 static rt_int32_t n32_get_sda(void *data)
112 {
113     struct n32_soft_i2c_config *cfg = (struct n32_soft_i2c_config *)data;
114     return rt_pin_read(cfg->sda);
115 }
116 
117 /**
118  * This function gets the scl pin state.
119  *
120  * @param The scl pin state.
121  */
n32_get_scl(void * data)122 static rt_int32_t n32_get_scl(void *data)
123 {
124     struct n32_soft_i2c_config *cfg = (struct n32_soft_i2c_config *)data;
125     return rt_pin_read(cfg->scl);
126 }
127 /**
128  * The time delay function.
129  *
130  * @param microseconds.
131  */
n32_udelay(rt_uint32_t us)132 static void n32_udelay(rt_uint32_t us)
133 {
134     rt_uint32_t ticks;
135     rt_uint32_t told, tnow, tcnt = 0;
136     rt_uint32_t reload = SysTick->LOAD;
137 
138     ticks = us * reload / (1000000 / RT_TICK_PER_SECOND);
139     told = SysTick->VAL;
140     while (1)
141     {
142         tnow = SysTick->VAL;
143         if (tnow != told)
144         {
145             if (tnow < told)
146             {
147                 tcnt += told - tnow;
148             }
149             else
150             {
151                 tcnt += reload - tnow + told;
152             }
153             told = tnow;
154             if (tcnt >= ticks)
155             {
156                 break;
157             }
158         }
159     }
160 }
161 
162 static const struct rt_i2c_bit_ops n32_bit_ops_default =
163 {
164     .data     = RT_NULL,
165     .pin_init = n32_i2c_pin_init,
166     .set_sda  = n32_set_sda,
167     .set_scl  = n32_set_scl,
168     .get_sda  = n32_get_sda,
169     .get_scl  = n32_get_scl,
170     .udelay   = n32_udelay,
171     .delay_us = 1,
172     .timeout  = 100,
173     .i2c_pin_init_flag = RT_FALSE
174 };
175 
176 /**
177  * if i2c is locked, this function will unlock it
178  *
179  * @param n32 config class
180  *
181  * @return RT_EOK indicates successful unlock.
182  */
n32_i2c_bus_unlock(const struct n32_soft_i2c_config * cfg)183 static rt_err_t n32_i2c_bus_unlock(const struct n32_soft_i2c_config *cfg)
184 {
185     rt_int32_t i = 0;
186 
187     if (PIN_LOW == rt_pin_read(cfg->sda))
188     {
189         while (i++ < 9)
190         {
191             rt_pin_write(cfg->scl, PIN_HIGH);
192             n32_udelay(100);
193             rt_pin_write(cfg->scl, PIN_LOW);
194             n32_udelay(100);
195         }
196     }
197     if (PIN_LOW == rt_pin_read(cfg->sda))
198     {
199         return -RT_ERROR;
200     }
201 
202     return RT_EOK;
203 }
204 
205 /* I2C initialization function */
rt_hw_i2c_init(void)206 int rt_hw_i2c_init(void)
207 {
208     rt_size_t obj_num = sizeof(i2c_obj) / sizeof(struct n32_i2c);
209     rt_err_t result;
210 
211     for (rt_size_t i = 0; i < obj_num; i++)
212     {
213         i2c_obj[i].ops = n32_bit_ops_default;
214         i2c_obj[i].ops.data = (void *)&soft_i2c_config[i];
215         i2c_obj[i].i2c_bus.priv = &i2c_obj[i].ops;
216 
217         result = rt_i2c_bit_add_bus(&i2c_obj[i].i2c_bus, soft_i2c_config[i].bus_name);
218         RT_ASSERT(result == RT_EOK);
219         n32_i2c_bus_unlock(&soft_i2c_config[i]);
220 
221         LOG_D("software simulation %s init done, pin scl: %d, pin sda %d",
222               soft_i2c_config[i].bus_name,
223               soft_i2c_config[i].scl,
224               soft_i2c_config[i].sda);
225     }
226 
227     return RT_EOK;
228 }
229 INIT_BOARD_EXPORT(rt_hw_i2c_init);
230 
231 #endif /* RT_USING_I2C */
232