247 lines
6.5 KiB
C
247 lines
6.5 KiB
C
//| Copyright: (C) 2018-2020 Kevin Larke <contact AT larke DOT org>
|
|
//| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
|
|
|
|
// Note this program is designed to pair with c_client.
|
|
// The i2c connection may be made directly between the two Arduino SDA and SCL pins.
|
|
// No i2c pullup resistors are require.d
|
|
#define F_CPU 16000000UL
|
|
#define BAUD 38400
|
|
|
|
#include <util/setbaud.h>
|
|
#include <avr/io.h>
|
|
#include <avr/interrupt.h>
|
|
|
|
#include "twi.h"
|
|
|
|
#define ICP_PRE_SCALE (4)
|
|
#if defined(__AVR_ATmega2560__)
|
|
#define SERIAL_RX_ISR USART0_RX_vect
|
|
#elif defined(__AVR_ATmega328__)
|
|
#define SERIAL_RX_ISR USART_RX_vect
|
|
#else
|
|
#error Unknown target processor
|
|
#endif
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
#define SER_BUF_N (16) // size of receive buffer
|
|
|
|
// Note that 'ser_buf_i_idx' must be declared volatile or the
|
|
// the compare in the main loop will not work.
|
|
volatile int ser_buf_i_idx = 0; // receive buffer input index
|
|
int ser_buf_o_idx = 0; // receive buffer output index
|
|
|
|
// Receive buffer
|
|
char ser_buf[ SER_BUF_N ];
|
|
|
|
|
|
void uart_init(void)
|
|
{
|
|
UBRR0H = UBRRH_VALUE;
|
|
UBRR0L = UBRRL_VALUE;
|
|
|
|
#if USE_2X
|
|
UCSR0A |= _BV(U2X0);
|
|
#else
|
|
UCSR0A &= ~(_BV(U2X0));
|
|
#endif
|
|
|
|
UCSR0C = _BV(UCSZ01) | _BV(UCSZ00); // 8-bit data
|
|
UCSR0B = _BV(RXEN0) | _BV(TXEN0) | _BV(RXCIE0); // Enable RX and TX and RX intertupt enable
|
|
}
|
|
|
|
void uart_putchar(char c)
|
|
{
|
|
loop_until_bit_is_set(UCSR0A, UDRE0); // Wait until data register empty.
|
|
UDR0 = c;
|
|
}
|
|
|
|
void uart_putchar_alt(char c)
|
|
{
|
|
UDR0 = c;
|
|
loop_until_bit_is_set(UCSR0A, TXC0); // Wait until transmission ready.
|
|
}
|
|
|
|
char uart_getchar(void)
|
|
{
|
|
loop_until_bit_is_set(UCSR0A, RXC0); // Wait until data exists.
|
|
return UDR0;
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
|
|
#define I2C_LOCAL_ADDR (9)
|
|
#define I2C_REMOTE_ADDR (8)
|
|
|
|
#define I2C_BUF_N (16)
|
|
static uint8_t i2c_buf[ I2C_BUF_N ];
|
|
static volatile uint8_t i2c_buf_i_idx = 0;
|
|
static uint8_t i2c_buf_o_idx = 0;
|
|
|
|
static uint8_t last_char = '0';
|
|
|
|
void i2c_read_from( uint8_t i2c_addr, uint8_t dev_reg_addr, uint8_t read_byte_cnt )
|
|
{
|
|
uint8_t recv_char = '0';
|
|
const uint8_t kWaitFl = 1;
|
|
const uint8_t kSendStopFl = 1;
|
|
const uint8_t kNoSendStopFl = 0;
|
|
|
|
// Request to read from the client. Note that 'sendStop'==0.
|
|
// Use this call to tell the client what data should be sent
|
|
// during the subsequent twi_readFrom().
|
|
twi_writeTo(i2c_addr, &dev_reg_addr, 1, kWaitFl, kNoSendStopFl);
|
|
|
|
|
|
// Blocking waiting and wait to read the client's response.
|
|
for( uint8_t i=0; i<read_byte_cnt; ++i)
|
|
if( twi_readFrom(i2c_addr, &recv_char, 1, i==read_byte_cnt-1) )
|
|
uart_putchar(recv_char);
|
|
|
|
PORTB ^= _BV(PORTB5); // toggle LED
|
|
|
|
|
|
}
|
|
|
|
uint8_t i2c_xmit( uint8_t remote_addr, uint8_t* buf, uint8_t n, uint8_t sendStopFl)
|
|
{
|
|
return twi_writeTo(remote_addr, buf, n, 1, sendStopFl);
|
|
}
|
|
|
|
void i2c_init()
|
|
{
|
|
twi_setAddress(I2C_LOCAL_ADDR);
|
|
twi_init();
|
|
}
|
|
|
|
ISR(SERIAL_RX_ISR)
|
|
{
|
|
// receive the incoming byte
|
|
ser_buf[ ser_buf_i_idx ] = uart_getchar();
|
|
|
|
// advance the buffer input index
|
|
ser_buf_i_idx = (ser_buf_i_idx + 1) % SER_BUF_N;
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------------
|
|
|
|
|
|
int main (void)
|
|
{
|
|
enum { kWait_for_cmd, kWait_for_i2c, kWait_for_reg, kWait_for_cnt, kWait_for_value };
|
|
const uint8_t kWaitFl = 1;
|
|
const uint8_t kSendStopFl = 1;
|
|
const uint8_t kNoSendStopFl = 0;
|
|
const uint8_t data_bufN = 0xff;
|
|
|
|
char c;
|
|
uint8_t state = kWait_for_cmd; // parser state
|
|
char cmd; // 'r' or 'w'
|
|
uint8_t i2c_addr; // remote i2c address
|
|
uint8_t dev_reg_addr; // remote device register address
|
|
uint8_t op_byte_cnt; // count of data bytes to send or recv
|
|
uint8_t data_buf[ data_bufN ]; // hold data during parsing
|
|
uint8_t data_buf_idx = 0; // next avail slot in the data buffer
|
|
|
|
cli(); // mask all interupts
|
|
|
|
DDRB |= _BV(DDB5); // set led pin for output
|
|
|
|
uart_init(); // setup UART data format and baud rate
|
|
i2c_init();
|
|
sei(); // re-enable interrupts
|
|
|
|
uart_putchar('a');
|
|
|
|
for(;;)
|
|
{
|
|
|
|
// if there are bytes waiting in the serial buffer
|
|
if( ser_buf_o_idx != ser_buf_i_idx )
|
|
{
|
|
// get the waiting byte
|
|
c = ser_buf[ser_buf_o_idx];
|
|
|
|
// advance the buffer output index
|
|
ser_buf_o_idx = (ser_buf_o_idx+1) % SER_BUF_N;
|
|
|
|
// Serial Protocol
|
|
// 'r', i2c-addr, reg-idx, cnt, -> i2c_read_from()
|
|
// 'w', i2c-addr, reg-idx, cnt, value0, ... valueN -> i2c_xmit()
|
|
|
|
switch(state)
|
|
{
|
|
case kWait_for_cmd:
|
|
if(c == 'w' || c == 'r')
|
|
{
|
|
cmd = c;
|
|
state = kWait_for_i2c;
|
|
}
|
|
else
|
|
uart_putchar('E'); // indicate a protocol error
|
|
break;
|
|
|
|
case kWait_for_i2c:
|
|
i2c_addr = (uint8_t)c;
|
|
state = kWait_for_reg;
|
|
break;
|
|
|
|
case kWait_for_reg:
|
|
dev_reg_addr = (uint8_t)c;
|
|
state = kWait_for_cnt;
|
|
break;
|
|
|
|
case kWait_for_cnt:
|
|
op_byte_cnt = (uint8_t)c;
|
|
|
|
if( cmd == 'r' )
|
|
{
|
|
i2c_read_from( i2c_addr, dev_reg_addr, op_byte_cnt );
|
|
state = kWait_for_cmd;
|
|
}
|
|
else
|
|
{
|
|
// TODO: handle case where there are no data bytes (only e.g. note-off)
|
|
state = kWait_for_value;
|
|
data_buf[0] = dev_reg_addr; // make 'dev_reg_addr' the first data value to write
|
|
data_buf_idx = 1; //
|
|
op_byte_cnt += 1; // incr op_byte_cnt to account for 'dev_reg_addr' as first byte
|
|
}
|
|
break;
|
|
|
|
case kWait_for_value:
|
|
if( data_buf_idx >= data_bufN )
|
|
{
|
|
uart_putchar('F'); // indicate a buffer overrun
|
|
state = kWait_for_cmd;
|
|
}
|
|
else
|
|
{
|
|
data_buf[ data_buf_idx++ ] = c;
|
|
|
|
if(data_buf_idx == op_byte_cnt )
|
|
{
|
|
/*
|
|
uint8_t ii;
|
|
for(ii=0; ii<op_byte_cnt; ++ii)
|
|
uart_putchar( data_buf[ii] );
|
|
*/
|
|
|
|
i2c_xmit( i2c_addr, data_buf, op_byte_cnt, kSendStopFl);
|
|
state = kWait_for_cmd;
|
|
}
|
|
}
|
|
break;
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|