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