-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0154654
commit 6180674
Showing
55 changed files
with
5,494 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,240 @@ | ||
/*************************************************** | ||
IoTuz class to provide basic driver methods for the ESP32 based IoTuz board | ||
from LCA 2017: https://github.com/CCHS-Melbourne/iotuz-esp32-hardware | ||
By Marc MERLIN <marc_soft@merlins.org> | ||
License: Apache 2.0. | ||
Required libraries: | ||
- Wire.h to support the pcf8574 port expander | ||
****************************************************/ | ||
|
||
#include <Wire.h> | ||
#include "IoTuz.h" | ||
|
||
volatile int16_t encoder0Pos = 0; | ||
|
||
// These need to be global because all sketches use them as a global | ||
// tft adafruit library (games use tft2 a separate library, for which we skip the init | ||
// since the adafruit init works fine for both) | ||
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_RST); | ||
// faster, better lib, that doesn't work yet. | ||
//ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC, TFT_RST); | ||
|
||
// If you are lacking the ESP32 patch, you will get no error, but the LEDs will not work | ||
Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, RGB_LED_PIN, NEO_GRB + NEO_KHZ800); | ||
|
||
// ADXL345 | ||
Adafruit_ADXL345_Unified accel = Adafruit_ADXL345_Unified(12345); | ||
|
||
// Until further notice, there is a hack to get HW SPI be as fast as SW SPI: | ||
// in espressif/esp32/cores/esp32/esp32-hal.h after the first define, add | ||
// #define CONFIG_DISABLE_HAL_LOCKS 1 | ||
// Use with caution, this may cause unknown issues | ||
|
||
void read_encoder_ISR() | ||
{ | ||
static uint8_t old_AB = 0; | ||
// grey code | ||
// http://hades.mech.northwestern.edu/index.php/Rotary_Encoder | ||
// also read up on 'Understanding Quadrature Encoded Signals' | ||
// https://www.pjrc.com/teensy/td_libs_Encoder.html | ||
// another interesting lib: https://github.com/0xPIT/encoder/blob/arduino/ClickEncoder.cpp | ||
static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0}; | ||
|
||
old_AB <<= 2; | ||
old_AB |= ((digitalRead(ENCODERB_PIN))?(1<<1):0) | ((digitalRead(ENCODERA_PIN))?(1<<0):0); | ||
encoder0Pos += ( enc_states[( old_AB & 0x0f )]); | ||
} | ||
|
||
// Talking to the port expander: | ||
// I2C/TWI success (transaction was successful). | ||
#define ku8TWISuccess 0 | ||
// I2C/TWI device not present (address sent, NACK received). | ||
#define ku8TWIDeviceNACK 2 | ||
// I2C/TWI data not received (data sent, NACK received). | ||
#define ku8TWIDataNACK 3 | ||
// I2C/TWI other error. | ||
#define ku8TWIError 4 | ||
void IoTuz::pcf8574_write_(uint8_t dt) | ||
{ | ||
uint8_t error; | ||
|
||
Wire.beginTransmission(I2C_EXPANDER); | ||
// Serial.print("Writing to I2CEXP: "); | ||
// Serial.println(dt); | ||
Wire.write(dt); | ||
error = Wire.endTransmission(); | ||
if (error != ku8TWISuccess) { | ||
// FIXME: do something here if you like | ||
} | ||
} | ||
|
||
// To clear bit #7, send 128 | ||
void IoTuz::i2cexp_clear_bits(uint8_t bitfield) | ||
{ | ||
// set bits to clear to 0, all other to 1, binary and to clear the bits | ||
_i2cexp &= (~bitfield); | ||
pcf8574_write_(_i2cexp); | ||
} | ||
|
||
// To set bit #7, send 128 | ||
void IoTuz::i2cexp_set_bits(uint8_t bitfield) | ||
{ | ||
_i2cexp |= bitfield; | ||
pcf8574_write_(_i2cexp); | ||
} | ||
|
||
uint8_t IoTuz::i2cexp_read() | ||
{ | ||
// For read to work, we must have sent 1 bits on the ports that get used as input | ||
// This is done by i2cexp_clear_bits called in setup. | ||
Wire.requestFrom(I2C_EXPANDER, 1); // FIXME: deal with returned error here? | ||
while (Wire.available() < 1) ; | ||
uint8_t read = ~Wire.read(); // Apparently one has to invert the bits read | ||
// When no buttons are pushed, this returns 0x91, which includes some ports | ||
// we use as output, so we do need to filter out the ports used as read. | ||
// Serial.println(read, HEX); | ||
return read; | ||
} | ||
|
||
int16_t IoTuz::read_encoder() | ||
{ | ||
return encoder0Pos; | ||
} | ||
|
||
bool IoTuz::encoder_changed() { | ||
static int16_t old_encoder0Pos = 0; | ||
if (encoder0Pos != old_encoder0Pos) | ||
{ | ||
old_encoder0Pos = encoder0Pos; | ||
return true; | ||
} | ||
return false; | ||
} | ||
|
||
ButtState IoTuz::read_encoder_button() | ||
{ | ||
static bool butEnc = false; | ||
uint8_t butt_state = i2cexp_read() & I2CEXP_ENC_BUT; | ||
|
||
if (butt_state && !butEnc) | ||
{ | ||
butEnc = true; | ||
//Serial.println("Encoder Button Pushed"); | ||
return ENC_PUSHED; | ||
} | ||
if (!butt_state && butEnc) | ||
{ | ||
butEnc = false; | ||
//Serial.println("Encoder Button Released"); | ||
return ENC_RELEASED; | ||
} | ||
return (butt_state?ENC_DOWN:ENC_UP); | ||
} | ||
|
||
// True turns the BL on | ||
void IoTuz::screen_bl(bool state) { | ||
state ? i2cexp_clear_bits(I2CEXP_LCD_BL_CTR) : i2cexp_set_bits(I2CEXP_LCD_BL_CTR); | ||
} | ||
|
||
IoTuz::IoTuz() | ||
{ | ||
// Any write to I2CEXP should contain those mask bits so allow reads to work later | ||
_i2cexp = I2CEXP_IMASK; | ||
|
||
pinMode (ENCODERA_PIN, INPUT_PULLUP); | ||
pinMode (ENCODERB_PIN, INPUT_PULLUP); | ||
|
||
pinMode(SPI_MOSI, OUTPUT); | ||
pinMode(SPI_MISO, INPUT); | ||
pinMode(SPI_CLK, OUTPUT); | ||
|
||
// Joystick Setup | ||
pinMode(JOYSTICK_BUT_PIN, INPUT_PULLUP); | ||
|
||
// TFT Setup | ||
pinMode(TFT_CS, OUTPUT); | ||
pinMode(TFT_DC, OUTPUT); | ||
pinMode(TFT_RST, OUTPUT); | ||
} | ||
|
||
void IoTuz::begin() | ||
{ | ||
Serial.begin(115200); | ||
Serial.println("Serial Begin"); | ||
|
||
// required for i2exp to work | ||
Wire.begin(); | ||
|
||
// Hardware SPI on ESP32 is actually slower than software SPI. Giving 80Mhz | ||
// here does not make things any faster. There seems to be a fair amount of | ||
// overhead in the fancier hw SPI on ESP32 which is designed to send more than | ||
// one byte at the time, and only ends up sending one byte when called from an | ||
// arduino library. | ||
// Sadly, using software SPI in the adafruit library would prevent SPI from working | ||
// in the touch screen code which only supports hardware SPI | ||
// The TFT code runs at 24Mhz as per below, but testing shows that any speed over 2Mhz | ||
// seems ignored and taken down to 2Mhz | ||
//SPI.beginTransaction(SPISettings(24000000, MSBFIRST, SPI_MODE0)); | ||
|
||
// Talking to the touch screen can only work at 2Mhz, and both drivers change the SPI | ||
// speed before sending data, so this works transparently. | ||
|
||
// ESP32 requires an extended begin with pin mappings (which is not supported by the | ||
// adafruit library), so we do an explicit begin here and then the other SPI libraries work | ||
// with hardware SPI as setup here (they will do a second begin without pin mappings and | ||
// that will be ignored). | ||
SPI.begin(SPI_CLK, SPI_MISO, SPI_MOSI); | ||
|
||
// Turn off TFT by default. | ||
// Note this also initializes the read bits on PCF8574 by setting them to 1 as per I2CEXP_IMASK | ||
i2cexp_set_bits(I2CEXP_LCD_BL_CTR); | ||
|
||
Serial.println("ILI9341 Test!"); | ||
tft.begin(); | ||
// read diagnostics (optional but can help debug problems) | ||
uint8_t x = tft.readcommand8(ILI9341_RDMODE); | ||
Serial.print("Display Power Mode: 0x"); Serial.println(x, HEX); | ||
x = tft.readcommand8(ILI9341_RDMADCTL); | ||
Serial.print("MADCTL Mode: 0x"); Serial.println(x, HEX); | ||
x = tft.readcommand8(ILI9341_RDPIXFMT); | ||
Serial.print("Pixel Format: 0x"); Serial.println(x, HEX); | ||
x = tft.readcommand8(ILI9341_RDIMGFMT); | ||
Serial.print("Image Format: 0x"); Serial.println(x, HEX); | ||
x = tft.readcommand8(ILI9341_RDSELFDIAG); | ||
Serial.print("Self Diagnostic: 0x"); Serial.println(x, HEX); | ||
tft.setRotation(3); | ||
tftw = tft.width(), tfth = tft.height(); | ||
Serial.print("Resolution: "); Serial.print(tftw); | ||
Serial.print(" x "); Serial.println(tfth); | ||
Serial.println(F("Done!")); | ||
|
||
|
||
// Init Accel | ||
if(!accel.begin()) { | ||
/* There was a problem detecting the ADXL345 ... check your connections */ | ||
Serial.println("Ooops, no ADXL345 detected ... Check your wiring!"); | ||
while(1); | ||
} | ||
accel.setRange(ADXL345_RANGE_16_G); | ||
sensor_t sensor; | ||
accel.getSensor(&sensor); | ||
Serial.println("------------------------------------"); | ||
Serial.print ("Sensor: "); Serial.println(sensor.name); | ||
Serial.print ("Driver Ver: "); Serial.println(sensor.version); | ||
Serial.print ("Unique ID: "); Serial.println(sensor.sensor_id); | ||
Serial.print ("Max Value: "); Serial.print(sensor.max_value); Serial.println(" m/s^2"); | ||
Serial.print ("Min Value: "); Serial.print(sensor.min_value); Serial.println(" m/s^2"); | ||
Serial.print ("Resolution: "); Serial.print(sensor.resolution); Serial.println(" m/s^2"); | ||
Serial.println("------------------------------------"); | ||
Serial.println(""); | ||
|
||
// Initialize rotary encoder reading and decoding | ||
attachInterrupt(ENCODERA_PIN, read_encoder_ISR, CHANGE); | ||
attachInterrupt(ENCODERB_PIN, read_encoder_ISR, CHANGE); | ||
} | ||
|
||
// vim:sts=4:sw=4 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
#ifndef IOTUZ_H | ||
#define IOTUZ_H | ||
|
||
#include <Wire.h> | ||
#include <SPI.h> | ||
|
||
#include <Adafruit_GFX.h> | ||
// Support for LCD screen | ||
// The latest version of that library may not be up to date and miss a patch for ESP32 | ||
// which will cause a compilation error: | ||
// Adafruit_ILI9341.cpp:113:3: error: 'mosiport' was not declared in this scope | ||
// If so, get the latest version from github, or just patch this single line | ||
// https://github.com/adafruit/Adafruit_ILI9341/blob/master/Adafruit_ILI9341.cpp#L98 | ||
#include <Adafruit_ILI9341.h> | ||
// faster, better lib, that doesn't work yet. | ||
//#include <ILI9341_t3.h> | ||
|
||
// https://learn.adafruit.com/adafruit-neopixel-uberguide/ | ||
// Support for APA106 RGB LEDs | ||
// APA106 is somewhat compatible with WS2811 or WS2812 (well, maybe not 100%, but close enough to work) | ||
// I have patched the Adafruit library to support ESP32. If that hasn't been merged yet, see this patch | ||
// https://github.com/adafruit/Adafruit_NeoPixel/pull/125 | ||
// If you do NOT apply my patch, the LEDS WILL NOT WORK | ||
#include "Adafruit_NeoPixel.h" | ||
|
||
// Accelerometer | ||
#include <Adafruit_Sensor.h> | ||
#include <Adafruit_ADXL345_U.h> | ||
|
||
|
||
// https://github.com/CCHS-Melbourne/iotuz-esp32-hardware/wiki has hardware mapping details | ||
/* | ||
I2C addresses: | ||
Audio: 0x1A | ||
IO_EXP: 0x20 => Spec sheet says | ||
"To enter the Read mode the master (microcontroller) addresses the slave | ||
device and sets the last bit of the address byte to logic 1 (address byte read)" | ||
The Wire lib automatically uses the right I/O port for read and write. | ||
ADXL: 0x53 | ||
BME230: 0x77 (Temp/Humidity/Pressure) | ||
*/ | ||
|
||
|
||
|
||
// TFT + Touch Screen Setup Start | ||
// These are the minimal changes from v0.1 to get the LCD working | ||
#define TFT_DC 4 | ||
#define TFT_CS 19 | ||
#define TFT_RST 32 | ||
// SPI Pins are shared by TFT, touch screen, SD Card | ||
#define SPI_MISO 12 | ||
#define SPI_MOSI 13 | ||
#define SPI_CLK 14 | ||
|
||
// APA106 LEDs | ||
#define RGB_LED_PIN 23 | ||
#define NUMPIXELS 2 | ||
|
||
// LCD brightness control and touchscreen CS are behind the port | ||
// expander, as well as both push buttons | ||
#define I2C_EXPANDER 0x20 //0100000 (7bit) address of the IO expander on i2c bus | ||
|
||
/* Port expander PCF8574, access via I2C on */ | ||
#define I2CEXP_ACCEL_INT 0x01 // (In) | ||
#define I2CEXP_A_BUT 0x02 // (In) | ||
#define I2CEXP_B_BUT 0x04 // (In) | ||
#define I2CEXP_ENC_BUT 0x08 // (In) | ||
#define I2CEXP_SD_CS 0x10 // (Out) | ||
#define I2CEXP_TOUCH_INT 0x20 // (In) | ||
#define I2CEXP_TOUCH_CS 0x40 // (Out) | ||
#define I2CEXP_LCD_BL_CTR 0x80 // (Out) | ||
|
||
// Dealing with the I/O expander is a bit counter intuitive. There is no difference between a | ||
// write meant to toggle an output port, and a write designed to turn off pull down resistors and trigger | ||
// a read request. | ||
// The write just before the read should have bits high on the bits you'd like to read back, but you | ||
// may get high bits back on other bits you didn't turn off the pull down resistor on. This is normal. | ||
// Just filter out the bits you're trying to get from the value read back and keep in mind that when | ||
// you write, you should still send the right bit values for the output pins. | ||
// This is all stored in i2cexp which we initialize to the bits used as input: | ||
#define I2CEXP_IMASK ( I2CEXP_ACCEL_INT + I2CEXP_A_BUT + I2CEXP_B_BUT + I2CEXP_ENC_BUT + I2CEXP_TOUCH_INT ) | ||
|
||
// This is calibration data for the raw touch data to the screen coordinates | ||
#define TS_MINX 320 | ||
#define TS_MINY 220 | ||
#define TS_MAXX 3920 | ||
#define TS_MAXY 3820 | ||
#define MINPRESSURE 400 | ||
#define MAXPRESSURE 3000 | ||
|
||
// Joystick Setup | ||
#define JOYSTICK_X_PIN 39 | ||
#define JOYSTICK_Y_PIN 34 | ||
#define JOYSTICK_BUT_PIN 0 | ||
|
||
// Center on my IoTuz board (not used anymore) | ||
#define JOYSTICK_CENTERX 1785 | ||
#define JOYSTICK_CENTERY 1854 | ||
|
||
#define ENCODERA_PIN 15 | ||
#define ENCODERB_PIN 36 | ||
|
||
extern Adafruit_ILI9341 tft; | ||
extern Adafruit_NeoPixel pixels; | ||
extern Adafruit_ADXL345_Unified accel; | ||
|
||
|
||
typedef enum { | ||
ENC_DOWN = 0, | ||
ENC_PUSHED = 1, | ||
ENC_UP = 2, | ||
ENC_RELEASED = 3, | ||
|
||
} ButtState; | ||
|
||
class IoTuz { | ||
public: | ||
IoTuz(); | ||
|
||
// tft_width, tft_height, calculated in setup after tft init | ||
uint16_t tftw, tfth; | ||
|
||
void i2cexp_clear_bits(uint8_t); | ||
void i2cexp_set_bits(uint8_t); | ||
uint8_t i2cexp_read(); | ||
void screen_bl(bool); | ||
int16_t read_encoder(); | ||
bool encoder_changed(); | ||
ButtState read_encoder_button(); | ||
void begin(); | ||
|
||
private: | ||
uint8_t _i2cexp; | ||
void pcf8574_write_(uint8_t); | ||
}; | ||
|
||
|
||
|
||
#endif | ||
|
||
// vim:sts=4:sw=4 |
Oops, something went wrong.