From: Stephan Veigl Date: Tue, 16 Aug 2011 13:05:21 +0000 (+0200) Subject: bitbang: add new bitbang sub-module X-Git-Tag: v1.0a9~12 X-Git-Url: https://git.htl-mechatronik.at/public/?a=commitdiff_plain;h=14c1d340b2ff1c6d108199aafe50580fe406af12;p=sx%2Fsimavr.git bitbang: add new bitbang sub-module Signed-off-by: Stephan Veigl --- diff --git a/simavr/sim/avr_bitbang.c b/simavr/sim/avr_bitbang.c new file mode 100644 index 0000000..1bee186 --- /dev/null +++ b/simavr/sim/avr_bitbang.c @@ -0,0 +1,235 @@ +/* + avr_bitbang.c + + Copyright 2008, 2009 Michel Pollet + 2011 Stephan Veigl + + 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 "avr_bitbang.h" + +#include "sim_regbit.h" +#include "sim_core.h" +#include "avr_ioport.h" + +///@todo refactor SPI to bitbang + +#define BITBANG_MASK 0xFFFFFFFFUL + +/** + * read (sample) data from input pin + * + * @param p internal bitbang structure + */ +static void avr_bitbang_read_bit(avr_bitbang_t *p) +{ + avr_ioport_state_t iostate; + uint8_t bit = 0; + + if ( !p->enabled ) + return; + + // read from HW pin + if ( p->p_in.port ) { + avr_ioctl(p->avr, AVR_IOCTL_IOPORT_GETSTATE( p->p_in.port ), &iostate); + bit = ( iostate.pin >> p->p_in.pin ) & 1; + + if ( p->data_order ) { + // data order: shift right + p->data = (p->data >> 1) | ( bit << (p->buffer_size-1)); + } else { + // data order: shift left + p->data = (p->data << 1) | bit; + } + + } + + // module callback + if ( p->callback_bit_read ) { + p->callback_bit_read(bit, p->callback_param); + } + + // data sanitary + p->data = p->data & ~(BITBANG_MASK << p->buffer_size); +} + +/** + * write data to output pin + * + * @param p bitbang structure + */ +static void avr_bitbang_write_bit(avr_bitbang_t *p) +{ + uint8_t bit = 0; + + if ( !p->enabled ) + return; + + if ( p->data_order ) { + // data order: shift right + bit = p->data & 1; + } else { + // data order: shift left + bit = (p->data >> (p->buffer_size-1)) & 1; + } + + // output to HW pin + if ( p->p_out.port ) { + avr_raise_irq(avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_out.port ), p->p_out.pin), bit); + } + + // module callback + if ( p->callback_bit_write ) { + p->callback_bit_write(bit, p->callback_param); + } +} + + +/** + * process clock edges (both: positive and negative edges) + * + * @param p bitbang structure + * + */ +static void avr_bitbang_clk_edge(avr_bitbang_t *p) +{ + uint8_t phase = (p->clk_count & 1) ^ p->clk_phase; + uint8_t clk = (p->clk_count & 1) ^ p->clk_pol; + + if ( !p->enabled ) + return; + + // increase clock + p->clk_count++; + clk ^= 1; + phase ^= 1; + + // generate clock output on HW pin + if ( p->clk_generate && p->p_clk.port ) { + avr_raise_irq(avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), clk); + } + + if ( phase ) { + // read data in + avr_bitbang_read_bit(p); + + } else { + // write data out + avr_bitbang_write_bit(p); + } + + if ( p->clk_count >= (p->buffer_size*2) ) { + // transfer finished + if ( p->callback_transfer_finished ) { + p->data = p->callback_transfer_finished(p->data, p->callback_param); + } + p->clk_count = 0; + } +} + +static avr_cycle_count_t avr_bitbang_clk_timer(struct avr_t * avr, avr_cycle_count_t when, void * param) +{ + avr_bitbang_t * p = (avr_bitbang_t *)param; + + avr_bitbang_clk_edge(p); + + if ( p->enabled ) + return when + p->clk_cycles/2; + else + return 0; +} + +static void avr_bitbang_clk_hook(struct avr_irq_t * irq, uint32_t value, void * param) +{ + avr_bitbang_t * p = (avr_bitbang_t *)param; + uint8_t clk = (p->clk_count & 1) ^ p->clk_pol; + + // no clock change + if ( clk == value ) + return; + + avr_bitbang_clk_edge(p); +} + +/** + * reset bitbang sub-module + * + * @param avr avr attached to + * @param p bitbang structure + */ +void avr_bitbang_reset(avr_t *avr, avr_bitbang_t * p) +{ + p->avr = avr; + p->enabled = 0; + p->clk_count = 0; + p->data = 0; + + if ( p->buffer_size < 1 || p->buffer_size > 32 ) { + fprintf(stderr, + "Error: bitbang buffer size should be between 1 and 32. set value: %d\n", p->buffer_size); + abort(); + } + +} + +/** + * start bitbang transfer + * + * buffers should be written / cleared in advanced + * timers and interrupts are connected + * + * @param p bitbang structure + */ +void avr_bitbang_start(avr_bitbang_t * p) +{ + p->enabled = 1; + p->clk_count = 0; + + if ( p->clk_phase == 0 ) { + // write first bit + avr_bitbang_write_bit(p); + } + + if ( p->clk_generate ) { + // master mode, generate clock -> set timer + avr_cycle_timer_register(p->avr, (p->clk_cycles/2), avr_bitbang_clk_timer, p); + } else { + // slave mode -> attach clock function to clock pin + ///@todo test + avr_irq_register_notify( avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), avr_bitbang_clk_hook, p); + } + +} + + +/** + * stop bitbang transfer + * + * timers and interrupts are disabled + * + * @param p bitbang structure + */ +void avr_bitbang_stop(avr_bitbang_t * p) +{ + + p->enabled = 0; + avr_cycle_timer_cancel(p->avr, avr_bitbang_clk_timer, p); + avr_irq_unregister_notify( avr_io_getirq(p->avr, AVR_IOCTL_IOPORT_GETIRQ( p->p_clk.port ), p->p_clk.pin), avr_bitbang_clk_hook, p); +} diff --git a/simavr/sim/avr_bitbang.h b/simavr/sim/avr_bitbang.h new file mode 100644 index 0000000..3d8832f --- /dev/null +++ b/simavr/sim/avr_bitbang.h @@ -0,0 +1,117 @@ +/* + avr_bitbang.h + + Copyright 2008, 2009 Michel Pollet + 2011 Stephan Veigl + + 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 . + */ + +/** + @defgroup avr_bitbang Generic BitBang Module + @{ + + Generic BitBang Module of simavr AVR simulator. + + @par Features / Implementation Status + - easy buffer access with push() / pop() functions + - one input and one output pin (can be the same HW pin for I2C) + + @todo + - one input and one output pin (can be the same HW pin for I2C) + - one clock pin which can be configured as input or output + when the clock is output, the clock signal is generated with a + configured frequency (master / slave mode) + - 2x 32-bit buffers (input / output) (allows start, stop bits for UART, etc.) + - on each read / write a callback is executed to notify the master module + +*/ + + +#ifndef AVR_BITBANG_H_ +#define AVR_BITBANG_H_ + +#include "sim_avr.h" +#include "avr_ioport.h" + + + + +/// SPI Module initialization and state structure +typedef struct avr_bitbang_t { + avr_t * avr; ///< avr we are attached to + + uint8_t enabled; ///< bit-bang enabled flag + uint8_t clk_generate; ///< generate clock and write to clock pin (if available) -> master / slave mode + uint8_t clk_pol; ///< clock polarity, base (inactive) value of clock + uint8_t clk_phase; ///< clock phase / data sampling edge + /// - 0: data are sampled at first clock edge + /// - 1: data are sampled at second clock edge + uint32_t clk_cycles; ///< cycles per clock period - must be multiple of 2! (used if clk_generate is enabled) + uint8_t data_order; ///< data order / shift + /// - 0: shift left + /// - 1: shift right + + uint8_t buffer_size; ///< size of buffer in bits (1...32) + + void *callback_param; /// anonymous parameter for callback functions + void (*callback_bit_read)(uint8_t bit, void *param); ///< callback function to notify about bit read + void (*callback_bit_write)(uint8_t bit, void *param); ///< callback function to notify about bit write + uint32_t (*callback_transfer_finished)(uint32_t data, void *param); ///< callback function to notify about a complete transfer + /// (read received data and write new output data) + + avr_iopin_t p_clk; ///< clock pin (optional) + avr_iopin_t p_in; ///< data in pin + avr_iopin_t p_out; ///< data out pin + +// private data + uint32_t data; ///< data buffer + /// - latest received bit the is lowest / most right one, bit number: 0 + /// - next bit to be written is the highest one, bit number: (buffer_size-1) + int8_t clk_count; ///< internal clock edge count +} avr_bitbang_t; + +/** + * reset bitbang sub-module + * + * @param avr avr attached to + * @param p bitbang structure + */ +void avr_bitbang_reset(avr_t *avr, avr_bitbang_t * p); + +/** + * start bitbang transfer + * + * buffers should be written / cleared in advanced + * timers and interrupts are connected + * + * @param p bitbang structure + */ +void avr_bitbang_start(avr_bitbang_t * p); + + +/** + * stop bitbang transfer + * + * timers and interrupts are disabled + * + * @param p bitbang structure + */ +void avr_bitbang_stop(avr_bitbang_t * p); + + +#endif /* AVR_BITBANG_H_ */ +/// @} end of avr_bitbang group