Skip to content

Instantly share code, notes, and snippets.

@blalor
Created September 15, 2011 01:40
Show Gist options
  • Select an option

  • Save blalor/1218310 to your computer and use it in GitHub Desktop.

Select an option

Save blalor/1218310 to your computer and use it in GitHub Desktop.
ATtiny85 pin crosstalk?
#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();
// }
}
#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();
}
#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