From 0bfce45007c162fa3f98f4369f7069f3da372502 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Mon, 7 Dec 2009 19:45:17 +0000 Subject: [PATCH] Added a real example on how to integrate simavr, etc + OpenGL app loads, runs a firmware and interacts with it. See the README in examples/ledramp + Updated Makefiles & Readme Streamlined the makefiles, so they use the Makefile.common rules and so on. Also updated README to bring it up to date. + Adding TWI - work in progress Non working implementation skeleton. Defines a "slave" and a "bus" And the AVR twi interface that has one of each Signed-off-by: Michel Pollet --- .simavr.jcc | 35 ++++ Makefile.common | 31 +++- README | 27 +-- examples/Makefile | 7 + examples/board_ledramp/Makefile | 43 +++++ examples/board_ledramp/README | 15 ++ examples/board_ledramp/atmega48_ledramp.c | 107 ++++++++++++ examples/board_ledramp/ledramp.c | 199 ++++++++++++++++++++++ examples/parts/button.c | 58 +++++++ examples/parts/button.h | 47 +++++ simavr/Makefile | 22 +-- simavr/cores/sim_mega644.c | 30 ++++ simavr/cores/sim_megax8.c | 1 + simavr/cores/sim_megax8.h | 29 ++++ simavr/sim/avr_twi.c | 139 +++++++++++++++ simavr/sim/avr_twi.h | 73 ++++++++ simavr/sim/sim_twi.c | 121 +++++++++++++ simavr/sim/sim_twi.h | 71 ++++++++ tests/Makefile | 6 - 19 files changed, 1021 insertions(+), 40 deletions(-) create mode 100644 examples/Makefile create mode 100644 examples/board_ledramp/Makefile create mode 100644 examples/board_ledramp/README create mode 100644 examples/board_ledramp/atmega48_ledramp.c create mode 100644 examples/board_ledramp/ledramp.c create mode 100644 examples/parts/button.c create mode 100644 examples/parts/button.h create mode 100644 simavr/sim/avr_twi.c create mode 100644 simavr/sim/avr_twi.h create mode 100644 simavr/sim/sim_twi.c create mode 100644 simavr/sim/sim_twi.h diff --git a/.simavr.jcc b/.simavr.jcc index d7a4a41..b8d106c 100644 --- a/.simavr.jcc +++ b/.simavr.jcc @@ -8,6 +8,14 @@ T F "./simavr/Makefile" T +2 "avr_twi.c" +F +"./simavr/sim/avr_twi.c" +T +2 "avr_twi.h" +F +"./simavr/sim/avr_twi.h" +T 2 "avr_spi.c" F "./simavr/sim/avr_spi.c" @@ -48,6 +56,14 @@ T F "./simavr/sim/avr_uart.h" T +2 "sim_twi.c" +F +"./simavr/sim/sim_twi.c" +T +2 "sim_twi.h" +F +"./simavr/sim/sim_twi.h" +T 2 "sim_core.c" F "./simavr/sim/sim_core.c" @@ -166,6 +182,25 @@ T F "./simavr/cores/sim_mega644.c" F +T +1 "examples" +T +2 "atmega48_ledramp.c" +F +"./examples/board_ledramp/atmega48_ledramp.c" +T +2 "ledramp.c" +F +"./examples/board_ledramp/ledramp.c" +T +2 "button.c" +F +"./examples/parts/button.c" +T +2 "button.h" +F +"./examples/parts/button.h" +F F # tasks 4 diff --git a/Makefile.common b/Makefile.common index 1f89b04..77b1ad3 100644 --- a/Makefile.common +++ b/Makefile.common @@ -26,6 +26,20 @@ # You should have received a copy of the GNU General Public License # along with simavr. If not, see . +CFLAGS += -g --std=gnu99 -O2 +CFLAGS += ${patsubst %,-I%,${subst :, ,${IPATH}}} +LDFLAGS += -lelf + +ifeq (${shell uname}, Darwin) +AVR_ROOT := "/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/" +AVR_INC := ${AVR_ROOT}/avr-4/ +AVR := ${AVR_ROOT}/bin/avr- +else +AVR_ROOT := /usr/lib/avr +AVR_INC := ${AVR_ROOT} +AVR := avr- +endif + %.hex: %.axf @${AVR}objcopy -j .text -j .data -O ihex ${<} ${@} @@ -34,7 +48,7 @@ # --mcall-prologues %.axf: %.c - @echo CC ${<} + @echo AVR-CC ${<} @part=${<} ; part=$${part/_*}; \ ${AVR}gcc -Wall -gdwarf-2 -Os -std=gnu99 \ -mmcu=$$part \ @@ -43,6 +57,19 @@ -ffunction-sections -fdata-sections \ -Wl,--relax,--gc-sections \ -Wl,--undefined=_mmcu,--section-start=.mmcu=0x910000 \ - -I../include \ + -I../include -I../../include \ ${<} -o ${@} @${AVR}size ${@}|sed '1d' + +OBJ = obj + +${OBJ}/%.o: %.c + @gcc $(CFLAGS) -MD \ + $< -c -o $@ + @echo CC $< + +${OBJ}: + mkdir -p ${OBJ} + +# include the dependency files generated by gcc, if any +-include ${wildcard ${OBJ}/*.d} diff --git a/README b/README index 7aaa72f..185162b 100644 --- a/README +++ b/README @@ -7,29 +7,32 @@ simavr is a new AVR simulator for linux, or any platform that uses avr-gcc. It u avr-gcc own register definition to simplify creating new targets for supoortted AVR devices. -The core was made to be small and compact, and hackable so allow quick protoyping +The core was made to be small and compact, and hackable so allow quick prototyping of an AVR project. The simulator loads .elf directly, and there is even a way to specify simulation parameters directly in the emulated code using an .elf section. -The status of the project is the core works at about 98% (ie, it works, but there -is a known bug). The supported IOs are eeprom, IO ports (including pin interupts), -8 bits timers (well, one of mode of the myriad) and a simple UART that makes -ÒprintfÓ work. +The status of the project is the core works fine now. The supported IOs are eeprom, +IO ports (including pin interupts), 8 bits timers (well, one of mode of the myriad), +SPI master & slave, and the UART with tx&rx interrupts. -gdb support is planned next. +gdb support is implemented and works great (minus watchpoints). + +The supported AVR cores are in the simavr/cores directory. It's very simple to +add new devices if yours is not there. + +There is also one example of how to integrate simavr core into your own program in the +"examples" directory. + +(C) 2008-2010, Michel Pollet PREQUISTES: + avr-gcc ; tested with 4.3.4 (from debian) The code rudely assumes the avr-gcc is in /usr/lib/avr/include/... + On OSX, the Makefile tries to use the Arduino.app toolchain & headers + libelf-dev BUILD: + make TEST: -+ ./simavr/simavr tests/atmega88_example.axf - -There are no command line options for now.. The simulator recognizes attiny85, -atmega48,88,168 and atmega644. It's fairly easy to add new cores. - -Michel Pollet ++ ./simavr/run_avr tests/atmega88_example.axf diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..e9ef6c5 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,7 @@ + +all: + make -C board_ledramp + +clean: + make -C board_ledramp clean + diff --git a/examples/board_ledramp/Makefile b/examples/board_ledramp/Makefile new file mode 100644 index 0000000..18238ab --- /dev/null +++ b/examples/board_ledramp/Makefile @@ -0,0 +1,43 @@ +# +# Copyright 2008, 2009 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 . + +board= ledramp +firm_src = ${wildcard at*${board}.c} +firmware = ${firm_src:.c=.axf} +simavr = ../../ + +IPATH = . +IPATH += ../parts +IPATH += ${simavr}/include +IPATH += ${simavr}/simavr/sim + +VPATH = . +VPATH += ../parts + +LDFLAGS += -lglut -lpthread + +all: obj ${firmware} ${board} + +include ${simavr}/Makefile.common + +${board} : ${OBJ}/button.o +${board} : ${OBJ}/${board}.o + gcc -MD ${CFLAGS} -o $@ $^ $(LDFLAGS) ${simavr}/simavr/libsimavr.a + +clean: + rm -rf obj *.hex *.a *.axf ${board} diff --git a/examples/board_ledramp/README b/examples/board_ledramp/README new file mode 100644 index 0000000..d701308 --- /dev/null +++ b/examples/board_ledramp/README @@ -0,0 +1,15 @@ + +This contains a sample program to demonstrate the use of simavr +using 'custom' code, and own "peripherals". It shows how it is +possible to "hook" code to the AVR pins, and also how to make +"peripherals" and also hook them up to AVR pins. + +The demo will display a running "led ramp" that are the representation +of the PORTB pins of the mega48. + +The mega48 firmware runs a timer and a pin-change interrupt that +will react to a "button press". + +The display uses opengl and "glut" so it should be very portable. +On most linux you will need "freeglut-dev" package. + diff --git a/examples/board_ledramp/atmega48_ledramp.c b/examples/board_ledramp/atmega48_ledramp.c new file mode 100644 index 0000000..9dba9b7 --- /dev/null +++ b/examples/board_ledramp/atmega48_ledramp.c @@ -0,0 +1,107 @@ +/* + atmega48_ledramp.c + + Copyright 2008, 2009 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 +/* ------------------------------------------------------------------------- */ +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); + +#define TICK_HZ 64 + +volatile uint32_t tickCount; + +ISR(TIMER2_COMPA_vect) // handler for Output Compare 1 overflow interrupt +{ + sei(); + tickCount++; +} + +void tick_init() +{ + /* + Timer 2 as RTC + */ + // needs to do that before changing the timer registers + // ASYNC timer using a 32k crystal + ASSR |= (1 << AS2); + TCCR2A = (1 << WGM21); + // use CLK/8 prescale value, clear timer/counter on compareA match + TCCR2B = (2 << CS20); + /* -- MathPad + clock=32768 + prescaler=8 + hz=64 + (clock/prescaler/hz)-1:63 -- */ + OCR2A = 63; + TIMSK2 |= (1 << OCIE2A); +} + +volatile uint8_t pressed = 0; + +ISR(PCINT1_vect) +{ + pressed = (PINC & (1 << PC0)) ? 0 : 1; + // wouldn't do that on real hardware, but it's a demo... + printf("PCINT1_vect %02x\n", PINC); +} + +int main() +{ + DDRB=0xff; // all PORT B output + DDRC = 0; // make PORT C input + // enable pin change interrupt for PORT C pin 0 + PCMSK1 |= (1 << PCINT8); // C0 + PCICR |= (1 << PCIE1); + + stdout = &mystdout; + + tick_init(); + sei(); + + uint8_t mask = 0; + for (;;) { + mask <<= 1; + if (!mask) + mask = 1; + if (pressed) + PORTB = 0xff; + else + PORTB = mask; + sleep_mode(); + } +} + diff --git a/examples/board_ledramp/ledramp.c b/examples/board_ledramp/ledramp.c new file mode 100644 index 0000000..78ec0c7 --- /dev/null +++ b/examples/board_ledramp/ledramp.c @@ -0,0 +1,199 @@ +/* + ledramp.c + + Copyright 2008, 2009 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 +#include + +#include "sim_avr.h" +#include "avr_ioport.h" +#include "sim_elf.h" +#include "sim_gdb.h" + +#include "button.h" + +button_t button; +int do_button_press = 0; +avr_t * avr = NULL; +uint8_t pin_state = 0; // current port B + +float pixsize = 64; +int window; + +/* + * called when the AVR change any of the pins on port B + * so lets update our buffer + */ +void pin_changed_hook(struct avr_irq_t * irq, uint32_t value, void * param) +{ + pin_state = (pin_state & ~(1 << irq->irq)) | (value << irq->irq); +} + +void displayCB(void) /* function called whenever redisplay needed */ +{ + // OpenGL rendering goes here... + glClear(GL_COLOR_BUFFER_BIT); + + // Set up modelview matrix + glMatrixMode(GL_MODELVIEW); // Select modelview matrix + glLoadIdentity(); // Start with an identity matrix + + float grid = pixsize; + float size = grid * 0.8; + glBegin(GL_QUADS); + glColor3f(1,0,0); + + for (int di = 0; di < 8; di++) { + char on = (pin_state & (1 << di)) != 0; + if (on) { + float x = (di) * grid; + float y = 0; //(si * grid * 8) + (di * grid); + glVertex2f(x + size, y + size); + glVertex2f(x, y + size); + glVertex2f(x, y); + glVertex2f(x + size, y); + } + } + + glEnd(); + glutSwapBuffers(); + //glFlush(); /* Complete any pending operations */ +} + +void keyCB(unsigned char key, int x, int y) /* called on key press */ +{ + if (key == 'q') + exit(0); + static uint8_t buf[64]; + switch (key) { + case 'q': + case 0x1f: // escape + exit(0); + break; + case ' ': + do_button_press++; // pass the message to the AVR thread + break; + } +} + +// gl timer. if the pin have changed states, refresh display +void timerCB(int i) +{ + static uint8_t oldstate = 0xff; + // restart timer + glutTimerFunc(1000/64, timerCB, 0); + + if (oldstate != pin_state) { + oldstate = pin_state; + glutPostRedisplay(); + } +} + +static void * avr_run_thread(void * oaram) +{ + int b_press = do_button_press; + + while (1) { + avr_run(avr); + if (do_button_press != b_press) { + b_press = do_button_press; + printf("Button pressed\n"); + button_press(&button, 1000000); + } + } +} + + +int main(int argc, char *argv[]) +{ + elf_firmware_t f; + const char * fname = "atmega48_ledramp.axf"; + + elf_read_firmware(fname, &f); + + printf("firmware %s f=%d mmcu=%s\n", fname, (int)f.mmcu.f_cpu, f.mmcu.name); + + avr = avr_make_mcu_by_name(f.mmcu.name); + if (!avr) { + fprintf(stderr, "%s: AVR '%s' now known\n", argv[0], f.mmcu.name); + exit(1); + } + avr_init(avr); + avr_load_firmware(avr, &f); + + // initialize our 'peripheral' + button_init(avr, &button); + // "connect" the output irw of the button to the port pin of the AVR + avr_connect_irq( + button.irq + IRQ_BUTTON_OUT, + avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('C'), 0)); + // 'raise' it, it's a "pullup" + avr_raise_irq(button.irq + IRQ_BUTTON_OUT, 1); + + // connect all the pins on port B to our callback + for (int i = 0; i < 8; i++) + avr_irq_register_notify( + avr_io_getirq(avr, AVR_IOCTL_IOPORT_GETIRQ('B'), i), + pin_changed_hook, + NULL); + + // even if not setup at startup, activate gdb if crashing + avr->gdb_port = 1234; + if (0) { + //avr->state = cpu_Stopped; + avr_gdb_init(avr); + } + + printf( "Demo launching: 'LED' bar is PORTB, updated every 1/64s by the AVR\n" + " firmware using a timer. If you press 'space' this presses a virtual\n" + " 'button' that is hooked to the virtual PORTC pin 0 and will\n" + " trigger a 'pin change interrupt' in the AVR core, and will 'invert'\n" + " the display.\n" + " Press 'q' to quit\n"); + + /* + * OpenGL init, can be ignored + */ + glutInit(&argc, argv); /* initialize GLUT system */ + + glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE); + glutInitWindowSize(8 * pixsize, 1 * pixsize); /* width=400pixels height=500pixels */ + window = glutCreateWindow("Glut"); /* create window */ + + // Set up projection matrix + glMatrixMode(GL_PROJECTION); // Select projection matrix + glLoadIdentity(); // Start with an identity matrix + glOrtho(0, 8 * pixsize, 0, 1 * pixsize, 0, 10); + glScalef(1,-1,1); + glTranslatef(0, -1 * pixsize, 0); + + glutDisplayFunc(displayCB); /* set window's display callback */ + glutKeyboardFunc(keyCB); /* set window's key callback */ + glutTimerFunc(1000 / 24, timerCB, 0); + + // the AVR run on it's own thread. it even allows for debugging! + pthread_t run; + pthread_create(&run, NULL, avr_run_thread, NULL); + + glutMainLoop(); +} diff --git a/examples/parts/button.c b/examples/parts/button.c new file mode 100644 index 0000000..2b89683 --- /dev/null +++ b/examples/parts/button.c @@ -0,0 +1,58 @@ +/* + button.c + + This defines a sample for a very simple "peripheral" + that can talk to an AVR core. + It is in fact a bit more involved than strictly necessary, + but is made to demonstrante a few useful features that are + easy to use. + + Copyright 2008, 2009 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 "sim_avr.h" +#include "button.h" + +static avr_cycle_count_t button_auto_release(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + button_t * b = (button_t *)param; + avr_raise_irq(b->irq + IRQ_BUTTON_OUT, 1); + printf("button_auto_release\n"); + return 0; +} + +/* + * button press. set the "pin" to zerok and register a timer + * that will reset it in a few usecs + */ +void button_press(button_t * b, uint32_t duration_usec) +{ + avr_cycle_timer_cancel(b->avr, button_auto_release, b); + avr_raise_irq(b->irq + IRQ_BUTTON_OUT, 0);// press + // register the auto-release + avr_cycle_timer_register_usec(b->avr, duration_usec, button_auto_release, b); +} + +void button_init(struct avr_t *avr, button_t * b) +{ + b->irq = avr_alloc_irq(0, IRQ_BUTTON_COUNT); + b->avr = avr; +} + diff --git a/examples/parts/button.h b/examples/parts/button.h new file mode 100644 index 0000000..51cecc8 --- /dev/null +++ b/examples/parts/button.h @@ -0,0 +1,47 @@ +/* + button.h + + This defines a sample for a very simple "peripheral" + that can talk to an AVR core. + It is in fact a bit more involved than strictly necessary, + but is made to demonstrante a few useful features that are + easy to use. + + Copyright 2008, 2009 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 . + */ + +#ifndef __BUTTON_H__ +#define __BUTTON_H__ + +#include "sim_irq.h" + +enum { + IRQ_BUTTON_OUT = 0, + IRQ_BUTTON_COUNT +}; + +typedef struct button_t { + avr_irq_t * irq; // output irq + struct avr_t * avr; + uint8_t value; +} button_t; + +void button_init(struct avr_t * avr, button_t * b); + +void button_press(button_t * b, uint32_t duration_usec); +#endif /* __BUTTON_H__*/ diff --git a/simavr/Makefile b/simavr/Makefile index 90e5646..ab80a90 100644 --- a/simavr/Makefile +++ b/simavr/Makefile @@ -18,13 +18,6 @@ target = run_avr -ifeq (${shell uname}, Darwin) -AVR_ROOT := "/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/avr-4/" -else -AVR_ROOT := /usr/lib/avr -endif - -CFLAGS = -g -std=gnu99 -Wall CFLAGS += -O3 -mfpmath=sse -msse2 cores = ${wildcard cores/*.c} @@ -42,25 +35,17 @@ IPATH += ../../shared IPATH += ../include IPATH += /opt/local/include -CFLAGS += ${patsubst %,-I%,${subst :, ,${IPATH}}} LFLAGS = -L/opt/local/lib/ -LDFLAGS += -lelf all: obj libsimavr.a ${target} -obj: - @mkdir -p obj - obj/sim_%.o : cores/sim_%.c @gcc $(CFLAGS) -MD \ - -I${AVR_ROOT}/include/ \ + -I${AVR_INC}/include/ \ $< -c -o $@ @echo CORE $< -obj/%.o: %.c - @gcc $(CFLAGS) -MD \ - $< -c -o $@ - @echo CC $< +include ../Makefile.common libsimavr.a : ${cores_o} libsimavr.a : ${sim_o} @@ -78,6 +63,3 @@ ${target} : obj/${target}.o clean: rm -rf ${target} obj *.a -# include the dependency files generated by gcc, if any --include ${wildcard obj/*.d} - diff --git a/simavr/cores/sim_mega644.c b/simavr/cores/sim_mega644.c index cc1b396..f9945c2 100644 --- a/simavr/cores/sim_mega644.c +++ b/simavr/cores/sim_mega644.c @@ -27,6 +27,7 @@ #include "avr_uart.h" #include "avr_timer8.h" #include "avr_spi.h" +#include "avr_twi.h" #define _AVR_IO_H_ #define __ASSEMBLER__ @@ -43,6 +44,7 @@ static struct mcu_t { avr_uart_t uart0,uart1; avr_timer8_t timer0,timer2; avr_spi_t spi; + avr_twi_t twi; } mcu = { .core = { .mmcu = "atmega644", @@ -220,6 +222,33 @@ static struct mcu_t { .vector = SPI_STC_vect, }, }, + + .twi = { + .disabled = AVR_IO_REGBIT(PRR,PRTWI), + + .r_twcr = TWCR, + .r_twsr = TWSR, + .r_twbr = TWBR, + .r_twdr = TWDR, + .r_twar = TWAR, + .r_twamr = TWAMR, + + .twen = AVR_IO_REGBIT(TWCR, TWEN), + .twea = AVR_IO_REGBIT(TWCR, TWEA), + .twsta = AVR_IO_REGBIT(TWCR, TWSTA), + .twsto = AVR_IO_REGBIT(TWCR, TWSTO), + .twwc = AVR_IO_REGBIT(TWCR, TWWC), + + .twsr = AVR_IO_REGBITS(TWSR, TWS3, 0x1f), // 5 bits + .twps = AVR_IO_REGBITS(TWSR, TWPS0, 0x3), // 2 bits + + .twi = { + .enable = AVR_IO_REGBIT(TWCR, TWIE), + .raised = AVR_IO_REGBIT(TWSR, TWINT), + .vector = TWI_vect, + }, + }, + }; static avr_t * make() @@ -248,6 +277,7 @@ static void init(struct avr_t * avr) avr_timer8_init(avr, &mcu->timer0); avr_timer8_init(avr, &mcu->timer2); avr_spi_init(avr, &mcu->spi); + avr_twi_init(avr, &mcu->twi); } static void reset(struct avr_t * avr) diff --git a/simavr/cores/sim_megax8.c b/simavr/cores/sim_megax8.c index 7b1f293..21ed0fb 100644 --- a/simavr/cores/sim_megax8.c +++ b/simavr/cores/sim_megax8.c @@ -37,6 +37,7 @@ void mx8_init(struct avr_t * avr) avr_timer8_init(avr, &mcu->timer0); avr_timer8_init(avr, &mcu->timer2); avr_spi_init(avr, &mcu->spi); + avr_twi_init(avr, &mcu->twi); } void mx8_reset(struct avr_t * avr) diff --git a/simavr/cores/sim_megax8.h b/simavr/cores/sim_megax8.h index 97eb61e..4ab93a5 100644 --- a/simavr/cores/sim_megax8.h +++ b/simavr/cores/sim_megax8.h @@ -29,6 +29,7 @@ #include "avr_uart.h" #include "avr_timer8.h" #include "avr_spi.h" +#include "avr_twi.h" void mx8_init(struct avr_t * avr); void mx8_reset(struct avr_t * avr); @@ -43,6 +44,7 @@ struct mcu_t { avr_uart_t uart; avr_timer8_t timer0,timer2; avr_spi_t spi; + avr_twi_t twi; }; #ifdef SIM_CORENAME @@ -196,6 +198,33 @@ struct mcu_t SIM_CORENAME = { .vector = SPI_STC_vect, }, }, + + .twi = { + .disabled = AVR_IO_REGBIT(PRR,PRTWI), + + .r_twcr = TWCR, + .r_twsr = TWSR, + .r_twbr = TWBR, + .r_twdr = TWDR, + .r_twar = TWAR, + .r_twamr = TWAMR, + + .twen = AVR_IO_REGBIT(TWCR, TWEN), + .twea = AVR_IO_REGBIT(TWCR, TWEA), + .twsta = AVR_IO_REGBIT(TWCR, TWSTA), + .twsto = AVR_IO_REGBIT(TWCR, TWSTO), + .twwc = AVR_IO_REGBIT(TWCR, TWWC), + + .twsr = AVR_IO_REGBITS(TWSR, TWS3, 0x1f), // 5 bits + .twps = AVR_IO_REGBITS(TWSR, TWPS0, 0x3), // 2 bits + + .twi = { + .enable = AVR_IO_REGBIT(TWCR, TWIE), + .raised = AVR_IO_REGBIT(TWSR, TWINT), + .vector = TWI_vect, + }, + }, + }; #endif /* SIM_CORENAME */ diff --git a/simavr/sim/avr_twi.c b/simavr/sim/avr_twi.c new file mode 100644 index 0000000..3c4d120 --- /dev/null +++ b/simavr/sim/avr_twi.c @@ -0,0 +1,139 @@ +/* + avr_twi.c + + Copyright 2008, 2009 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 "avr_twi.h" + +static uint8_t avr_twi_read(struct avr_t * avr, avr_io_addr_t addr, void * param) +{ + avr_twi_t * p = (avr_twi_t *)param; +// uint8_t v = p->input_data_register; +// p->input_data_register = 0; +// printf("avr_twi_read = %02x\n", v); + return 0; +} + +static void avr_twi_write(struct avr_t * avr, avr_io_addr_t addr, uint8_t v, void * param) +{ + avr_twi_t * p = (avr_twi_t *)param; +#if 0 + if (addr == p->r_spdr) { +// printf("avr_twi_write = %02x\n", v); + avr_core_watch_write(avr, addr, v); + + if (avr_regbit_get(avr, p->spe)) { + // in master mode, any byte is sent as it comes.. + if (avr_regbit_get(avr, p->mstr)) { + avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT, v); + } + } + } +#endif +} + +static void avr_twi_irq_input(struct avr_irq_t * irq, uint32_t value, void * param) +{ + avr_twi_t * p = (avr_twi_t *)param; + avr_t * avr = p->io.avr; + + // check to see if we are enabled + if (!avr_regbit_get(avr, p->twen)) + return; +#if 0 + // double buffer the input.. ? + p->input_data_register = value; + avr_raise_interrupt(avr, &p->twi); + + // if in slave mode, + // 'output' the byte only when we received one... + if (!avr_regbit_get(avr, p->mstr)) { + avr_raise_irq(p->io.irq + TWI_IRQ_OUTPUT, avr->data[p->r_spdr]); + } +#endif +} + +static int twi_slave_has_address(struct twi_slave_t* p, uint8_t address) +{ + return 0; +} + + // handle start conditionto address+w, restart means "stop" wasn't called +static int twi_slave_start(struct twi_slave_t* p, uint8_t address, int restart) +{ + return 0; +} + + // handle a data write, after a (re)start +static int twi_slave_write(struct twi_slave_t* p, uint8_t v) +{ + return 0; +} + + // handle a data read, after a (re)start +static uint8_t twi_slave_read(struct twi_slave_t* p) +{ + return 0; +} + + // stop condition detected +static void twi_slave_stop(struct twi_slave_t* p) +{ +} + +static twi_slave_t slave_driver = { + .has_address = twi_slave_has_address, + .start = twi_slave_start, + .stop = twi_slave_stop, + .write = twi_slave_write, + .read = twi_slave_read +}; + +void avr_twi_reset(struct avr_io_t *io) +{ + avr_twi_t * p = (avr_twi_t *)io; + //avr_irq_register_notify(p->io.irq + TWI_IRQ_INPUT, avr_twi_irq_input, p); +} + +static avr_io_t _io = { + .kind = "twi", + .reset = avr_twi_reset, +}; + +void avr_twi_init(avr_t * avr, avr_twi_t * p) +{ + p->io = _io; + avr_register_io(avr, &p->io); + avr_register_vector(avr, &p->twi); + p->slave = slave_driver; // get default callbacks + twi_slave_init(&p->slave, p); + twi_bus_init(&p->bus); + + printf("%s TWI%c init\n", __FUNCTION__, p->name); + + // allocate this module's IRQ + p->io.irq_count = TWI_IRQ_COUNT; + p->io.irq = avr_alloc_irq(0, p->io.irq_count); + p->io.irq_ioctl_get = AVR_IOCTL_TWI_GETIRQ(p->name); + + avr_register_io_write(avr, p->r_twdr, avr_twi_write, p); + avr_register_io_read(avr, p->r_twdr, avr_twi_read, p); +} + diff --git a/simavr/sim/avr_twi.h b/simavr/sim/avr_twi.h new file mode 100644 index 0000000..31c3ca5 --- /dev/null +++ b/simavr/sim/avr_twi.h @@ -0,0 +1,73 @@ +/* + avr_twi.h + + Copyright 2008, 2009 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 . + */ + +#ifndef AVR_TWI_H_ +#define AVR_TWI_H_ + +#include "sim_avr.h" + +#include "sim_twi.h" + +enum { + TWI_IRQ_INPUT = 0, + TWI_IRQ_OUTPUT, + TWI_IRQ_COUNT +}; + + +// add port number to get the real IRQ +#define AVR_IOCTL_TWI_GETIRQ(_name) AVR_IOCTL_DEF('t','w','i',(_name)) +// return a pointer to the slave structure related to this TWI port +#define AVR_IOCTL_TWI_GETSLAVE(_name) AVR_IOCTL_DEF('t','w','s',(_name)) +// retutn this twi interface "master" bus +#define AVR_IOCTL_TWI_GETBUS(_name) AVR_IOCTL_DEF('t','w','b',(_name)) + +typedef struct avr_twi_t { + avr_io_t io; + char name; + + twi_slave_t slave; // when we are a slave, to be attached to some bus + twi_bus_t bus; // when we are a master, to attach slaves to + + avr_regbit_t disabled; // bit in the PRR + + avr_io_addr_t r_twbr; // bit rate register + avr_io_addr_t r_twcr; // control register + avr_io_addr_t r_twsr; // status register + avr_io_addr_t r_twar; // address register (slave) + avr_io_addr_t r_twamr; // address mask register + avr_io_addr_t r_twdr; // data register + + avr_regbit_t twen; // twi enable bit + avr_regbit_t twea; // enabke acknowledge bit + avr_regbit_t twsta; // start condition + avr_regbit_t twsto; // stop condition + avr_regbit_t twwc; // write collision + + avr_regbit_t twsr; // status registers, (5 bits) + avr_regbit_t twps; // prescaler bits (2 bits) + + avr_int_vector_t twi; // spi interrupt +} avr_twi_t; + +void avr_twi_init(avr_t * avr, avr_twi_t * port); + +#endif /* AVR_TWI_H_ */ diff --git a/simavr/sim/sim_twi.c b/simavr/sim/sim_twi.c new file mode 100644 index 0000000..9d6501e --- /dev/null +++ b/simavr/sim/sim_twi.c @@ -0,0 +1,121 @@ +/* + sim_twi.c + + Internal TWI/i2c slave/master subsystem + + Copyright 2008, 2009 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 "sim_twi.h" + +void twi_bus_init(twi_bus_t * bus) +{ +} + +void twi_bus_attach(twi_bus_t * bus, twi_slave_t * slave) +{ + twi_slave_detach(slave); + slave->bus = bus; + slave->next = bus->slave; + bus->slave = slave; +} + +int twi_bus_start(twi_bus_t * bus, uint8_t address) +{ + // if we already have a peer, check to see if it's + // still matching, if so, skip the lookup + if (bus->peer && twi_slave_match(bus->peer, address)) + return bus->peer->start(bus->peer, address, 1); + + bus->peer = NULL; + twi_slave_t *s = bus->slave; + while (s) { + if (twi_slave_match(s, address)) { + if (s->start(s, address, 0)) { + bus->peer = s; + s->byte_index = 0; + return 1; + } + } + s = s->next; + } + return 0; +} + +void twi_bus_stop(twi_bus_t * bus) +{ + if (bus->peer && bus->peer->stop) + bus->peer->stop(bus->peer); + bus->peer = NULL; +} + +int twi_bus_write(twi_bus_t * bus, uint8_t data) +{ + if (!bus->peer || !bus->peer->write) + return 0; + + int res = bus->peer->write(bus->peer, data); + if (bus->peer) + bus->peer->byte_index++; + return res; +} + +uint8_t twi_bus_read(twi_bus_t * bus) +{ + if (!bus->peer || !bus->peer->read) + return 0; + + uint8_t res = bus->peer->read(bus->peer); + if (bus->peer) + bus->peer->byte_index++; + return res; +} + +void twi_slave_init(twi_slave_t * slave, void * param) +{ + slave->param = param; +} + +void twi_slave_detach(twi_slave_t * slave) +{ + if (!slave || !slave->bus) + return; + twi_slave_t *s = slave->bus->slave; + while (s) { + if (s->next == slave) { + // clear that, too + if (slave->bus->peer == slave) + slave->bus->peer = NULL; + + s->next = slave->next; + slave->next = NULL; + slave->bus = NULL; + return; + } + s = s->next; + } +} + +int twi_slave_match(twi_slave_t * slave, uint8_t address) +{ + if (slave->has_address) + return slave->has_address(slave, address); + return (address & ~1) == (slave->address & ~1); +} + diff --git a/simavr/sim/sim_twi.h b/simavr/sim/sim_twi.h new file mode 100644 index 0000000..7f383cb --- /dev/null +++ b/simavr/sim/sim_twi.h @@ -0,0 +1,71 @@ +/* + sim_twi.h + + Internal TWI/i2c slave/master subsystem + + You can have a "bus" to talk to a bunch of "slaves" + + Copyright 2008, 2009 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 . + */ + +#ifndef SIM_TWI_H_ +#define SIM_TWI_H_ + +#include + +typedef struct twi_slave_t { + struct twi_bus_t * bus; // bus we are attached to + struct twi_slave_t * next; // daisy chain on the bus + + void * param; // module parameter + uint8_t address; // slave address (lowest bit is not used, it's for the W bit) + int byte_index; // byte index in the transaction (since last start, restart) + + // return non-zero if this slave address is handled by this slave + // if NULL, the "address" field is used instead. If this function + // is present, 'address' field is not used. + int (*has_address)(struct twi_slave_t* p, uint8_t address); // optional + + // handle start conditionto address+w, restart means "stop" wasn't called + int (*start)(struct twi_slave_t* p, uint8_t address, int restart); + // handle a data write, after a (re)start + int (*write)(struct twi_slave_t* p, uint8_t v); + // handle a data read, after a (re)start + uint8_t (*read)(struct twi_slave_t* p); + // stop condition detected + void (*stop)(struct twi_slave_t* p); +} twi_slave_t; + + +typedef struct twi_bus_t { + struct twi_slave_t * slave; // daisy chain on the bus + + struct twi_slave_t * peer; // during a transaction, this is the selected slave +} twi_bus_t; + +void twi_bus_init(twi_bus_t * bus); +int twi_bus_start(twi_bus_t * bus, uint8_t address); +int twi_bus_write(twi_bus_t * bus, uint8_t data); +uint8_t twi_bus_read(twi_bus_t * bus); +void twi_bus_stop(twi_bus_t * bus); + +void twi_slave_init(twi_slave_t * slave, void * param); +void twi_slave_detach(twi_slave_t * slave); +int twi_slave_match(twi_slave_t * slave, uint8_t address); + +#endif /* SIM_TWI_H_ */ diff --git a/tests/Makefile b/tests/Makefile index e75a12f..900ad75 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -26,12 +26,6 @@ # You should have received a copy of the GNU General Public License # along with simavr. If not, see . -ifeq (${shell uname}, Darwin) -AVR_ROOT := "/Applications/Arduino.app/Contents/Resources/Java/hardware/tools/avr/bin/" -else -AVR_ROOT := -endif -AVR := ${AVR_ROOT}avr- SHELL = /bin/bash -- 2.39.5