Commit 06c4b54e authored by Anders Blomdell's avatar Anders Blomdell
Browse files

Simple text based test of China I/O basic functionality

parent 0334488f
TARGETS=china-io china-io-test
china-io.ARCH=avr
china-io.CHIP=atmega32
# 14.7456 MHz crystal, brown out
china-io.FUSE=--wr_fuse_l=0x1f --wr_fuse_h=0xd9 --wr_fuse_e=0xff
china-io.FUSE_L=0x1f
china-io.FUSE_H=0xd9
china-io.C=china-io
china-io.H=../lib/serialio i2c_master
china-io-test.ARCH=avr
china-io-test.CHIP=atmega32
# 14.7456 MHz crystal, brown out
china-io-test.FUSE=--wr_fuse_l=0x1f --wr_fuse_h=0xd9 --wr_fuse_e=0xff
china-io-test.FUSE_L=0x1f
china-io-test.FUSE_H=0xd9
china-io-test.C=china-io-test
china-io-test.H=i2c_master
include ../../lib/avr/Makefile.common
#include <avr/interrupt.h>
#include <avr/io.h>
#include <inttypes.h>
#include <stdio.h>
#include <util/twi.h>
#include "i2c_master.h"
#define DDR_SPI DDRB
#define DD_MISO PB6
#define DD_MOSI PB5
#define DD_SCK PB7
#define DD_SS PB4
#define PORT_SPI PORTB
static void putch(unsigned char ch)
{
while ((UCSRA & 0x20) == 0) {};
UDR =ch;
}
static void putonehex(char i) {
if (i < 10) {
putch(i + '0');
} else if (i < 16) {
putch(i - 10 + 'a');
}
}
static void puthex(char value) {
putonehex((value >> 4) & 0x0f);
putonehex(value & 0x0f);
}
void putstr(char *message)
{
for (int i = 0 ; message[i] ; i++) {
putch(message[i]);
}
}
void putint32(int32_t value)
{
char buf[20];
snprintf(buf, sizeof(buf), "%8ld", value);
putstr(buf);
}
void putint16(int16_t value)
{
char buf[20];
snprintf(buf, sizeof(buf), "%8d", value);
putstr(buf);
}
static uint8_t ADS8694_W(
uint8_t length,
const __flash uint8_t *data)
{
DDR_SPI |= (1<<DD_MOSI) | (1<<DD_SCK) | (1<<DD_SS);
SPCR &= (1<<SPIE);
SPCR = (1<<SPE) | (1<<MSTR) | (1<<CPHA);
SPSR = (1<<SPI2X);
PORT_SPI |= (1<<DD_MISO); // Pull up
PORT_SPI &= ~(1<<DD_SS);
for (uint8_t i = 0 ; i < length ; i++) {
SPDR = data[i];
while ((SPSR & (1<<SPIF)) == 0) {}
}
uint8_t dummy = SPDR; // Clear SPIF flag
PORT_SPI |= (1<<DD_SS);
return dummy;
}
/*
* DAC8574 4-channel D/A convert
*/
#define DAC8574ADDR 0x4c
static volatile struct dac8574 {
union dac8574_channel {
uint8_t bytes[3];
struct {
uint8_t control;
uint8_t data[2];
} reg;
} a, b, c, d;
} dac8574 = {
.a={ .reg={ .control=0x10 } },
.b={ .reg={ .control=0x12 } },
.c={ .reg={ .control=0x14 } },
.d={ .reg={ .control=0x16 } },
};
static void dac8574_set_chan(
volatile union dac8574_channel *channel,
int value)
{
channel->reg.data[0] = (value & 0xff00) >> 8;
channel->reg.data[1] = (value & 0x00ff);
}
static const __flash struct i2c_transcation write_all[] = {
I2C_WRITE_RAM(DAC8574ADDR, 3, dac8574.a.bytes),
I2C_WRITE_RAM(DAC8574ADDR, 3, dac8574.b.bytes),
I2C_WRITE_RAM(DAC8574ADDR, 3, dac8574.c.bytes),
I2C_WRITE_RAM(DAC8574ADDR, 3, dac8574.d.bytes),
I2C_END()
};
static volatile int32_t ADS8694_value[4] = { 0, 0, 0, 0};
SIGNAL(TIMER1_COMPA_vect)
{
unsigned char data[5];
SPCR = (1<<SPE) | (1<<MSTR) | (1<< CPHA);
PORT_SPI &= ~(1<<DD_SS); // Chip select
SPDR = 0x00; // NOP
for (int i = 0 ; i < 5 ; i++) {
while ((SPSR & 0x80) == 0) { }
data[i] = SPDR;
if (i < 4) {
// More to read
SPDR = 0;
}
}
PORT_SPI |= (1<<DD_SS); // Chip deselect
union {
int32_t i;
unsigned char b[4];
} v;
uint8_t chan;
v.i = 0;
v.b[2] = data[2];
v.b[1] = data[3];
v.b[0] = data[4];
chan = (v.i>>2) & 0xf;
ADS8694_value[chan] = (v.i>>6) - 0x20000;
}
static int16_t v = 0;
SIGNAL(USART_RXC_vect)
{
char ch = UDR;
puthex(ch);
putstr("RX Status:");
puthex(TWSR);
puthex(TWCR);
if (ch == '+') {
v += 1024;
} else if (ch == '-') {
v -= 1024;
}
putstr("\r\n");
}
int main()
{
/* Setup serial port */
UCSRA = 0x00; // USART:
UCSRB = 0x98; // USART: RxIntEnable|RxEnable|TxEnable
UCSRC = 0x86; // USART: 8bit, no parity
UBRRH = 0; // USART: 115200 @ 14.7456MHz
UBRRL = 7; // USART: 115200 @ 14.7456MHz
/* Setup timer1 */
TCCR1A = 0x00; // Normal port mode (no output)
TCCR1B = 0x09; // Clock / 1
OCR1A = 0x3fff;
TIMSK = 0x10; // Enable Timer1 compare A interrupts
/* Setup I2C (TWI), used by DAC8574 */
TWBR = 0x10;
TWSR = 0x00;
/* Setup DAC8574 (SPI setup is done in ADS8694_W) */
static const __flash uint8_t RST[] = { 0x85, 0x00, 0x00 };
static const __flash uint8_t SEQUENCE[] = { (0x01<<1)|0x01, 0x0f, 0x00 };
static const __flash uint8_t FEATURES[] = { (0x03<<1)|0x01, 0x03, 0x00 };
static const __flash uint8_t AUTO_RST[] = { 0xa0, 0x00, 0x00, 0x00, 0x00 };
ADS8694_W(3, RST);
ADS8694_W(3, SEQUENCE);
ADS8694_W(3, FEATURES);
ADS8694_W(5, AUTO_RST);
sei(); // Global interrupt enable
putstr("\r\n\r\n");
i2c_start(write_all);
while (1) {
struct dac8574 tmp;
dac8574_set_chan(&tmp.a, 0x8000);
dac8574_set_chan(&tmp.b, 0x8000);
dac8574_set_chan(&tmp.c, v+0x0000);
dac8574_set_chan(&tmp.d, v+0x8000);
// v = (v + 1024) & 0xffff;
putstr("i2c status=");
putint16(i2c_ctx.status);
putstr(" v=");
putint16(v);
putint16(v+0x8000);
for (int i = 0 ; i < 4 ; i++) {
putint32(ADS8694_value[i]);
}
putstr("\r");
if (i2c_idle()) {
cli();
*dac8574.a.reg.data = *tmp.a.reg.data;
*dac8574.b.reg.data = *tmp.b.reg.data;
*dac8574.c.reg.data = *tmp.c.reg.data;
*dac8574.d.reg.data = *tmp.d.reg.data;
i2c_start(write_all);
sei();
putstr("\r\n");
}
}
}
#ifndef __I2C_MASTER_H__
#define __I2C_MASTER_H__
#include <util/atomic.h>
enum i2c_op {
I2C_R_OP=0x01,
I2C_W_FLASH_OP,
I2C_W_RAM_OP,
I2C_W_1_OP,
I2C_W_2_OP,
I2C_W_3_OP,
I2C_CALL_OP,
I2C_CHAIN_OP,
I2C_END_OP,
};
struct i2c_transcation {
enum i2c_op op:8;
union {
struct i2c_read {
unsigned char address;
unsigned char count;
void volatile *data;
} read;
struct i2c_write_flash {
unsigned char address;
unsigned char count;
const volatile unsigned __flash char *data;
} write_flash;
struct i2c_write_ram {
unsigned char address;
unsigned char count;
const volatile unsigned char *data;
} write_ram;
struct i2c_write {
unsigned char address;
const volatile unsigned char data[3];
} write;
struct i2c_callback {
unsigned char (*function)(const volatile void *context);
const volatile void *context;
} callback;
struct i2c_chain {
const __flash struct i2c_transcation *transaction;
} chain;
};
};
static volatile struct i2c_ctx {
const __flash struct i2c_transcation *transaction;
// unsigned char length;
unsigned char pos;
char status;
} i2c_ctx;
#define I2C_READ(ADDR, COUNT, DATA) \
{.op=I2C_R_OP,{.read={.address=ADDR,.count=COUNT, .data=DATA}}}
#define I2C_WRITE_FLASH(ADDR, COUNT, DATA) \
{.op=I2C_W_FLASH_OP,{.write_flash={.address=ADDR,.count=COUNT,.data=DATA}}}
#define I2C_WRITE_RAM(ADDR, COUNT, DATA) \
{.op=I2C_W_RAM_OP,{.write_ram={.address=ADDR,.count=COUNT,.data=DATA}}}
#define I2C_WRITE_1(ADDR, DATA0) \
{.op=I2C_W_1_OP,{.write={.address=ADDR, \
.data[0]=DATA0}}}
#define I2C_WRITE_2(ADDR, DATA0, DATA1) \
{.op=I2C_W_2_OP,{.write={.address=ADDR, \
.data[0]=DATA0, \
.data[1]=DATA1}}}
#define I2C_WRITE_3(ADDR, DATA0, DATA1, DATA2) \
{.op=I2C_W_3_OP,{.write={.address=ADDR, \
.data[0]=DATA0, \
.data[1]=DATA1, \
.data[2]=DATA2}}}
#define I2C_CALLBACK(FUNC, CONTEXT) \
{.op=I2C_CALL_OP,{.callback={.function=FUNC,.context=CONTEXT}}}
#define I2C_CHAIN(TRANSACTION) \
{.op=I2C_CHAIN_OP,{.chain={.transaction=TRANSACTION}}}
#define I2C_END() \
{.op=I2C_END_OP,{}}
static unsigned char i2c_chain(
const __flash struct i2c_transcation *transaction)
{
i2c_ctx.transaction = transaction;
i2c_ctx.pos = 0;
i2c_ctx.status = 0;
return 0;
}
static unsigned char i2c_start(
const __flash struct i2c_transcation *transaction)
{
i2c_chain(transaction);
TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN) | (1<<TWIE);
return 0;
}
/* Return number of bytes in current transaction */
static unsigned char i2c_count()
{
struct i2c_transcation tr = i2c_ctx.transaction[i2c_ctx.pos];
switch (tr.op) {
case I2C_CALL_OP: return 0; // ERROR
case I2C_R_OP: return tr.read.count;
case I2C_W_FLASH_OP: return tr.write_flash.count;
case I2C_W_RAM_OP: return tr.write_ram.count;
case I2C_W_1_OP: return 1;
case I2C_W_2_OP: return 2;
case I2C_W_3_OP: return 3;
case I2C_CHAIN_OP: return 0; // ERROR
case I2C_END_OP: return 0; // ERROR
}
return 0;
}
static unsigned char i2c_get_data(unsigned char index)
{
struct i2c_transcation tr = i2c_ctx.transaction[i2c_ctx.pos];
switch (tr.op) {
case I2C_CALL_OP: return 0; // ERROR
case I2C_R_OP: return 0; // ERROR
case I2C_W_FLASH_OP: return tr.write_flash.data[index];
case I2C_W_RAM_OP: return tr.write_ram.data[index];
case I2C_W_1_OP: return tr.write.data[index];
case I2C_W_2_OP: return tr.write.data[index];
case I2C_W_3_OP: return tr.write.data[index];
case I2C_CHAIN_OP: return 0; // ERROR
case I2C_END_OP: return 0; // ERROR
}
return 0;
}
static void i2c_put_data(unsigned char index, unsigned char data)
{
struct i2c_transcation tr = i2c_ctx.transaction[i2c_ctx.pos];
switch (tr.op) {
case I2C_CALL_OP: break;
case I2C_R_OP: {
if (index < tr.read.count) {
((unsigned char *)tr.read.data)[index] = data;
}
} break;
case I2C_W_FLASH_OP: break;
case I2C_W_RAM_OP: break;
case I2C_W_1_OP: break;
case I2C_W_2_OP: break;
case I2C_W_3_OP: break;
case I2C_CHAIN_OP: break;
case I2C_END_OP: break;
}
}
static char i2c_callback()
{
while (i2c_ctx.status == 0) {
enum i2c_op op = i2c_ctx.transaction[i2c_ctx.pos].op;
if (op == I2C_END_OP) {
break;
} else if (i2c_ctx.transaction[i2c_ctx.pos].op == I2C_CALL_OP) {
struct i2c_callback cb = i2c_ctx.transaction[i2c_ctx.pos].callback;
i2c_ctx.status = cb.function(cb.context);
if (i2c_ctx.status == 0) {
i2c_ctx.pos++;
continue;
}
} else if (i2c_ctx.transaction[i2c_ctx.pos].op == I2C_CHAIN_OP) {
i2c_chain(i2c_ctx.transaction[i2c_ctx.pos].chain.transaction);
continue;
} else {
break;
}
}
return i2c_ctx.status;
}
static char i2c_next()
{
#if 0
putstr("POS: ");
puthex(i2c_ctx.pos);
putstr(" OP: ");
puthex( i2c_ctx.transaction[i2c_ctx.pos].op);
putstr("\r\n");
#endif
if (i2c_ctx.status != 0) {
return i2c_ctx.status;
}
if (i2c_ctx.transaction[i2c_ctx.pos].op != I2C_END_OP) {
i2c_ctx.pos++;
i2c_callback();
}
if (i2c_ctx.status != 0) {
return i2c_ctx.status;
}
return i2c_ctx.transaction[i2c_ctx.pos].op != I2C_END_OP;
}
static char i2c_more(unsigned char index)
{
if (i2c_ctx.status != 0) {
return i2c_ctx.status;
}
return index < i2c_count();
}
static char i2c_idle()
{
char result = 1;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
if (i2c_ctx.transaction &&
i2c_ctx.transaction[i2c_ctx.pos].op != I2C_END_OP) {
result = 0;
}
}
return result;
}
SIGNAL(TWI_vect)
{
// unsigned char twsr = TWSR;
unsigned char twcr = 0x00;
static unsigned char data_pos = 0x00;
// Consume callback operations
if (i2c_callback() != 0) {
// putstr("Error\r\n");
}
switch (TW_STATUS) {
case TW_START:
case TW_REP_START:
{
switch (i2c_ctx.transaction[i2c_ctx.pos].op) {
case I2C_CALL_OP:
case I2C_CHAIN_OP:
case I2C_END_OP:
{
// ERROR
} break;
case I2C_R_OP: {
// send SLA+R
TWDR = (i2c_ctx.transaction[i2c_ctx.pos].read.address << 1) | 1;
} break;
case I2C_W_FLASH_OP: {
// send SLA+W
TWDR = (i2c_ctx.transaction[i2c_ctx.pos].write_flash.address << 1);
} break;
case I2C_W_RAM_OP: {
// send SLA+W
TWDR = (i2c_ctx.transaction[i2c_ctx.pos].write_ram.address << 1);
} break;
case I2C_W_1_OP:
case I2C_W_2_OP:
case I2C_W_3_OP: {
// send SLA+W
TWDR = (i2c_ctx.transaction[i2c_ctx.pos].write.address << 1);
} break;
}
twcr = 0;
data_pos = 0;
} break;
case TW_MT_SLA_ACK:
case TW_MT_DATA_ACK:
{
// Send next byte
if (i2c_more(data_pos)) {
TWDR = i2c_get_data(data_pos);
twcr = 0;
data_pos++;
} else {
if (i2c_next()) {
// Repeated start
twcr = (1 << TWSTA);
} else {
// Done
twcr = (1 << TWSTO);
}
}
} break;
case TW_MR_SLA_ACK:
{
if (i2c_more(data_pos + 1)) {
// Receive byte, send ACK
twcr = (1<<TWEA);
} else {
// Receive last byte, send NOT ACK
twcr = 0;
}
} break;
case TW_MR_DATA_ACK:
case TW_MR_DATA_NACK:
{
// Data received:
i2c_put_data(data_pos, TWDR);
data_pos++;
if (i2c_more(data_pos+1)) {
// Receive byte, send ACK
twcr = (1<<TWEA);
} else if (i2c_more(data_pos)) {
// Receive last byte, send NOT ACK
twcr = 0;
} else if (i2c_next()) {
// Send repeated START
twcr = (1 << TWSTA);
} else {
// Send STOP
twcr = (1<<TWSTO);
}
} break;
default: {
i2c_ctx.status = TW_STATUS | 0x01;
twcr = (1<<TWSTO);
// Done
#if 0
putstr("TWSR: ");
puthex(TW_STATUS);
putstr(" TWCR: ");
puthex(twcr);
putstr("\r\n");
#endif
} break;
}
#if 0
putstr("TWSR: ");
puthex(TW_STATUS);
putstr(" TWCR: ");
puthex(twcr);
putstr("\r\n");
#endif
TWCR = twcr | (1<<TWINT) | (1<<TWEN) |(1<<TWIE);
}
#endif
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