Add I2C slave support and include a simple example

Also fix the initial pins for the 2nd I2C interface onboard.
parent 09ec5e0d
......@@ -24,6 +24,8 @@
#include <Arduino.h>
#include <hardware/gpio.h>
#include <hardware/i2c.h>
#include <hardware/irq.h>
#include <hardware/regs/intctrl.h>
#include "Wire.h"
TwoWire::TwoWire(i2c_inst_t *i2c, pin_size_t sda, pin_size_t scl) {
......@@ -85,11 +87,16 @@ void TwoWire::begin() {
_buffLen = 0;
}
static void _handler0() {
Wire.onIRQ();
}
static void _handler1() {
Wire1.onIRQ();
}
// Slave mode
void TwoWire::begin(uint8_t addr) {
// Slave mode isn't documented in the SDK, need to twiddle raw registers
// and use bare interrupts. TODO to implement, for now.
#if 0
if (_running) {
// ERROR
return;
......@@ -97,11 +104,54 @@ void TwoWire::begin(uint8_t addr) {
_slave = true;
i2c_init(_i2c, _clkHz);
i2c_set_slave_mode(_i2c, true, addr);
// Our callback IRQ
_i2c->hw->intr_mask = (1<<10) | (1<<9) | (1<<5)| (1<<2);
int irqNo = I2C0_IRQ + i2c_hw_index(_i2c);
irq_set_exclusive_handler(irqNo, i2c_hw_index(_i2c) == 0 ? _handler0 : _handler1);
irq_set_enabled(irqNo, true);
gpio_set_function(_sda, GPIO_FUNC_I2C);
gpio_pull_up(_sda);
gpio_set_function(_scl, GPIO_FUNC_I2C);
gpio_pull_up(_scl);
#endif
_running = true;
}
void TwoWire::onIRQ() {
if (_i2c->hw->intr_stat & (1<<10)) {
_buffLen = 0;
_buffOff = 0;
_slaveStartDet = true;
_i2c->hw->clr_start_det;
}
if (_i2c->hw->intr_stat & (1<<9)) {
if (_onReceiveCallback) {
_onReceiveCallback(_buffLen);
}
_buffLen = 0;
_buffOff = 0;
_slaveStartDet = false;
_i2c->hw->clr_stop_det;
}
if (_i2c->hw->intr_stat & (1<<5)) {
// RD_REQ
if (_onRequestCallback) {
_onRequestCallback();
}
_i2c->hw->clr_rd_req;
}
if (_i2c->hw->intr_stat & (1<<2)) {
// RX_FULL
if (_slaveStartDet && (_buffLen < sizeof(_buff))) {
_buff[_buffLen++] = _i2c->hw->data_cmd & 0xff;
} else {
_i2c->hw->data_cmd;
}
}
}
void TwoWire::end() {
......@@ -161,11 +211,22 @@ uint8_t TwoWire::endTransmission() {
}
size_t TwoWire::write(uint8_t ucData) {
if (!_running || !_txBegun || (_buffLen == sizeof(_buff))) {
if (!_running) {
return 0;
}
_buff[_buffLen++] = ucData;
return 1 ;
if (_slave) {
// Wait for a spot in the TX FIFO
while (0 == _i2c->hw->status & (1<<1)) { /* noop wait */ }
_i2c->hw->data_cmd = ucData;
return 1;
} else {
if (!_txBegun || (_buffLen == sizeof(_buff))) {
return 0;
}
_buff[_buffLen++] = ucData;
return 1 ;
}
}
size_t TwoWire::write(const uint8_t *data, size_t quantity) {
......@@ -213,5 +274,4 @@ void TwoWire::onRequest(void(*function)(void))
}
TwoWire Wire(i2c0, 0, 1);
TwoWire Wire1(i2c1, 4, 5);
TwoWire Wire1(i2c1, 2, 3);
......@@ -75,6 +75,9 @@ public:
inline size_t write(int n) { return write((uint8_t)n); }
using Print::write;
// IRQ callback
void onIRQ();
private:
i2c_inst_t *_i2c;
pin_size_t _sda;
......@@ -94,6 +97,8 @@ private:
void (*_onRequestCallback)(void);
void (*_onReceiveCallback)(int);
bool _slaveStartDet = false;
// TWI clock frequency
static const uint32_t TWI_CLOCK = 100000;
};
......
// Simple I2C master and slave demo - Earle F. Philhower, III
// Released into the public domain
//
// Using both onboard I2C interfaces, have one master and one slave
// and send data both ways between them
//
// To run, connect GPIO0 to GPIO2, GPIO1 to GPIO3 on a single Pico
#include <Wire.h>
void setup() {
Serial.begin(115200);
delay(5000);
Wire.begin();
Wire1.begin(0x30);
Wire1.onReceive(recv);
Wire1.onRequest(req);
}
static char buff[100];
void loop() {
static int p;
char b[90];
// Write a value over I2C to the slave
Serial.println("Sending...");
Wire.beginTransmission(0x30);
sprintf(b, "pass %d", p++);
Wire.write(b, strlen(b));
Wire.endTransmission();
// Ensure the slave processing is done and print it out
delay(1000);
Serial.printf("buff: '%s'\r\n", buff);
// Read from the slave and print out
Wire.requestFrom(0x30, 6);
Serial.print("\nrecv: '");
while(Wire.available()) {
Serial.print((char)Wire.read());
}
Serial.println("'");
delay(1000);
}
// These are called in an **INTERRUPT CONTEXT** which means NO serial port
// access (i.e. Serial.print is illegal) and no memory allocations, etc.
// Called when the I2C slave gets written to
void recv(int len) {
int i;
// Just stuff the sent bytes into a global the main routine can pick up and use
for (i=0; i<len; i++) buff[i] = Wire1.read();
buff[i] = 0;
}
// Called when the I2C slave is read from
void req() {
static int ctr = 765;
char buff[7];
// Return a simple incrementing hex value
sprintf(buff, "%06X", (ctr++) % 65535);
Wire1.write(buff, 6);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment