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-2021 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 
39 namespace 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 
53 I2C::I2C(int bus)
54  : m_i2c(nullptr),
55  m_bus(bus),
56  m_commTaskHandle(nullptr),
57  m_eventGroup(nullptr)
58 {
59 }
60 
61 
62 I2C::~I2C()
63 {
64  end();
65 }
66 
67 
68 bool I2C::begin(gpio_num_t SDAGPIO, gpio_num_t SCLGPIO)
69 {
70  m_SDAGPIO = SDAGPIO;
71  m_SCLGPIO = SCLGPIO;
72 
73  m_eventGroup = xEventGroupCreate();
74 
75  // why a task? Because esp32 i2c communications must be done on the same core
76  // must be pinned to one core (0 in this case)
77  xTaskCreatePinnedToCore(&commTaskFunc, "", I2C_COMMTASK_STACK, this, I2C_COMMTASK_PRIORITY, &m_commTaskHandle, 0);
78 
79  // wait for commTaskFunc() ends initialization
80  xEventGroupWaitBits(m_eventGroup, EVTGROUP_DONE, true, false, portMAX_DELAY);
81 
82  // ready to accept jobs
83  xEventGroupSetBits(m_eventGroup, EVTGROUP_READY);
84 
85  return m_i2c != nullptr;
86 }
87 
88 
89 void I2C::end()
90 {
91  if (m_commTaskHandle)
92  vTaskDelete(m_commTaskHandle);
93  m_commTaskHandle = nullptr;
94 
95  if (m_i2c)
96  i2cRelease(m_i2c);
97  m_i2c = nullptr;
98 
99  if (m_eventGroup)
100  vEventGroupDelete(m_eventGroup);
101  m_eventGroup = nullptr;
102 }
103 
104 
105 bool I2C::write(int address, uint8_t * buffer, int size, int frequency, int timeOutMS)
106 {
107  // wait for I2C to be ready
108  xEventGroupWaitBits(m_eventGroup, EVTGROUP_READY, true, false, portMAX_DELAY);
109 
110  m_jobInfo.frequency = frequency;
111  m_jobInfo.address = address;
112  m_jobInfo.buffer = buffer;
113  m_jobInfo.size = size;
114  m_jobInfo.timeout = timeOutMS;
115 
116  // unlock comm task for writing
117  // wait for comm task to finish job
118  xEventGroupSync(m_eventGroup, EVTGROUP_WRITE, EVTGROUP_DONE, portMAX_DELAY);
119 
120 
121  bool ret = (m_jobInfo.lastError == I2C_ERROR_OK);
122 
123  // makes I2C ready for new requests
124  xEventGroupSetBits(m_eventGroup, EVTGROUP_READY);
125 
126  return ret;
127 }
128 
129 
130 int I2C::read(int address, uint8_t * buffer, int size, int frequency, int timeOutMS)
131 {
132  // wait for I2C to be ready
133  xEventGroupWaitBits(m_eventGroup, EVTGROUP_READY, true, false, portMAX_DELAY);
134 
135  m_jobInfo.frequency = frequency;
136  m_jobInfo.address = address;
137  m_jobInfo.buffer = buffer;
138  m_jobInfo.size = size;
139  m_jobInfo.timeout = timeOutMS;
140 
141  // unlock comm task for reading
142  // wait for comm task to finish job
143  xEventGroupSync(m_eventGroup, EVTGROUP_READ, EVTGROUP_DONE, portMAX_DELAY);
144 
145  int ret = m_jobInfo.readCount;
146 
147  // makes I2C ready for new requests
148  xEventGroupSetBits(m_eventGroup, EVTGROUP_READY);
149 
150  return ret;
151 }
152 
153 
154 
155 void I2C::commTaskFunc(void * pvParameters)
156 {
157  I2C * ths = (I2C*) pvParameters;
158 
159  i2c_t * i2c = i2cInit(ths->m_bus, ths->m_SDAGPIO, ths->m_SCLGPIO, I2C_DEFAULT_FREQUENCY);
160  if (!i2c) {
161  ESP_LOGE("unable to init I2C");
162  abort();
163  }
164 
165  i2cFlush(i2c);
166 
167  ths->m_i2c = i2c;
168 
169  // get initial default frequency
170  int freq = i2cGetFrequency(i2c);
171 
172  I2CJobInfo * job = &ths->m_jobInfo;
173 
174  // main send/receive loop
175  while (true) {
176 
177  // unlock waiting task
178  xEventGroupSetBits(ths->m_eventGroup, EVTGROUP_DONE);
179 
180  // wait for another job
181  auto bits = xEventGroupWaitBits(ths->m_eventGroup, EVTGROUP_WRITE | EVTGROUP_READ, true, false, portMAX_DELAY);
182 
183  // setup frequency if necessary
184  if (freq != job->frequency) {
185  freq = job->frequency;
186  i2cSetFrequency(i2c, freq);
187  }
188 
189  if (bits & EVTGROUP_WRITE)
190  job->lastError = i2cWrite(i2c, job->address, job->buffer, job->size, true, job->timeout);
191  else if (bits & EVTGROUP_READ)
192  job->lastError = i2cRead(i2c, job->address, job->buffer, job->size, true, job->timeout, &job->readCount);
193 
194  }
195 
196 }
197 
198 
199 
200 } // end of namespace
201 
202 
203 #endif // #ifdef ARDUINO
bool begin(gpio_num_t SDAGPIO, gpio_num_t SCLGPIO)
Initializes I2C instance associating GPIOs to I2C signals.
Definition: tsi2c.cpp:68
I2C class allows multiple tasks to communicate with I2C devices, serializing read/write jobs...
Definition: tsi2c.h:80
I2C(int bus=0)
I2C class constructor.
Definition: tsi2c.cpp:53
int read(int address, uint8_t *buffer, int size, int frequency=100000, int timeOutMS=50)
Receives a buffer from I2C bus.
Definition: tsi2c.cpp:130
Definition: canvas.cpp:36
This file contains fabgl::I2C definition.
bool write(int address, uint8_t *buffer, int size, int frequency=100000, int timeOutMS=50)
Sends a buffer to I2C bus.
Definition: tsi2c.cpp:105