diff --git a/china_io-2019/avr/Makefile b/china_io-2019/avr/Makefile index 2e6b2c84a3343360588bd7f6531e53583c49a6de..f435c2d6ffdee0e37f4b91bad77c5b797c68f20d 100644 --- a/china_io-2019/avr/Makefile +++ b/china_io-2019/avr/Makefile @@ -7,7 +7,7 @@ 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/avr/serialio i2c_master +china-io.H=../../lib/avr/serialio i2c_master ads869x china-io-test.ARCH=avr china-io-test.CHIP=atmega32 diff --git a/china_io-2019/avr/ads869x.h b/china_io-2019/avr/ads869x.h new file mode 100644 index 0000000000000000000000000000000000000000..1ff1a0e17d0db2561bf566d36f12b46070ffc6f2 --- /dev/null +++ b/china_io-2019/avr/ads869x.h @@ -0,0 +1,48 @@ +#ifndef __ADS869X_H__ +#define __ADS869X_H__ + +#define ADS869X_AUTO_SEQ_EN 0x01 +#define ADS869X_FeatureSelect 0x03 + + +#define ADS869X_REG_W(REG_ADDR, VALUE) ((REG_ADDR<<1)|0x01),(VALUE) +#define ADS869X_REG_R(REG_ADDR, VALUE) ((REG_ADDR<<1)),(VALUE) + +#define ADS869X_W(W_DATA) DO_ADS869X_W_R(sizeof(W_DATA), W_DATA, \ + 0, NULL) +#define ADS869X_R(R_DATA) DO_ADS869X_W_R(0, NULL, \ + sizeof(R_DATA), R_DATA) +#define ADS869X_W_R(W_DATA, R_DATA) DO_ADS869X_W_R(sizeof(W_DATA), W_DATA, \ + sizeof(R_DATA), R_DATA) + +static uint8_t DO_ADS869X_W_R( + uint8_t w_length, + const __flash uint8_t *w_data, + uint8_t r_length, + uint8_t *r_data) +{ + uint8_t last_byte; + + /* Always setup SPI in case others use SPI */ + DDR_SPI |= (1<<DD_MOSI) | (1<<DD_SCK) | (1<<DD_SS); + PORT_SPI |= (1<<DD_MISO); // Pull up + SPCR = (SPCR & (1<<SPIE)) | (1<<SPE) | (1<<MSTR) | (1<<CPHA); + SPSR = (1<<SPI2X); + + PORT_SPI &= ~(1<<DD_SS); // Chip select + for (uint8_t i = 0 ; i < w_length ; i++) { + SPDR = w_data[i]; + while ((SPSR & (1<<SPIF)) == 0) {} + last_byte = SPDR; // Clear SPIF flag + } + for (uint8_t i = 0 ; i < r_length ; i++) { + SPDR = 0; + while ((SPSR & (1<<SPIF)) == 0) {} + last_byte = SPDR; // Clear SPIF flag + r_data[i] = last_byte; + } + PORT_SPI |= (1<<DD_SS); // Chip deselect + return last_byte; +} + +#endif diff --git a/china_io-2019/avr/china-io-test.c b/china_io-2019/avr/china-io-test.c index 3373cdc89165b7f61003153f903a86eeb9309399..6c90da78a6c04131c60d05e50bdc8865a60819a2 100644 --- a/china_io-2019/avr/china-io-test.c +++ b/china_io-2019/avr/china-io-test.c @@ -110,6 +110,35 @@ static const __flash struct i2c_transcation write_all[] = { I2C_END() }; + +static volatile struct dac8574_check { + struct dac8574_check_chan { + uint8_t no_pdo[2]; + uint8_t pdo[3]; + } chan[4]; +} dac8574_check; + +static const __flash struct i2c_transcation read_all[] = { + I2C_WRITE_1(DAC8574ADDR, 0x10), + I2C_READ(DAC8574ADDR, 2, dac8574_check.chan[0].no_pdo), + I2C_WRITE_1(DAC8574ADDR, 0x11), + I2C_READ(DAC8574ADDR, 3, dac8574_check.chan[0].pdo), + I2C_WRITE_1(DAC8574ADDR, 0x12), + I2C_READ(DAC8574ADDR, 2, dac8574_check.chan[1].no_pdo), + I2C_WRITE_1(DAC8574ADDR, 0x13), + I2C_READ(DAC8574ADDR, 3, dac8574_check.chan[1].pdo), + I2C_WRITE_1(DAC8574ADDR, 0x14), + I2C_READ(DAC8574ADDR, 2, dac8574_check.chan[2].no_pdo), + I2C_WRITE_1(DAC8574ADDR, 0x15), + I2C_READ(DAC8574ADDR, 3, dac8574_check.chan[2].pdo), + I2C_WRITE_1(DAC8574ADDR, 0x16), + I2C_READ(DAC8574ADDR, 2, dac8574_check.chan[3].no_pdo), + I2C_WRITE_1(DAC8574ADDR, 0x17), + I2C_READ(DAC8574ADDR, 3, dac8574_check.chan[3].pdo), + I2C_CHAIN(write_all), + I2C_END() +}; + static volatile int32_t ADS8694_value[4] = { 0, 0, 0, 0}; SIGNAL(TIMER1_COMPA_vect) @@ -210,12 +239,23 @@ int main() } putstr("\r"); if (i2c_idle()) { + putstr("\r\n"); + for (uint8_t i = 0 ; i < 4 ; i++) { + for (int j = 0 ; j < 2 ; j++) { + puthex(dac8574_check.chan[i].no_pdo[j]); + } + putch(' '); + for (int j = 0 ; j < 3 ; j++) { + puthex(dac8574_check.chan[i].pdo[j]); + } + putch(','); + } 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); + i2c_start(read_all); sei(); putstr("\r\n"); } diff --git a/china_io-2019/avr/china-io.c b/china_io-2019/avr/china-io.c index ac414e6bf68e27cbae7185e47d7856f694d44df5..2e90011f2dbd5b68f9886e3b2173d45448c29873 100644 --- a/china_io-2019/avr/china-io.c +++ b/china_io-2019/avr/china-io.c @@ -5,26 +5,13 @@ #include "i2c_master.h" #include "serialio.h" -/* - * Serial I/O assignments: - * - * AI 0 -- Analog In 0 - * AI 1 -- Analog In 1 - * AI 2 -- Analog In 2 - * AI 3 -- Analog In 3 - * - * AO 0 -- Analog Out 0 - * AO 1 -- Analog Out 1 - * - */ - /* * Used I/O pins * - * PB4 SPI SS / ADS8694 SPI CS - * PB5 Programming MOSI / ADS8694 SPI SDI - * PB6 Programming MISO / ADS8694 SPI SDO - * PB7 Programming SCK / ADS8694 SPI SCLK + * PB4 SPI SS / ADS869X SPI CS + * PB5 Programming MOSI / ADS869X SPI SDI + * PB6 Programming MISO / ADS869X SPI SDO + * PB7 Programming SCK / ADS869X SPI SCLK * * PC0 I2C SCL * PC1 I2C SDA @@ -34,6 +21,7 @@ * */ +/* Needed for ads869x.h definitions */ #define DDR_SPI DDRB #define DD_MISO PB6 #define DD_MOSI PB5 @@ -41,6 +29,22 @@ #define DD_SS PB4 #define PORT_SPI PORTB +#include "ads869x.h" + +/* + * Serial I/O assignments: + * + * AI 0 -- Analog In 0 + * AI 1 -- Analog In 1 + * AI 2 -- Analog In 2 + * AI 3 -- Analog In 3 + * + * AO 0 -- Analog Out 0 + * AO 1 -- Analog Out 1 + * + */ + + static volatile uint8_t serial_readchannels; static volatile uint8_t serial_writechannels; static volatile uint8_t serial_readconfig; @@ -62,26 +66,6 @@ static volatile struct calibration { } channel[2]; } calibration; -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 */ @@ -174,33 +158,28 @@ SIGNAL(USART_RXC_vect) } } +static volatile int16_t timeout_timer = 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 + timeout_timer++; + + static const __flash uint8_t NOP[] = { 0x00, 0x00 }; + uint8_t data[] = { 0x00, 0x00, 0x00 }; + + // Write NOP and read back 3 bytes (18 bits + channel #) + ADS869X_W_R(NOP, data); union { uint32_t i; - unsigned char b[4]; + uint8_t 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; + v.b[2] = data[0]; + v.b[1] = data[1]; + v.b[0] = data[2]; + chan = (v.i>>2) & 0x03; adc.value[chan] = (v.i>>6); } @@ -213,8 +192,60 @@ static uint32_t conf_millivolt(int32_t value) } } +static uint8_t dac8574_check() +{ + static struct dac8574_check { + struct { + uint8_t no_pdo[2]; + uint8_t pdo[3]; + } chan[4]; + } check; + + static const __flash struct i2c_transcation read_all[] = { + I2C_WRITE_1(DAC8574ADDR, 0x10), + I2C_READ(DAC8574ADDR, 2, check.chan[0].no_pdo), + I2C_WRITE_1(DAC8574ADDR, 0x11), + I2C_READ(DAC8574ADDR, 3, check.chan[0].pdo), + I2C_WRITE_1(DAC8574ADDR, 0x12), + I2C_READ(DAC8574ADDR, 2, check.chan[1].no_pdo), + I2C_WRITE_1(DAC8574ADDR, 0x13), + I2C_READ(DAC8574ADDR, 3, check.chan[1].pdo), + I2C_WRITE_1(DAC8574ADDR, 0x14), + I2C_READ(DAC8574ADDR, 2, check.chan[2].no_pdo), + I2C_WRITE_1(DAC8574ADDR, 0x15), + I2C_READ(DAC8574ADDR, 3, check.chan[2].pdo), + I2C_WRITE_1(DAC8574ADDR, 0x16), + I2C_READ(DAC8574ADDR, 2, check.chan[3].no_pdo), + I2C_WRITE_1(DAC8574ADDR, 0x17), + I2C_READ(DAC8574ADDR, 3, check.chan[3].pdo), + I2C_CHAIN(write_all), + I2C_END() + }; + + // Read back channel data + timeout_timer = 0; + while (!i2c_idle() && timeout_timer < 14000) { } + if (!i2c_idle()) { return 0; } + i2c_start(read_all); + + // Check that channel data with and without power down are equal + timeout_timer = 0; + while (!i2c_idle() && timeout_timer < 14000) { } + if (!i2c_idle()) { return 0; } + + for (int i = 0 ; i < 4 ; i++) { + if ((check.chan[i].pdo[0] & 0x3f) != 0x3f) { return 0; } + if (check.chan[i].pdo[1] != check.chan[i].no_pdo[0]) { return 0; } + if (check.chan[i].pdo[2] != check.chan[i].no_pdo[1]) { return 0; } + } + return 1; +} + int main() { + static uint8_t in_channel_count = 0; + static uint8_t out_channel_count = 0; + eeprom_read_block((void*)&calibration, 0, sizeof(calibration)); if (calibration.not_valid) { calibration.not_valid = 0; @@ -243,15 +274,41 @@ int main() TWBR = 0x0a; // Max allowed master speed 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); + /* Check and setup ADS869X (SPI setup is done in ADS869X_W) */ + static const __flash uint8_t RST[] = + { 0x85, 0x00, 0x00 }; + static const __flash uint8_t SEQUENCE_READ[] = + { ADS869X_REG_R(ADS869X_AUTO_SEQ_EN, 0x01), 0x00 }; + static const __flash uint8_t SEQUENCE_0[] = + { ADS869X_REG_W(ADS869X_AUTO_SEQ_EN, 0x00), 0x00 }; + static const __flash uint8_t SEQUENCE_4[] = + { ADS869X_REG_W(ADS869X_AUTO_SEQ_EN, 0x0f), 0x00 }; + static const __flash uint8_t FEATURES[] = + { ADS869X_REG_W(ADS869X_FeatureSelect, 0x03), 0x00 }; + static const __flash uint8_t AUTO_RST[] = + { 0xa0, 0x00, 0x00, 0x00, 0x00 }; + uint8_t seq_bits; + ADS869X_W(RST); + seq_bits = ADS869X_W(SEQUENCE_READ); + if (seq_bits != 0xff) { + in_channel_count = 0; + } else { + ADS869X_W(SEQUENCE_0); + seq_bits = ADS869X_W(SEQUENCE_READ); + if (seq_bits == 0xf0 || seq_bits == 0x00) { + // ADS8694 or ADS8698 + ADS869X_W(SEQUENCE_4); + in_channel_count = 4; + } else { + in_channel_count = 0; + } + seq_bits = ADS869X_W(SEQUENCE_READ); + if (seq_bits != 0x0f) { + in_channel_count = 0; + } + } + ADS869X_W(FEATURES); + ADS869X_W(AUTO_RST); /* Setup serialio communication */ serialio_init(); @@ -262,7 +319,11 @@ int main() dac.value[0] = 0x8000; dac.value[1] = 0x8000; - sei(); // Global interrupt enable + sei(); // Global interrupt enable + + if (dac8574_check()) { + out_channel_count = 2; + } unsigned char readchannels = 0; while (1) { @@ -303,26 +364,18 @@ int main() } if (config) { - CONF_ANALOG_IN(0, CONF_RESOLUTION(18)); // Analog In 0 - CONF_ANALOG_IN(0, CONF_MIN(CONF_NEGATIVE_MILLIVOLT(10000))); - CONF_ANALOG_IN(0, CONF_MAX(CONF_POSITIVE_MILLIVOLT(10000))); - CONF_ANALOG_IN(1, CONF_RESOLUTION(18)); // Analog In 1 - CONF_ANALOG_IN(1, CONF_MIN(CONF_NEGATIVE_MILLIVOLT(10000))); - CONF_ANALOG_IN(1, CONF_MAX(CONF_POSITIVE_MILLIVOLT(10000))); - CONF_ANALOG_IN(2, CONF_RESOLUTION(18)); // Analog In 2 - CONF_ANALOG_IN(2, CONF_MIN(CONF_NEGATIVE_MILLIVOLT(10000))); - CONF_ANALOG_IN(2, CONF_MAX(CONF_POSITIVE_MILLIVOLT(10000))); - CONF_ANALOG_IN(3, CONF_RESOLUTION(18)); // Analog In 3 - CONF_ANALOG_IN(3, CONF_MIN(CONF_NEGATIVE_MILLIVOLT(10000))); - CONF_ANALOG_IN(3, CONF_MAX(CONF_POSITIVE_MILLIVOLT(10000))); - - CONF_ANALOG_OUT(0, CONF_RESOLUTION(16)); // Analog Out 0 - CONF_ANALOG_OUT(0, CONF_MIN(conf_millivolt(calibration.channel[0].min))); - CONF_ANALOG_OUT(0, CONF_MAX(conf_millivolt(calibration.channel[0].max))); - CONF_ANALOG_OUT(1, CONF_RESOLUTION(16)); // Analog Out 1 - CONF_ANALOG_OUT(1, CONF_MIN(conf_millivolt(calibration.channel[1].min))); - CONF_ANALOG_OUT(1, CONF_MAX(conf_millivolt(calibration.channel[1].max))); - + for (int i = 0 ; i < in_channel_count ; i++) { + CONF_ANALOG_IN(i, CONF_RESOLUTION(18)); // Analog In 0 + CONF_ANALOG_IN(i, CONF_MIN(CONF_NEGATIVE_MILLIVOLT(10000))); + CONF_ANALOG_IN(i, CONF_MAX(CONF_POSITIVE_MILLIVOLT(10000))); + } + for (int i = 0 ; i < out_channel_count ; i++) { + CONF_ANALOG_OUT(i, CONF_RESOLUTION(16)); + CONF_ANALOG_OUT(i, CONF_MIN(conf_millivolt( + calibration.channel[i].min))); + CONF_ANALOG_OUT(i, CONF_MAX(conf_millivolt( + calibration.channel[i].max))); + } CONF_END(); } }