Created
September 15, 2011 01:40
-
-
Save blalor/1218310 to your computer and use it in GitHub Desktop.
ATtiny85 pin crosstalk?
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "soft_serial.h" | |
| #include <avr/io.h> | |
| #include <avr/interrupt.h> | |
| #include <util/delay.h> | |
| #include <stdio.h> | |
| #include <stdbool.h> | |
| static volatile uint8_t data_byte; | |
| static volatile bool have_data; | |
| static const SoftSerialRegisters ssRegs = { | |
| // rx port, pin, ddr, port number | |
| &PORTB, | |
| &PINB, | |
| &DDRB, | |
| PB3, | |
| PCINT3, | |
| // tx port, ddr, port number | |
| &PORTB, | |
| &DDRB, | |
| PB4, | |
| }; | |
| static int uart_putchar(char c, FILE *stream) { | |
| softserial_tx_byte(c); | |
| return 0; | |
| } | |
| static FILE mystdout = FDEV_SETUP_STREAM(uart_putchar, NULL, _FDEV_SETUP_WRITE); | |
| static void byte_received_handler(const uint8_t b) { | |
| data_byte = b; | |
| have_data = true; | |
| } | |
| int main(int argc, char **argv) { | |
| have_data = false; | |
| DDRB |= _BV(PB0); | |
| PORTB |= _BV(PB0); | |
| softserial_init(&ssRegs, &byte_received_handler, BAUD_9600); | |
| stdout = &mystdout; | |
| sei(); | |
| puts("READY"); | |
| for (;;) { | |
| if (have_data) { | |
| have_data = false; | |
| softserial_tx_byte(data_byte); | |
| } | |
| } | |
| return 0; | |
| } | |
| ISR(PCINT0_vect) { | |
| // need to figure out which pin changed if more than one PCINT is enabled | |
| // if ((PINB & _BV(PB3)) == 0) { | |
| softserial_handle_pcint(); | |
| // } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "soft_serial.h" | |
| #include <avr/io.h> | |
| #include <stdbool.h> | |
| #include <stdio.h> | |
| #include <avr/interrupt.h> | |
| #include <util/atomic.h> | |
| // ===== data structures and module data | |
| // registers for configuring the RX/TX pins | |
| static const SoftSerialRegisters *_regs; | |
| // callback handler for dispatching received bytes | |
| static void (*byte_received_handler)(uint8_t); | |
| // the baud rate bit period (cpu/8 prescaler only!) | |
| static uint8_t bit_period, rx_initial_bit_period, tx_bit_period, rx_bit_period; | |
| // data structure for managing TX state | |
| typedef struct __tx_state { | |
| volatile bool transmitting; | |
| uint8_t bit_index; // index of bit being transmitted | |
| uint8_t byte_to_tx; // the byte being transmitted | |
| } TxState; | |
| static TxState txState; | |
| // data structure for managing RX state | |
| typedef struct __rx_state { | |
| volatile bool receiving; | |
| uint8_t bit_index; // index of bit being received | |
| uint8_t rx_byte; // the byte being received | |
| } RxState; | |
| static RxState rxState; | |
| // ===== private methods called by OCR1A compare | |
| static inline void tx_tick(void); | |
| static inline void rx_tick(void); | |
| static inline void disable_pcint(void); | |
| static inline void enable_pcint(void); | |
| // ===== public functions | |
| void softserial_init(const SoftSerialRegisters *regs, | |
| void (*_handler)(uint8_t), | |
| const BaudRate br) | |
| { | |
| // disable the timer by clearing the prescaler | |
| TCCR1 &= 0xF0; | |
| // clear the TX flag | |
| txState.transmitting = false; | |
| // clear the RX flag | |
| rxState.receiving = false; | |
| _regs = regs; | |
| byte_received_handler = _handler; | |
| // calculate the base bit period from the baud rate and prescaler | |
| bit_period = (F_CPU / br) / 8; | |
| // calculate the modified bit periods for RX and TX | |
| rx_initial_bit_period = ((bit_period * 3) / 2) - 14; | |
| rx_bit_period = bit_period - 3; | |
| tx_bit_period = bit_period - 3; | |
| // configure the pins | |
| *_regs->pRXDDR &= ~_BV(_regs->rx_port_pin); // rx pin as input | |
| // *_regs->pRXPORT |= _BV(_regs->rx_port_pin); // enable rx internal pull-up | |
| *_regs->pTXDDR |= _BV(_regs->tx_port_pin); // tx pin as output | |
| *_regs->pTXPORT |= _BV(_regs->tx_port_pin); // drive tx pin high (idle) | |
| GIMSK |= _BV(PCIE); // enable pin-change interrupts | |
| enable_pcint(); // enable pcint for rx pin | |
| // reset timer counter | |
| TCNT1 = 0; | |
| // configure OCR1A (TX) | |
| TIFR |= _BV(OCF1A) | _BV(OCF1B); // clear existing compare flags | |
| TIMSK |= _BV(OCIE1A) | _BV(OCIE1B); // enable compare interrupts | |
| OCR1A = tx_bit_period; // set compare value for TX | |
| // no compare value needed for RX | |
| // disable OCR1B (RX) | |
| TIMSK &= ~_BV(OCIE1B); | |
| TIFR |= _BV(OCF1B); // clear existing compare flag | |
| TIMSK |= _BV(OCIE1B); // enable compare interrupt | |
| // start the timer; prescaler to cpu/8 | |
| TCCR1 |= _BV(CS12); | |
| } | |
| void softserial_tx_byte(const uint8_t b) { | |
| // wait for existing operations to complete | |
| while (txState.transmitting); | |
| txState.byte_to_tx = b; | |
| txState.bit_index = 0; | |
| // allow the tx_tick() handler to take over | |
| txState.transmitting = true; | |
| } | |
| inline void softserial_handle_pcint() { | |
| // configure initial timer compare tick for 1.5 times the bit period | |
| OCR1B = TCNT1 + rx_initial_bit_period; | |
| // PORTB &= ~_BV(PB0); | |
| rxState.receiving = true; | |
| // keep PCINTx from firing until receive is done | |
| disable_pcint(); | |
| rxState.bit_index = 0; | |
| rxState.rx_byte = 0; | |
| } | |
| // ===== internal methods | |
| /* | |
| * timer ticked for transmit | |
| */ | |
| static inline void tx_tick() { | |
| // reload OCR1A on every tick | |
| OCR1A = TCNT1 + tx_bit_period; | |
| if (txState.transmitting) { | |
| if (txState.bit_index == 0) { | |
| // PORTB &= ~_BV(PB0); | |
| // start bit is low | |
| *_regs->pTXPORT &= ~_BV(_regs->tx_port_pin); | |
| } else if (txState.bit_index <= 8) { | |
| // (still) transmitting data | |
| if (txState.byte_to_tx & 1) { | |
| // '1' | |
| // *_regs->pTXPORT |= _BV(_regs->tx_port_pin); | |
| PORTB |= _BV(PB4); | |
| } else { | |
| // '0' | |
| // *_regs->pTXPORT &= ~_BV(_regs->tx_port_pin); | |
| PORTB &= ~_BV(PB4); | |
| } | |
| // right-shift byte by 1 | |
| txState.byte_to_tx >>= 1; | |
| } else if (txState.bit_index == 9) { | |
| // throw the stop bit | |
| // *_regs->pTXPORT |= _BV(_regs->tx_port_pin); | |
| PORTB |= _BV(PB4); | |
| } else { | |
| // stop bit's done, shut 'er down | |
| txState.transmitting = false; | |
| // PORTB |= _BV(PB0); | |
| } | |
| txState.bit_index += 1; | |
| } | |
| } | |
| /* | |
| * timer ticked for receive | |
| */ | |
| static inline void rx_tick() { | |
| // reload OCR1B on every tick | |
| OCR1B = TCNT1 + rx_bit_period; | |
| if (rxState.receiving) { | |
| PORTB ^= _BV(PB0); | |
| if (rxState.bit_index <= 7) { | |
| // read the bit value from the PINx register | |
| // if bit is set, left-shift it into place | |
| if (*_regs->pRXPIN & _BV(_regs->rx_port_pin)) { | |
| rxState.rx_byte |= _BV(rxState.bit_index); | |
| } | |
| // increment the bit index | |
| rxState.bit_index += 1; | |
| } else { | |
| // PORTB |= _BV(PB0); | |
| // all bits have been read, this branch got triggered for the stop | |
| // bit, so now we can dispatch the byte and reset to the idle state. | |
| byte_received_handler(rxState.rx_byte); | |
| rxState.receiving = false; | |
| enable_pcint(); | |
| } | |
| } | |
| } | |
| /* | |
| * disable PCINTx to disable receive | |
| */ | |
| static inline void disable_pcint() { | |
| PCMSK &= ~_BV(_regs->rx_pcint_bit); | |
| } | |
| /* | |
| * stops the timer and re-enables the PCINT | |
| */ | |
| static inline void enable_pcint() { | |
| // enable PCINTx to enable receive | |
| PCMSK |= _BV(_regs->rx_pcint_bit); | |
| } | |
| ISR(TIMER1_COMPA_vect) { | |
| tx_tick(); | |
| } | |
| ISR(TIMER1_COMPB_vect) { | |
| rx_tick(); | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #ifndef SOFT_SERIAL_H | |
| #define SOFT_SERIAL_H | |
| #include <stdint.h> | |
| #include "baud_rates.h" | |
| typedef struct __soft_ser_regs { | |
| // rx port, pin, ddr, port number | |
| volatile uint8_t *pRXPORT; | |
| volatile uint8_t *pRXPIN; | |
| volatile uint8_t *pRXDDR; | |
| const uint8_t rx_port_pin; | |
| const uint8_t rx_pcint_bit; | |
| // tx port, ddr, port number | |
| volatile uint8_t *pTXPORT; | |
| volatile uint8_t *pTXDDR; | |
| const uint8_t tx_port_pin; | |
| } SoftSerialRegisters; | |
| /* | |
| * initialization | |
| * | |
| * @param registers configuration registers and data | |
| * @param byte_received_handler handler for received data | |
| * @param baud_rate baud rate | |
| */ | |
| void softserial_init(const SoftSerialRegisters *registers, | |
| void (*byte_received_handler)(uint8_t), | |
| const BaudRate baud_rate); | |
| /* | |
| * transmits a byte | |
| */ | |
| void softserial_tx_byte(const uint8_t); | |
| /* | |
| * initiates reception of a byte. | |
| * must be called from an ISR(PCINTx_vect) provided by the main application. | |
| */ | |
| void softserial_handle_pcint(void); | |
| #endif |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment