Skip to content

Instantly share code, notes, and snippets.

@RickKimball
Created May 24, 2011 16:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save RickKimball/989034 to your computer and use it in GitHub Desktop.
Save RickKimball/989034 to your computer and use it in GitHub Desktop.
fabooh core sample code
/*
* RingBuffer.h - template for a circular buffer
*
* License: Do with this code what you want. However, don't blame
* me if you connect it to a heart pump and it stops. This source
* is provided as is with no warranties. It probably has bugs!!
* You have been warned!
*
* Author: Rick Kimball
* email: rick@kimballsoftware.com
* Version: 1.00 Initial version 05-12-2011
*/
#ifndef RINGBUFFER_H_
#define RINGBUFFER_H_
/**
* RingBuffer - a template based interrupt safe circular buffer structure with access methods
*/
template<typename T, int MAX_ITEMS>
struct RingBuffer {
volatile int head;
volatile int tail;
volatile T buffer[MAX_ITEMS];
inline bool empty();
inline void push_back(T c);
inline T pop_front();
inline T peek_front();
inline uint16_t available();
};
/**
* empty() - checks the buffer for data
*
* returns true if empty, false if there is data
*/
template<typename T, int MAX_ITEMS>
bool RingBuffer<T, MAX_ITEMS>::empty() {
bool isEmpty;
__dint(); // prevent inconsistent reads
isEmpty = (head == tail);
__eint();
return isEmpty;
}
/**
* push_back() - append one value to the buffer is possible
*
* Note: implementation assumes it is called from inside the recv
* interrupt, it doesn't disable/enable global interrupt flag
*
*/
template<typename T, int MAX_ITEMS>
void RingBuffer<T, MAX_ITEMS>::push_back(T c) {
int i = (unsigned int) (head + 1) % MAX_ITEMS;
if (i != tail) {
buffer[head] = c;
head = i;
}
}
/**
* pop_front() - remove a value from front of ring buffer
*/
template<typename T, int MAX_ITEMS>
T RingBuffer<T, MAX_ITEMS>::pop_front() {
T c = -1;
__dint(); // disable interrupts to protect head and tail values
// This prevents the RX_ISR from modifying them
// while we are trying to read and modify
// if the head isn't ahead of the tail, we don't have any characters
if (head != tail) {
c = (T) buffer[tail];
tail = (unsigned int) (tail + 1) % MAX_ITEMS;
}
__eint(); // ok .. let everyone at them
return c;
}
/**
* peek_front() - inspect a value from front of ring buffer
*/
template<typename T, int MAX_ITEMS>
T RingBuffer<T, MAX_ITEMS>::peek_front() {
T c(-1);
__dint(); // disable interrupts to protect head and tail values
// This prevents the RX_ISR from modifying them
// while we are trying to read and modify
// if the head isn't ahead of the tail, we don't have any characters
if (head != tail) {
c = (T) buffer[tail];
}
__eint(); // ok .. let everyone at them
return c;
}
/**
* available() - returns count of unread bytes in buffer
*/
template<typename T, int MAX_ITEMS>
uint16_t RingBuffer<T, MAX_ITEMS>::available() {
uint16_t cnt;
__dint();
cnt = (uint16_t)(MAX_ITEMS + head - tail) % MAX_ITEMS;
__eint();
return cnt;
}
typedef RingBuffer<uint8_t, 8> ringbuffer_ui8_8; // RingBuffer, max of 16 uint8_t values
typedef RingBuffer<uint8_t, 16> ringbuffer_ui8_16; // RingBuffer, max of 16 uint8_t values
typedef RingBuffer<uint8_t, 32> ringbuffer_ui8_32; // RingBuffer, max of 32 uint8_t values
#endif /* RINGBUFFER_H_ */
/*
* SerialT.h - common template for both HW and Software UART interfaces
*
* License: Do with this code what you want. However, don't blame
* me if you connect it to a heart pump and it stops. This source
* is provided as is with no warranties. It probably has bugs!!
* You have been warned!
*
* Author: Rick Kimball
* email: rick@kimballsoftware.com
* Version: 1.00 Initial version 05-12-2011
*/
#ifndef SERIALT_H_
#define SERIALT_H_
namespace fabooh {
template<typename IMPL_T>
struct SerialT {
IMPL_T *impl;
inline bool empty();
inline void init(IMPL_T&);
inline void xmit(uint8_t);
inline void xmit(const char *);
inline uint8_t recv();
};
template<typename IMPL_T>
bool SerialT<IMPL_T>::empty()
{
return impl->empty();
}
template<typename IMPL_T>
void SerialT<IMPL_T>::init(IMPL_T& v)
{
impl = &v;
impl->init();
}
template<typename IMPL_T>
void SerialT<IMPL_T>::xmit(uint8_t c)
{
impl->xmit(c);
}
template<typename IMPL_T>
void SerialT<IMPL_T>::xmit(const char *s)
{
while(*s) {
impl->xmit((uint8_t)*s++);
}
}
template<typename IMPL_T>
uint8_t SerialT<IMPL_T>::recv()
{
return impl->recv();
}
} /* fabooh */
#endif /* SERIALT_H_ */
/**
* SerialT.ipp - implementation files for SerialT
*
* Include this file once in your main.cpp file to implement the
* data structures and interrupt handlers associated with the SerialT.
*
* ENABLE_HW_UART - control usage of the hardware USCI implementation
*/
ringbuffer_ui8_8 rx_buffer = { 0, 0, { 0 } };
#ifdef ENABLE_HW_UART
USCISerial<ringbuffer_ui8_8> serialImpl = { rx_buffer };
fabooh::SerialT<USCISerial<ringbuffer_ui8_8> > Serial;
/**
* USCIAB0RX_ISR - Receive Interrupt Handler
*
* attempt to save the received character in the ring buffer
* if it is full we just ignore the error.
*
*/
interrupt(USCIAB0RX_VECTOR) USCIAB0RX_ISR(void) {
rx_buffer.push_back(UCA0RXBUF);
}
#else
TimerASerial<ringbuffer_ui8_8> serialImpl = { rx_buffer };
fabooh::SerialT<TimerASerial<ringbuffer_ui8_8> > Serial;
#ifndef TA0IV_TACCR1 // provide common names for TIMERA vectors
#define TA0IV_TACCR1 TAIV_TACCR1
#define TIMER0_A1_VECTOR TIMERA1_VECTOR
#define TIMER0_A0_VECTOR TIMERA0_VECTOR
#endif
/**
* SoftSerial_RX_ISR - Receive Interrupt Handler
*
* This ISR works in two modes. It starts out in capture mode
* waiting for the RX line to go from HI to LO indicating a
* start bit from the sender. It then switches to
* compare mode. Each tick, it sets a future time to wake up and
* sample the RX line. The sample is done by calculating
* the time for the center of the data bit for the given
* baud rate and waking up and taking a sample at that time.
* Once the stop bit is received it goes back into
* capture mode waiting for the next start bit.
*
* Note: serial data is LSB first
*
*/
interrupt(TIMER0_A1_VECTOR) SoftSerial_RX_ISR(void) {
static uint8_t rxBitCnt = 8;
static uint8_t rxData = 0;
if ( TAIV == TA0IV_TACCR1 ) {
TACCR1 += TICKS_PER_BIT; // Setup next time to sample
if (TACCTL1 & CAP) { // Is this the start bit?
TACCTL1 &= ~CAP; // Switch capture to compare mode
TACCR1 += TICKS_PER_BIT_DIV2; // Sample from the middle of D0
}
else {
rxData >>= 1;
if (TACCTL1 & SCCI) { // Get bit waiting in receive latch
rxData |= 0x80;
}
rxBitCnt--;
if (rxBitCnt == 0) { // All bits RXed?
rx_buffer.push_back(rxData); // Store in ring_buffer
rxBitCnt = 8; // Re-load bit counter
TACCTL1 |= CAP; // Switch compare to capture mode
TACCR1 += TICKS_PER_BIT; // account for the stop bit
}
}
}
}
/**
* SoftSerial_TX_ISR - TX Interrupt Handler
*
* Handle the sending of a data byte with one
* start bit + 8 data bits + stop bit.
*
*/
interrupt(TIMER0_A0_VECTOR) SoftSerial_TX_ISR(void) {
static uint8_t txBitCnt = 10; // 1 Start bit + 8 data bits + 1 stop bit
if (txBitCnt == 0) { // All bits TXed?
TACCTL0 &= ~CCIE; // disable interrupt, used as a flag to SoftSerial_xmit
txBitCnt = 10; // Re-load bit counter
} else {
TACCR0 += TICKS_PER_BIT; // setup next time to send a bit
if (serialImpl.UARTTXBUF & 0x01) { // look at LSB and decide what to send
TACCTL0 &= ~OUTMOD2; // TX Mark '1'
} else {
TACCTL0 |= OUTMOD2; // TX Space '0'
}
serialImpl.UARTTXBUF >>= 1; // pull in the next bit to send
txBitCnt--;
}
}
#endif
/**
* TimerASerial.h - use TimerA to implement a software UART
*
* This code implements interrupt driven input and poll driven output.
*
* License: Do with this code what you want. However, don't blame
* me if you connect it to a heart pump and it stops. This source
* is provided as is with no warranties. It probably has bugs!!
* You have been warned!
*
* Author: Rick Kimball
* email: rick@kimballsoftware.com
* Version: 1.00 Initial version 05-12-2011
*/
#ifndef TIMERA_SERIAL_H
#define TIMERA_SERIAL_H
//------------------------------------------------------------
// TX/RX PINS - you can't really change these as we use the
// latch feature of CCR1. Different chips might have more
// options
//------------------------------------------------------------
#define TX_PIN BIT1 // TX Data on P1.1 (Timer0_A.OUT0)
#define RX_PIN BIT2 // RX Data on P1.2 (Timer0_A.CCI1A)
#define TICKS_PER_BIT ( F_CPU / BAUD_RATE ) // timer clock ticks per bit
#define TICKS_PER_BIT_DIV2 (( F_CPU / (BAUD_RATE*2) ) ) // timer clock ticks per half bit
template<typename T_STORAGE>
struct TimerASerial {
T_STORAGE &_recv_buffer;
volatile unsigned int UARTTXBUF; // Software UART TX data
inline void init();
inline uint8_t available(void) {
return _recv_buffer.available();
}
inline bool empty() {
return _recv_buffer.empty();
}
inline int recv() {
return _recv_buffer.pop_front();
}
void xmit(uint8_t c);
};
/**
* init - setup the USCI UART hardware for 9600-8-N-1
*
* P1.1 = RX PIN
* P1.2 = TX PIN
*/
template<typename T_STORAGE>
void TimerASerial<T_STORAGE>::init() {
P1OUT |= TX_PIN + RX_PIN;
P1SEL |= TX_PIN + RX_PIN;
P1DIR |= TX_PIN;
TACCTL0 = OUT;
TACCTL1 = SCS + CM1 + CAP + CCIE;
TACTL = TASSEL_2 + MC_2 + TACLR;
}
template<typename T_STORAGE>
void TimerASerial<T_STORAGE>::xmit(uint8_t c) {
// TIMERA0 disables the interrupt flag when it has sent
// the final stop bit. While a transmit is in progress the
// interrupt is enabled
while (TACCTL0 & CCIE) {
; // wait for previous xmit to finish
}
// make the next output at least TICKS_PER_BIT in the future
// so we don't stomp on the the stop bit from our previous xmt
TACCR0 = TAR; // resync with current TimerA clock
TACCR0 += TICKS_PER_BIT; // setup the next timer tick
TACCTL0 = OUTMOD0 + CCIE; // set TX_PIN HIGH and reenable interrupts
// now that we have set the next interrupt in motion
// we quickly need to set the TX data. Hopefully the
// next 2 lines happens before the next timer tick.
// Note: This code makes great use of multiple peripherals
//
// In the code above, we start with a busy wait on the CCIE
// interrupt flag. As soon as it is available, we setup the next
// send time and then enable the interrupt. Until that time happens,
// we have a few free cycles available to stuff the start and stop bits
// into the data buffer before the timer ISR kicks in and handles
// the event. Note: if you are using a really slow clock or a really
// fast baud rate you could run into problems if the interrupt is
// triggered before you have finished with the USARTTXBUF
UARTTXBUF = c; // load our psuedo register used by the TIMERA0_VECTOR
UARTTXBUF |= 0x100; // Add the stop bit '1'
UARTTXBUF <<= 1; // Add the start bit '0'
}
#endif /* TIMERA_SERIAL_H */
/**
* USCISerial.h - use the USCI Hardware UART
*
* This code implements interrupt driven input and poll driven output.
* Received characters are placed in a circular buffer if there is
* room available.
*
* P1.1 = RX PIN
* P1.2 = TX PIN
*
* License: Do with this code what you want. However, don't blame
* me if you connect it to a heart pump and it stops. This source
* is provided as is with no warranties. It probably has bugs!!
* You have been warned!
*
* Author: Rick Kimball
* email: rick@kimballsoftware.com
* Version: 1.00 Initial version 05-12-2011
*/
#ifndef USCI_SERIAL_H
#define USCI_SERIAL_H
template<typename T_STORAGE>
struct USCISerial {
T_STORAGE &_recv_buffer;
inline void init();
inline bool empty(void);
inline int recv(void);
void xmit(uint8_t c);
inline uint8_t available(void);
};
/**
* init - setup the USCI UART hardware for 9600-8-N-1
*
* P1.1 = RX PIN
* P1.2 = TX PIN
*/
template<typename T_STORAGE>
void USCISerial<T_STORAGE>::init() {
P1SEL = BIT1 | BIT2; // P1.1=RXD, P1.2=TXD
P1SEL2 = BIT1 | BIT2; // P1.1=RXD, P1.2=TXD
UCA0CTL1 |= UCSSEL_2; // use SMCLK for USCI clock
UCA0BR0 = 130; // 16MHz 9600
UCA0BR1 = 6; // 16MHz 9600
UCA0MCTL = UCBRS1 | UCBRS0; // Modulation UCBRSx = 3
UCA0CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
IE2 |= UCA0RXIE; // Enable USCI0RX_ISR interrupt
}
template<typename T_STORAGE>
uint8_t USCISerial<T_STORAGE>::available(void) {
return _recv_buffer.available();
}
template<typename T_STORAGE>
bool USCISerial<T_STORAGE>::empty(void) {
return _recv_buffer.empty();
}
template<typename T_STORAGE>
int USCISerial<T_STORAGE>::recv(void) {
return _recv_buffer.pop_front();
}
template<typename T_STORAGE>
void USCISerial<T_STORAGE>::xmit(uint8_t c) {
while (!(IFG2 & UCA0TXIFG)) {
; // busywait until USCI_A0 TX buffer is ready?
}
UCA0TXBUF = (uint8_t) c; // TX -> RXed character
}
#endif /* USCI_SERIAL_H */
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment