FabGL
ESP32 Display Controller and Graphics Library
tsi2c.cpp
1/*
2 Created by Fabrizio Di Vittorio (fdivitto2013@gmail.com) - <http://www.fabgl.com>
3 Copyright (c) 2019-2022 Fabrizio Di Vittorio.
4 All rights reserved.
5
6
7* Please contact fdivitto2013@gmail.com if you need a commercial license.
8
9
10* This library and related software is available under GPL v3.
11
12 FabGL is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 FabGL is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with FabGL. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26
27#ifdef ARDUINO
28
29
30#include "freertos/FreeRTOS.h"
31#include "freertos/task.h"
32
33#include "tsi2c.h"
34
35
36#pragma GCC optimize ("O2")
37
38
39namespace fabgl {
40
41
42#define I2C_COMMTASK_STACK 1000
43#define I2C_COMMTASK_PRIORITY 5
44#define I2C_DEFAULT_FREQUENCY 100000
45
46
47#define EVTGROUP_READY (1 << 0)
48#define EVTGROUP_WRITE (1 << 1)
49#define EVTGROUP_READ (1 << 2)
50#define EVTGROUP_DONE (1 << 3)
51
52
53I2C::I2C(int bus)
54 :
55 #if FABGL_ESP_IDF_VERSION < FABGL_ESP_IDF_VERSION_VAL(4, 4, 0)
56 m_i2c(nullptr),
57 #endif
58 m_i2cAvailable(false),
59 m_bus(bus),
60 m_commTaskHandle(nullptr),
61 m_eventGroup(nullptr)
62{
63}
64
65
66I2C::~I2C()
67{
68 end();
69}
70
71
72bool I2C::begin(gpio_num_t SDAGPIO, gpio_num_t SCLGPIO)
73{
74 m_SDAGPIO = SDAGPIO;
75 m_SCLGPIO = SCLGPIO;
76
77 m_eventGroup = xEventGroupCreate();
78
79 // why a task? Because esp32 i2c communications must be done on the same core
80 // must be pinned to one core (0 in this case)
81 xTaskCreatePinnedToCore(&commTaskFunc, "", I2C_COMMTASK_STACK, this, I2C_COMMTASK_PRIORITY, &m_commTaskHandle, 0);
82
83 // wait for commTaskFunc() ends initialization
84 xEventGroupWaitBits(m_eventGroup, EVTGROUP_DONE, true, false, portMAX_DELAY);
85
86 // ready to accept jobs
87 xEventGroupSetBits(m_eventGroup, EVTGROUP_READY);
88
89 return m_i2cAvailable;
90}
91
92
93void I2C::end()
94{
95 if (m_commTaskHandle)
96 vTaskDelete(m_commTaskHandle);
97 m_commTaskHandle = nullptr;
98
99 #if FABGL_ESP_IDF_VERSION < FABGL_ESP_IDF_VERSION_VAL(4, 4, 0)
100 if (m_i2c)
101 i2cRelease(m_i2c);
102 m_i2c = nullptr;
103 #endif
104
105 if (m_eventGroup)
106 vEventGroupDelete(m_eventGroup);
107 m_eventGroup = nullptr;
108
109 m_i2cAvailable = false;
110}
111
112
113bool I2C::write(int address, uint8_t * buffer, int size, int frequency, int timeOutMS)
114{
115 // wait for I2C to be ready
116 xEventGroupWaitBits(m_eventGroup, EVTGROUP_READY, true, false, portMAX_DELAY);
117
118 m_jobInfo.frequency = frequency;
119 m_jobInfo.address = address;
120 m_jobInfo.buffer = buffer;
121 m_jobInfo.size = size;
122 m_jobInfo.timeout = timeOutMS;
123
124 // unlock comm task for writing
125 // wait for comm task to finish job
126 xEventGroupSync(m_eventGroup, EVTGROUP_WRITE, EVTGROUP_DONE, portMAX_DELAY);
127
128 #if FABGL_ESP_IDF_VERSION < FABGL_ESP_IDF_VERSION_VAL(4, 4, 0)
129 bool ret = (m_jobInfo.lastError == I2C_ERROR_OK);
130 #else
131 bool ret = (m_jobInfo.lastError == ESP_OK);
132 #endif
133
134 // makes I2C ready for new requests
135 xEventGroupSetBits(m_eventGroup, EVTGROUP_READY);
136
137 return ret;
138}
139
140
141int I2C::read(int address, uint8_t * buffer, int size, int frequency, int timeOutMS)
142{
143 // wait for I2C to be ready
144 xEventGroupWaitBits(m_eventGroup, EVTGROUP_READY, true, false, portMAX_DELAY);
145
146 m_jobInfo.frequency = frequency;
147 m_jobInfo.address = address;
148 m_jobInfo.buffer = buffer;
149 m_jobInfo.size = size;
150 m_jobInfo.timeout = timeOutMS;
151
152 // unlock comm task for reading
153 // wait for comm task to finish job
154 xEventGroupSync(m_eventGroup, EVTGROUP_READ, EVTGROUP_DONE, portMAX_DELAY);
155
156 int ret = m_jobInfo.readCount;
157
158 // makes I2C ready for new requests
159 xEventGroupSetBits(m_eventGroup, EVTGROUP_READY);
160
161 return ret;
162}
163
164
165#if FABGL_ESP_IDF_VERSION >= FABGL_ESP_IDF_VERSION_VAL(4, 4, 0)
166static int i2cGetFrequency(uint8_t i2c_num)
167{
168 uint32_t r;
169 i2cGetClock(i2c_num, &r);
170 return r;
171}
172static void i2cSetFrequency(uint8_t i2c_num, int freq)
173{
174 i2cSetClock(i2c_num, freq);
175}
176#endif
177
178
179void I2C::commTaskFunc(void * pvParameters)
180{
181 I2C * ths = (I2C*) pvParameters;
182
183 auto initRes = i2cInit(ths->m_bus, ths->m_SDAGPIO, ths->m_SCLGPIO, I2C_DEFAULT_FREQUENCY);
184
185 #if FABGL_ESP_IDF_VERSION < FABGL_ESP_IDF_VERSION_VAL(4, 4, 0)
186 if (!initRes) {
187 ESP_LOGE("FabGL", "unable to init I2C");
188 abort();
189 }
190 auto i2c = initRes;
191 i2cFlush(i2c);
192 ths->m_i2c = i2c;
193 #else
194 if (initRes != ESP_OK) {
195 ESP_LOGE("FabGL", "unable to init I2C");
196 abort();
197 }
198 auto i2c = ths->m_bus;
199 #endif
200
201 ths->m_i2cAvailable = true;
202
203 // get initial default frequency
204 int freq = i2cGetFrequency(i2c);
205
206 I2CJobInfo * job = &ths->m_jobInfo;
207
208 // main send/receive loop
209 while (true) {
210
211 // unlock waiting task
212 xEventGroupSetBits(ths->m_eventGroup, EVTGROUP_DONE);
213
214 // wait for another job
215 auto bits = xEventGroupWaitBits(ths->m_eventGroup, EVTGROUP_WRITE | EVTGROUP_READ, true, false, portMAX_DELAY);
216
217 // setup frequency if necessary
218 if (freq != job->frequency) {
219 freq = job->frequency;
220 i2cSetFrequency(i2c, freq);
221 }
222
223 #if FABGL_ESP_IDF_VERSION < FABGL_ESP_IDF_VERSION_VAL(4, 4, 0)
224 if (bits & EVTGROUP_WRITE)
225 job->lastError = i2cWrite(i2c, job->address, job->buffer, job->size, true, job->timeout);
226 else if (bits & EVTGROUP_READ)
227 job->lastError = i2cRead(i2c, job->address, job->buffer, job->size, true, job->timeout, &job->readCount);
228 #else
229 if (bits & EVTGROUP_WRITE)
230 job->lastError = i2cWrite(i2c, job->address, job->buffer, job->size, job->timeout);
231 else if (bits & EVTGROUP_READ)
232 job->lastError = i2cRead(i2c, job->address, job->buffer, job->size, job->timeout, &job->readCount);
233 #endif
234
235 }
236
237}
238
239
240
241} // end of namespace
242
243
244#endif // #ifdef ARDUINO
bool write(int address, uint8_t *buffer, int size, int frequency=100000, int timeOutMS=50)
Sends a buffer to I2C bus.
Definition: tsi2c.cpp:113
I2C(int bus=0)
I2C class constructor.
Definition: tsi2c.cpp:53
bool begin(gpio_num_t SDAGPIO, gpio_num_t SCLGPIO)
Initializes I2C instance associating GPIOs to I2C signals.
Definition: tsi2c.cpp:72
int read(int address, uint8_t *buffer, int size, int frequency=100000, int timeOutMS=50)
Receives a buffer from I2C bus.
Definition: tsi2c.cpp:141
This file contains fabgl::I2C definition.