Mark Gottscho / UtilityLib

Fork of UtilityLib by Mark Gottscho

Utility.cpp

Committer:
mgottscho
Date:
2014-03-11
Revision:
4:28f50e540872
Parent:
3:87ce0325374d
Child:
5:9cd633b79b23

File content as of revision 4:28f50e540872:

/* Utility.cpp
 * Tested with mbed board: FRDM-KL46Z
 * Author: Mark Gottscho
 * mgottscho@ucla.edu
 */
 
#include "mbed.h"
#include <string>
#include "Utility.h"

using namespace std;

Utility::Utility(PinName green_led_pin, PinName red_led_pin, PinName serial_tx_pin, PinName serial_rx_pin, int baudrate, bool enableSerialInterrupts) :
                                            console(serial_tx_pin, serial_rx_pin),
                                            green_led(green_led_pin),
                                            red_led(red_led_pin),
                                            __interrupts_en(enableSerialInterrupts),
                                            __user_fptr(NULL),
                                            __green_led_interrupt(),
                                            __red_led_interrupt(),
                                            __rx_head(0),
                                            __rx_tail(0),
                                            __tx_head(0),
                                            __tx_tail(0),
                                            __have_rx_serial(false)
                                             {
                                                
    red_led = 1;
    green_led = 1;
    
    console.baud(baudrate); //115200, 230400 confirmed to work, above this seems to fail
    
    //Set up serial interrupt handlers
    if (__interrupts_en) {
        console.attach(this, &Utility::__console_rx_ISR, Serial::RxIrq);
        //console.attach(this, &Utility::__console_tx_ISR, Serial::TxIrq);
    }
}
                                            
Utility::~Utility() {
    detach();
}

void Utility::attach(void (*fptr)(void)) {
    //set user function pointer
    detach();
    if (fptr != NULL)
        __user_fptr = new FunctionPointer(fptr);
}

template<typename T> void Utility::attach(T *tptr, void (T::*mptr)(void)) {
    //set user function pointer
    detach();
    if (tptr != NULL && mptr != NULL)
        __user_fptr = new FunctionPointer(tptr, mptr);
}

void Utility::detach() {
    if (__user_fptr != NULL)
        delete __user_fptr;
    __user_fptr = NULL;
}

void Utility::panic(string errorMessage, int errorCode) {
    //We're dead. This is the point of no return! Permanently ignore interrupts.
    __disable_irq();
    
    console.printf(">>> PANIC, CODE # %d: %s <<<\r\n", errorCode, errorMessage.c_str());
    
    green_led = 1;
    red_led = 0;
    
    volatile uint32_t fake = 0;
    while(1) { fake = 0; } //Spinloop for eternity
}

void Utility::warn(string errorMessage, int errorCode) {
    console.printf(">>> WARN, CODE # %d: %s <<<\r\n", errorCode, errorMessage.c_str());
}

#ifdef NDEBUG
void Utility::myAssert(bool condition, const char *file, const unsigned long line) { }
#else
void Utility::myAssert(bool condition, const char *file, const unsigned long line) {
    if (!condition)
    {
        char msg[256];
        sprintf(msg, "Assertion failed at file %s, line %d", file, line);
        panic(msg, -1); \
    }   
}
#endif

void Utility::blinkGreen(bool enable, float half_period) {
    if (enable) {
        if (half_period <= 0.01) {
            __green_led_interrupt.detach();
            green_led = 0;
        } else if (half_period <= 1800)
            __green_led_interrupt.attach(this, &Utility::__greenLED_ISR, half_period);
    }
    else {
        __green_led_interrupt.detach();
        green_led = 1;   
    }
}

void Utility::blinkRed(bool enable, float half_period) {
    if (enable) {
        if (half_period <= 0.01) {
            __red_led_interrupt.detach();
            red_led = 0;
        } else if (half_period <= 1800)
            __red_led_interrupt.attach(this, &Utility::__redLED_ISR, half_period);
    }
    else {
        __red_led_interrupt.detach();
        red_led = 1;   
    }
}

//THIS DOES NOT WORK YET
/*uint32_t Utility::sendLine(const char *line, const uint32_t len) {
    int i = 0;
    char temp_byte;
    bool buf_empty;
    
    if (line == NULL) //check input
        return 0;

    G_red_led = 0;
    
    // Start critical section - don't interrupt while changing global buffer variables
    __disable_irq();
    buf_empty = (G_tx_head == G_tx_tail);
    
    while (i < len && line[i] != '\r') { //Loop until we have sent the maximum number of characters or we hit a carriage return
        // Wait if tx buffer full
        if ((G_tx_head + 1) % BUFFER_SIZE == G_tx_tail) { //If TX buffer is full, wait.
            // End critical section - need to let interrupt routine empty buffer by sending
            __enable_irq();
            while ((G_tx_head + 1) % BUFFER_SIZE == G_tx_tail) { } //Spinloop until TX buffer is not full
            // Start critical section - don't interrupt while changing global buffer variables
            __disable_irq();
        }
        G_tx_head = (G_tx_head + 1) % BUFFER_SIZE;
        G_tx_buf[G_tx_head] = line[i++];
    }
    
    //Now we have buffered all characters in the line. Trigger the TX serial interrupt
    if (G_console.writeable() && buf_empty) {
        //Write the first byte to get it started
        temp_byte = G_tx_buf[G_tx_tail];
        G_tx_tail = (G_tx_tail + 1) % BUFFER_SIZE;
        
        // Send first character to start tx interrupts, if stopped
        G_console.putc(temp_byte);
    }
    
    // End critical section
    __enable_irq();
    
    G_red_led = 1;
    
    return i;
}*/


uint32_t Utility::receiveLine(char *line, const uint32_t len) {
    int i = 0;
    char lastChar = '\0';
    
    if (line == NULL) //check input
        return 0;

    // Start critical section - don't interrupt while changing global buffer variables
    __disable_uart_irq();
    
    while (i < len && lastChar != '\r') { //Loop until maximum number of characters or a newline symbol
        //Wait for more characters if the rx buffer is empty
        if (__rx_tail == __rx_head) {
            // End critical section - need to allow rx interrupt to get new characters for buffer
            __enable_uart_irq();
            while (__rx_tail == __rx_head) { } //Spinloop until there are some characters
            // Start critical section - don't interrupt while changing global buffer variables
            __disable_uart_irq();
        }
        
        lastChar = __rx_buf[__rx_tail];
        if (lastChar == '\r') //newline symbol
            line[i] = '\0';
        else
            line[i] = lastChar;
        i++;
        __rx_tail = (__rx_tail + 1) % BUFFER_SIZE;
    }
    
    //Clear flag
    __have_rx_serial = false;
    
    // End critical section 
    __enable_uart_irq();
    
    return i;
}

bool Utility::haveRxSerialData() {
    return __have_rx_serial;
}

void Utility::__greenLED_ISR() {
    if (green_led == 0) //Just flip the LED status every time we are called
        green_led = 1;
    else
        green_led = 0;
}

void Utility::__redLED_ISR() {
    if (red_led == 0) //Just flip the LED status every time we are called
        red_led = 1;
    else
        red_led = 0;
}

void Utility::__console_rx_ISR() {
    char tmp;
    
    //Loop while the UART inbound FIFO is not empty and the receiving buffer is not full
    while (console.readable() && (__rx_head != (__rx_tail - 1) % BUFFER_SIZE)) {
        tmp = console.getc(); //read a byte into the buffer from the serial port
        __rx_buf[__rx_head] = tmp;
        __rx_head = (__rx_head + 1) % BUFFER_SIZE;
        if (tmp == '\r')
            __have_rx_serial = true;
    }
    
    if (__user_fptr != NULL) //user callback
        __user_fptr->call();
}

//THIS DOES NOT WORK YET
/*void Utility::__console_tx_ISR() {
    //Loop while the UART outbound FIFO is not full and the transmitting buffer is not empty
    while (G_console.writeable() && (G_tx_tail != G_tx_head)) {
        G_console.putc(G_tx_buf[G_tx_tail]); //write a byte to the serial port from the buffer
        G_tx_tail = (G_tx_tail + 1) % BUFFER_SIZE;  
    }
}*/

inline void Utility::__disable_uart_irq() {
    // Start critical section - don't interrupt with serial I/O
    // Since user specifies UART TX/RX pins, we don't know which we are using, so disable all 3
    NVIC_DisableIRQ(UART0_IRQn);
    NVIC_DisableIRQ(UART1_IRQn);
    NVIC_DisableIRQ(UART2_IRQn);   
}

inline void Utility::__enable_uart_irq() {
    // End critical section - can now interrupt with serial I/O
    // Since user specifies UART TX/RX pins, we don't know which we are using, so enable all 3
    NVIC_EnableIRQ(UART0_IRQn);
    NVIC_EnableIRQ(UART1_IRQn);
    NVIC_EnableIRQ(UART2_IRQn);   
}