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