From 98fd52f7234dc86c27b95beb259c7c496aa7f9d7 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Tue, 15 Mar 2011 13:41:59 +0000 Subject: [PATCH] example: Added i2ctest This example need some polish. It simulates an AVR with an i2c eeprom of 1024 bytes. The eeprom is "generic" an will go to the parts/ directory when this is fully working. Right now write the eeprom works, but there is a stray byte somewhere; the bug /could/ be in the atmel code tho, wich I had to fix and tweak. Signed-off-by: Michel Pollet --- examples/board_i2ctest/Makefile | 50 +++++ examples/board_i2ctest/atmega48_i2ctest.c | 85 ++++++++ .../{twi_master.c => avr_twi_master.c} | 33 ++- .../{twi_master.h => avr_twi_master.h} | 9 +- examples/board_i2ctest/i2ctest.c | 195 ++++++++++++++++++ 5 files changed, 350 insertions(+), 22 deletions(-) create mode 100644 examples/board_i2ctest/atmega48_i2ctest.c rename examples/board_i2ctest/{twi_master.c => avr_twi_master.c} (91%) rename examples/board_i2ctest/{twi_master.h => avr_twi_master.h} (92%) create mode 100644 examples/board_i2ctest/i2ctest.c diff --git a/examples/board_i2ctest/Makefile b/examples/board_i2ctest/Makefile index e69de29..ed686fe 100644 --- a/examples/board_i2ctest/Makefile +++ b/examples/board_i2ctest/Makefile @@ -0,0 +1,50 @@ +# +# Copyright 2008-2011 Michel Pollet +# +# This file is part of simavr. +# +# simavr is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# simavr is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with simavr. If not, see . + +target= i2ctest +firm_src = ${wildcard at*${board}.c} +firmware = ${firm_src:.c=.axf} +simavr = ../../ + +SHELL = /bin/bash + +IPATH = . +IPATH += ../parts +IPATH += ${simavr}/include +IPATH += ${simavr}/simavr/sim + +VPATH = . +VPATH += ../parts + + +all: obj atmega48_${target}.axf ${target} + +include ${simavr}/Makefile.common + +atmega48_${target}.axf: atmega48_${target}.c + +board = ${OBJ}/${target}.elf + +${board} : ${OBJ}/${target}.o +${board} : ${simavr}/simavr/${OBJ}/libsimavr.a + +${target}: ${board} + @echo $@ done + +clean: clean-${OBJ} + rm -rf *.hex *.a *.axf ${target} *.vcd .*.swo .*.swp .*.swm .*.swn diff --git a/examples/board_i2ctest/atmega48_i2ctest.c b/examples/board_i2ctest/atmega48_i2ctest.c new file mode 100644 index 0000000..938eaa1 --- /dev/null +++ b/examples/board_i2ctest/atmega48_i2ctest.c @@ -0,0 +1,85 @@ +/* + atmega48_i2ctest.c + + Copyright 2008-2011 Michel Pollet + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + +#include +#include +#include + +// for linker, emulator, and programmer's sake +#include "avr_mcu_section.h" +AVR_MCU(F_CPU, "atmega48"); + +#include "avr_twi_master.c" + +#include + +static int uart_putchar(char c, FILE *stream) { + if (c == '\n') + uart_putchar('\r', stream); + loop_until_bit_is_set(UCSR0A, UDRE0); + UDR0 = c; + return 0; +} + +static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, + _FDEV_SETUP_WRITE); + +int main() +{ + stdout = &mystdout; + + sei(); + TWI_Master_Initialise(); + + { // write 2 bytes at some random address + uint8_t msg[8] = { + 0xa0, // TWI address, + 0xaa, 0x01, // eeprom address, in little endian + 0xde, 0xad, // data bytes + }; + TWI_Start_Transceiver_With_Data(msg, 5, 1); + + while (TWI_Transceiver_Busy()) + sleep_mode(); + } + { + uint8_t msg[8] = { + 0xa0, // TWI address, + 0xa8, 0x01, // eeprom address, in little endian + }; + TWI_Start_Transceiver_With_Data(msg, 3, 0); // dont send stop! + + while (TWI_Transceiver_Busy()) + sleep_mode(); + } + { + uint8_t msg[9] = { + 0xa0 + 1, // TWI address, + }; + TWI_Start_Transceiver_With_Data(msg, 9, 1); // write 1 byte, read 8, send stop + + while (TWI_Transceiver_Busy()) + sleep_mode(); + } + cli(); + sleep_mode(); +} + diff --git a/examples/board_i2ctest/twi_master.c b/examples/board_i2ctest/avr_twi_master.c similarity index 91% rename from examples/board_i2ctest/twi_master.c rename to examples/board_i2ctest/avr_twi_master.c index 16c19b9..317e27b 100644 --- a/examples/board_i2ctest/twi_master.c +++ b/examples/board_i2ctest/avr_twi_master.c @@ -23,12 +23,12 @@ * ****************************************************************************/ -#include "ioavr.h" -#include "inavr.h" -#include "TWI_Master.h" +#include "avr_twi_master.h" -static unsigned char TWI_buf[ TWI_BUFFER_SIZE ]; // Transceiver buffer +//static unsigned char TWI_buf[ TWI_BUFFER_SIZE ]; // Transceiver buffer +static unsigned char *TWI_buf; static unsigned char TWI_msgSize; // Number of bytes to be transmitted. +static unsigned char TWI_sendStop; static unsigned char TWI_state = TWI_NO_STATE; // State byte. Default set to TWI_NO_STATE. union TWI_statusReg TWI_statusReg = {0}; // TWI_statusReg is defined in TWI_Master.h @@ -74,19 +74,16 @@ from the slave. Also include how many bytes that should be sent/read including t The function will hold execution (loop) until the TWI_ISR has completed with the previous operation, then initialize the next operation and return. ****************************************************************************/ -void TWI_Start_Transceiver_With_Data( unsigned char *msg, unsigned char msgSize ) +void TWI_Start_Transceiver_With_Data( + unsigned char *msg, unsigned char msgSize, unsigned char sendStop ) { - unsigned char temp; +// unsigned char temp; while ( TWI_Transceiver_Busy() ); // Wait until TWI is ready for next transmission. TWI_msgSize = msgSize; // Number of data to transmit. - TWI_buf[0] = msg[0]; // Store slave address with R/W setting. - if (!( msg[0] & (TRUE< + + This file is part of simavr. + + simavr is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + simavr is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with simavr. If not, see . + */ + + + +#include +#include +#include +#include + +#include "sim_avr.h" +#include "avr_twi.h" +#include "sim_elf.h" +#include "sim_gdb.h" +#include "sim_vcd_file.h" + + +typedef struct i2c_eeprom_t { + avr_irq_t * irq; // irq list + uint8_t addr_base; + uint8_t addr_mask; + + uint8_t selected; // selected address + int index; // byte index + + uint16_t reg_addr; // read/write address register + int size; // also implies the address size, one or two byte + uint8_t ee[4096]; +} i2c_eeprom_t; + +#include + +/* + * called when a RESET signal is sent + */ +static void +i2c_eeprom_in_hook( + struct avr_irq_t * irq, + uint32_t value, + void * param) +{ + i2c_eeprom_t * p = (i2c_eeprom_t*)param; + avr_twi_msg_irq_t v; + v.u.v = value; + + if (v.u.twi.msg & TWI_COND_STOP) { + if (p->selected) { + // it was us ! + printf("eeprom received stop\n"); + } + p->selected = 0; + p->index = 0; + p->reg_addr = 0; + } + if (v.u.twi.msg & TWI_COND_START) { + p->selected = 0; + p->index = 0; + if ((p->addr_base & p->addr_mask) == (v.u.twi.addr & p->addr_mask)) { + // it's us ! + p->selected = v.u.twi.addr; + avr_raise_irq(p->irq + TWI_IRQ_MISO, + avr_twi_irq_msg(TWI_COND_ACK, p->selected, 1)); + } + } + if (p->selected) { + if (v.u.twi.msg & TWI_COND_WRITE) { + printf("eeprom write %02x\n", v.u.twi.data); + // address size is how many bytes we use for address register + avr_raise_irq(p->irq + TWI_IRQ_MISO, + avr_twi_irq_msg(TWI_COND_ACK, p->selected, 1)); + int addr_size = p->size > 256 ? 2 : 1; + if (p->index < addr_size) { + p->reg_addr |= (v.u.twi.data << (p->index * 8)); + printf("eeprom set address to %04x\n", p->reg_addr); + } else { + printf("eeprom WRITE data %04x: %02x\n", p->reg_addr, v.u.twi.data); + p->ee[p->reg_addr++] = v.u.twi.data; + } + p->reg_addr &= (p->size -1); + p->index++; + } + if (v.u.twi.msg & TWI_COND_READ) { + printf("eeprom READ data %04x: %02x\n", p->reg_addr, p->ee[p->reg_addr]); + uint8_t data = p->ee[p->reg_addr++]; + avr_raise_irq(p->irq + TWI_IRQ_MISO, + avr_twi_irq_msg(TWI_COND_READ, p->selected, data)); + p->reg_addr &= (p->size -1); + p->index++; + } + } +} + +static const char * _ee_irq_names[2] = { + [TWI_IRQ_MISO] = "8>eeprom.out", + [TWI_IRQ_MOSI] = "32ee, 0xff, sizeof(p->ee)); + p->irq = avr_alloc_irq(&avr->irq_pool, 0, 2, _ee_irq_names); + avr_irq_register_notify(p->irq + TWI_IRQ_MOSI, i2c_eeprom_in_hook, p); + + p->size = size > sizeof(p->ee) ? sizeof(p->ee) : size; + if (data) + memcpy(p->ee, data, p->size); +} + +avr_t * avr = NULL; +avr_vcd_t vcd_file; + +i2c_eeprom_t ee; + +int main(int argc, char *argv[]) +{ + elf_firmware_t f; + const char * fname = "atmega48_i2ctest.axf"; + char path[256]; + + sprintf(path, "%s/%s", dirname(argv[0]), fname); + printf("Firmware pathname is %s\n", fname); + elf_read_firmware(fname, &f); + + printf("firmware %s f=%d mmcu=%s\n", fname, (int)f.frequency, f.mmcu); + + avr = avr_make_mcu_by_name(f.mmcu); + if (!avr) { + fprintf(stderr, "%s: AVR '%s' now known\n", argv[0], f.mmcu); + exit(1); + } + avr_init(avr); + avr_load_firmware(avr, &f); + + // initialize our 'peripheral' + i2c_eeprom_init(avr, &ee, 0xa0, 0xfe, NULL, 1024); + + // "connect" the IRQs of the button to the port pin of the AVR + avr_connect_irq( + ee.irq + TWI_IRQ_MISO, + avr_io_getirq(avr, AVR_IOCTL_TWI_GETIRQ(0), TWI_IRQ_MISO)); + avr_connect_irq( + avr_io_getirq(avr, AVR_IOCTL_TWI_GETIRQ(0), TWI_IRQ_MOSI), + ee.irq + TWI_IRQ_MOSI ); + + // even if not setup at startup, activate gdb if crashing + avr->gdb_port = 1234; + if (0) { + //avr->state = cpu_Stopped; + avr_gdb_init(avr); + } + + /* + * VCD file initialization + * + * This will allow you to create a "wave" file and display it in gtkwave + * Pressing "r" and "s" during the demo will start and stop recording + * the pin changes + */ + avr_vcd_init(avr, "gtkwave_output.vcd", &vcd_file, 100000 /* usec */); + avr_vcd_add_signal(&vcd_file, + avr_io_getirq(avr, AVR_IOCTL_TWI_GETIRQ(0), TWI_IRQ_STATUS), 8 /* bits */ , + "TWSR" ); + + printf( "Demo launching:\n" + ); + + while (1) + avr_run(avr); +} -- 2.39.5