Created
May 24, 2011 16:20
-
-
Save RickKimball/989034 to your computer and use it in GitHub Desktop.
fabooh core sample code
This file contains 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
/* | |
* 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_ */ |
This file contains 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
/* | |
* 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_ */ |
This file contains 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
/** | |
* 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 |
This file contains 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
/** | |
* 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 */ |
This file contains 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
/** | |
* 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