1 /*
2  * Copyright (c) 2006-2022, Synwit Technology Co.,Ltd.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2021-07-01     lik          first version
9  */
10 
11 #include "drv_soft_i2c.h"
12 
13 #ifdef RT_USING_I2C
14 #ifdef BSP_USING_I2C
15 
16 //#define DRV_DEBUG
17 #define LOG_TAG "drv.i2c"
18 #include <drv_log.h>
19 
20 #if !defined(BSP_USING_I2C0) && !defined(BSP_USING_I2C1)
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 #ifdef BSP_USING_I2C0
26 #define I2C0_BUS_CFG             \
27     {                            \
28         .scl = BSP_I2C0_SCL_PIN, \
29         .sda = BSP_I2C0_SDA_PIN, \
30         .name = "i2c0",          \
31     }
32 #endif
33 
34 #ifdef BSP_USING_I2C1
35 #define I2C1_BUS_CFG             \
36     {                            \
37         .scl = BSP_I2C1_SCL_PIN, \
38         .sda = BSP_I2C1_SDA_PIN, \
39         .name = "i2c1",          \
40     }
41 #endif
42 
43 /* swm config class */
44 struct swm_soft_i2c_cfg
45 {
46     rt_uint8_t scl;
47     rt_uint8_t sda;
48     const char *name;
49 };
50 /* swm i2c dirver class */
51 struct swm_soft_i2c_device
52 {
53     struct rt_i2c_bit_ops ops;
54     struct rt_i2c_bus_device i2c_bus;
55 };
56 
57 static const struct swm_soft_i2c_cfg swm_soft_i2c_cfg[] =
58     {
59 #ifdef BSP_USING_I2C0
60         I2C0_BUS_CFG,
61 #endif
62 #ifdef BSP_USING_I2C1
63         I2C1_BUS_CFG,
64 #endif
65 };
66 
67 static struct swm_soft_i2c_device i2c_obj[sizeof(swm_soft_i2c_cfg) / sizeof(swm_soft_i2c_cfg[0])];
68 
69 /**
70  * This function initializes the i2c pin.
71  *
72  * @param swm i2c dirver class.
73  */
swm_i2c_gpio_init(struct swm_soft_i2c_device * i2c)74 static void swm_i2c_gpio_init(struct swm_soft_i2c_device *i2c)
75 {
76     struct swm_soft_i2c_cfg *soft_i2c_cfg = (struct swm_soft_i2c_cfg *)i2c->ops.data;
77 
78     rt_pin_mode(soft_i2c_cfg->scl, PIN_MODE_OUTPUT_OD);
79     rt_pin_mode(soft_i2c_cfg->sda, PIN_MODE_OUTPUT_OD);
80 
81     rt_pin_write(soft_i2c_cfg->scl, PIN_HIGH);
82     rt_pin_write(soft_i2c_cfg->sda, PIN_HIGH);
83 }
84 
85 /**
86  * This function sets the sda pin.
87  *
88  * @param swm config class.
89  * @param The sda pin state.
90  */
swm_i2c_set_sda(void * data,rt_int32_t state)91 static void swm_i2c_set_sda(void *data, rt_int32_t state)
92 {
93     struct swm_soft_i2c_cfg *soft_i2c_cfg = (struct swm_soft_i2c_cfg *)data;
94     rt_pin_mode(soft_i2c_cfg->sda, PIN_MODE_OUTPUT_OD);
95     if (state)
96     {
97         rt_pin_write(soft_i2c_cfg->sda, PIN_HIGH);
98     }
99     else
100     {
101         rt_pin_write(soft_i2c_cfg->sda, PIN_LOW);
102     }
103 }
104 
105 /**
106  * This function sets the scl pin.
107  *
108  * @param swm config class.
109  * @param The scl pin state.
110  */
swm_i2c_set_scl(void * data,rt_int32_t state)111 static void swm_i2c_set_scl(void *data, rt_int32_t state)
112 {
113     struct swm_soft_i2c_cfg *soft_i2c_cfg = (struct swm_soft_i2c_cfg *)data;
114     rt_pin_mode(soft_i2c_cfg->scl, PIN_MODE_OUTPUT_OD);
115     if (state)
116     {
117         rt_pin_write(soft_i2c_cfg->scl, PIN_HIGH);
118     }
119     else
120     {
121         rt_pin_write(soft_i2c_cfg->scl, PIN_LOW);
122     }
123 }
124 
125 /**
126  * This function gets the sda pin state.
127  *
128  * @param The sda pin state.
129  */
swm_i2c_get_sda(void * data)130 static rt_int32_t swm_i2c_get_sda(void *data)
131 {
132     struct swm_soft_i2c_cfg *soft_i2c_cfg = (struct swm_soft_i2c_cfg *)data;
133     rt_pin_mode(soft_i2c_cfg->sda, PIN_MODE_INPUT_PULLDOWN);
134     return rt_pin_read(soft_i2c_cfg->sda);
135 }
136 
137 /**
138  * This function gets the scl pin state.
139  *
140  * @param The scl pin state.
141  */
swm_i2c_get_scl(void * data)142 static rt_int32_t swm_i2c_get_scl(void *data)
143 {
144     struct swm_soft_i2c_cfg *soft_i2c_cfg = (struct swm_soft_i2c_cfg *)data;
145     rt_pin_mode(soft_i2c_cfg->scl, PIN_MODE_INPUT_PULLDOWN);
146     return rt_pin_read(soft_i2c_cfg->scl);
147 }
148 
149 /**
150  * The time delay function.
151  *
152  * @param microseconds.
153  */
swm_i2c_udelay(rt_uint32_t us)154 static void swm_i2c_udelay(rt_uint32_t us)
155 {
156     rt_uint32_t ticks;
157     rt_uint32_t told, tnow, tcnt = 0;
158     rt_uint32_t reload = SysTick->LOAD;
159 
160     ticks = us * reload / (1000000 / RT_TICK_PER_SECOND);
161     told = SysTick->VAL;
162     while (1)
163     {
164         tnow = SysTick->VAL;
165         if (tnow != told)
166         {
167             if (tnow < told)
168             {
169                 tcnt += told - tnow;
170             }
171             else
172             {
173                 tcnt += reload - tnow + told;
174             }
175             told = tnow;
176             if (tcnt >= ticks)
177             {
178                 break;
179             }
180         }
181     }
182 }
183 
184 static const struct rt_i2c_bit_ops swm_i2c_bit_ops =
185     {
186         .data = RT_NULL,
187         .set_sda = swm_i2c_set_sda,
188         .set_scl = swm_i2c_set_scl,
189         .get_sda = swm_i2c_get_sda,
190         .get_scl = swm_i2c_get_scl,
191         .udelay = swm_i2c_udelay,
192         .delay_us = 1,
193         .timeout = 100};
194 
195 /* I2C initialization function */
swm_i2c_init(void)196 int swm_i2c_init(void)
197 {
198     rt_err_t result;
199 
200     for (int i = 0; i < sizeof(i2c_obj) / sizeof(struct swm_soft_i2c_device); i++)
201     {
202         i2c_obj[i].ops = swm_i2c_bit_ops;
203         i2c_obj[i].ops.data = (void *)&swm_soft_i2c_cfg[i];
204         i2c_obj[i].i2c_bus.priv = &i2c_obj[i].ops;
205         swm_i2c_gpio_init(&i2c_obj[i]);
206         result = rt_i2c_bit_add_bus(&i2c_obj[i].i2c_bus, swm_soft_i2c_cfg[i].name);
207         RT_ASSERT(result == RT_EOK);
208 
209         LOG_D("software simulation %s init done, pin scl: %d, pin sda %d",
210               swm_soft_i2c_cfg[i].name,
211               swm_soft_i2c_cfg[i].scl,
212               swm_soft_i2c_cfg[i].sda);
213     }
214 
215     return RT_EOK;
216 }
217 INIT_DEVICE_EXPORT(swm_i2c_init);
218 #endif /* BSP_USING_I2C */
219 #endif /* RT_USING_I2C */
220