diff --git a/china_io-2019/avr/Makefile b/china_io-2019/avr/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b658ee4533bfaf5f61963942e28951740aa1a367 --- /dev/null +++ b/china_io-2019/avr/Makefile @@ -0,0 +1,21 @@ +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 diff --git a/china_io-2019/avr/china-io-test.c b/china_io-2019/avr/china-io-test.c new file mode 100644 index 0000000000000000000000000000000000000000..bb0f11424c49f535490cf41fe7b06ac5efbf876a --- /dev/null +++ b/china_io-2019/avr/china-io-test.c @@ -0,0 +1,224 @@ +#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"); + } + } +} diff --git a/china_io-2019/avr/i2c_master.h b/china_io-2019/avr/i2c_master.h new file mode 100644 index 0000000000000000000000000000000000000000..51ee51d54880779d7e227fc2b4230970b6abbe3b --- /dev/null +++ b/china_io-2019/avr/i2c_master.h @@ -0,0 +1,335 @@ +#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