Carbide module reference

Header :
#include <carbide.h>
Makefile :
CARBIDE=true

Description

This module defines helpers to make development on the Carbide board quicker.

You should start your main() function by calling Carbide::init(true) (instead of Core::init()). This will :

  • initialize the microcontroller
  • switch the CPU to a 12MHz clock
  • initialize the LEDs and button pins (PA00, PA01, PA02 and PA04)
  • register default error handlers which will blink the red LED in case something goes wrong (see the Error module)
  • if true was passed as an argument, the USB port will be initialized with a handler allowing the board to automatically reset into bootloader mode when you type make upload

The autoBootloaderReset parameter (enabled by passing true to init() as explained above) is a very handy feature allowing you to quickly upload and test each iteration of your program without the need to reset the board manually each time. However, if your program needs to use the USB port, avoid conflict by disabling this feature (remove true) and implementing the handler in your own code (look at the definition of usbControlHandler()).

Here is a simple example that uses the Carbide module to set up the board and flash the LEDs :

#include <carbide.h>
#include <core.h>

int main() {
    // Initialize the board
    Carbide::init();

    // Blink the three LEDs alternatively
    while (true) {
        // Turn the blue LED off, and the red LED on
        Carbide::setLedB(false);
        Carbide::setLedR();
        Core::sleep(200);

        // Red off, green on
        Carbide::setLedR(false);
        Carbide::setLedG();
        Core::sleep(200);
        
        // Green off, blue on
        Carbide::setLedG(false);
        Carbide::setLedB();
        Core::sleep(200);
    }
}

API

void init(bool autoBootloaderReset=false)
Initialize the board as explained above.
void setLedR(bool on=true)
Turn on or off the red LED.
void setLedG(bool on=true)
Turn on or off the green LED.
void setLedB(bool on=true)
Turn on or off the blue LED.
bool isButtonPressed()
Return true if the button is currently being pressed.
void onButtonPressed(void (*handler)(), bool released=false)
Register a handler to be called by interrupt when the button is either pressed (by default) or released (by passing true as the second argument).
bool buttonRisingEdge()
Return true if the button has been pressed since the last call to this function (you must call this function at regular interval to not miss an input, see the the Polling paragraph in the GPIO module reference for more information).
bool buttonFallingEdge()
Return true if the button has been released since the last call to this function (you must call this function at regular interval to not miss an input, see the the Polling paragraph in the GPIO module reference for more information).
void waitButtonPressed()()
Block execution until the button is pressed.
void setCPUFrequency(CPUFreq frequency)
Set the CPU frequency to one of these values by automatically starting and selecting an appropriate clock :
  • FREQ_4MHZ
  • FREQ_8MHZ
  • FREQ_12MHZ
  • FREQ_24MHZ
  • FREQ_36MHZ
  • FREQ_48MHZ
void initLedR()
Initialize as output the PA00 pin on which the red LED is connected. This is called in init().
void initLedG()
Initialize as output the PA01 pin on which the green LED is connected. This is called in init().
void initLedB()
Initialize as output the PA02 pin on which the blue LED is connected. This is called in init().
void initLeds()
Initialize the three LEDs.
void initButton()
Initialize as input the PA04 pin on which the button is connected. This is called in init().
void warningHandler(Error::Module module, int userModule, Error::Code code)
Internal handler defined to be called when an error of warning level arises. It will blink the red LED three times and return.
void criticalHandler(Error::Module module, int userModule, Error::Code code)
Internal handler defined to be called when an error of critical level arises. It will blink the red LED continuously and block the execution.

Code

Header

#ifndef _CARBIDE_H_
#define _CARBIDE_H_

#include <gpio.h>

#if PACKAGE != 64
#warning "You are compiling for the Carbide with an incorrect package, please set PACKAGE=64 in your Makefile"
#endif

namespace Carbide {

    // Pins definition
    const GPIO::Pin PIN_LED_R = GPIO::PA00;
    const GPIO::Pin PIN_LED_G = GPIO::PA01;
    const GPIO::Pin PIN_LED_B = GPIO::PA02;
    const GPIO::Pin PIN_BUTTON = GPIO::PA04;

    // Predefined CPU frequencies
    enum class CPUFreq {
        FREQ_4MHZ,
        FREQ_8MHZ,
        FREQ_12MHZ,
        FREQ_24MHZ,
        FREQ_36MHZ,
        FREQ_48MHZ
    };

    // Helper functions
    void init(bool autoBootloaderReset=false);
    void setCPUFrequency(CPUFreq frequency);
    void warningHandler(Error::Module module, int userModule, Error::Code code);
    void criticalHandler(Error::Module module, int userModule, Error::Code code);
    inline void initLedR() { GPIO::enableOutput(PIN_LED_R, GPIO::HIGH); }
    inline void setLedR(bool on=true) { GPIO::set(PIN_LED_R, !on); } // Inverted : pin must be LOW to turn the LED on
    inline void initLedG() { GPIO::enableOutput(PIN_LED_G, GPIO::HIGH); }
    inline void setLedG(bool on=true) { GPIO::set(PIN_LED_G, !on); }
    inline void initLedB() { GPIO::enableOutput(PIN_LED_B, GPIO::HIGH); }
    inline void setLedB(bool on=true) { GPIO::set(PIN_LED_B, !on); }
    inline void initLeds() { initLedR(); initLedG(); initLedB(); }
    inline void initButton() { GPIO::enableInput(PIN_BUTTON, GPIO::Pulling::PULLUP); }
    inline bool isButtonPressed() { return !GPIO::get(PIN_BUTTON); } // Inverted : the pin is LOW when the button is pressed (pullup)
    void onButtonPressed(void (*handler)(), bool released=false);
    inline bool buttonRisingEdge() { return GPIO::fallingEdge(PIN_BUTTON); } // Rising/falling are also inverted for the same reasons
    inline bool buttonFallingEdge() { return GPIO::risingEdge(PIN_BUTTON); }
    inline void waitButtonPressed() { while (!buttonRisingEdge()); }

}

#endif

Module

#include "carbide.h"
#include <core.h>
#include <error.h>
#include <scif.h>
#include <pm.h>
#include <bpm.h>
#include <gpio.h>
#include <usb.h>

namespace Carbide {

    // USB request codes
    enum class Request {
        START_BOOTLOADER,
        CONNECT,
        STATUS,
        WRITE,
        GET_ERROR,
    };

    // Handler called when a CONTROL packet is sent over USB
    int usbControlHandler(USB::SetupPacket &lastSetupPacket, uint8_t* data, int size) {
        Request request = static_cast<Request>(lastSetupPacket.bRequest);
        if (request == Request::START_BOOTLOADER) {
            lastSetupPacket.handled = true;
            Core::resetToBootloader(10);
        }
        return 0;
    }

    // Handler called when an error of Warning level is triggered
    void warningHandler(Error::Module module, int userModule, Error::Code code) {
        // Blink the red LED three times and resume operation
        setLedR(false);
        Core::sleep(100);
        setLedR();
        Core::sleep(100);
        setLedR(false);
        Core::sleep(100);
        setLedR();
        Core::sleep(100);
        setLedR(false);
        Core::sleep(100);
    }

    // Handler called when an error of Critical level is triggered
    void criticalHandler(Error::Module module, int userModule, Error::Code code) {
        // Stop the execution and blink the red LED rapidly
        while (1) {
            setLedR();
            Core::sleep(100);
            setLedR(false);
            Core::sleep(100);
        }
    }

    void init(bool autoBootloaderReset) {
        // Init the microcontroller on the default 12MHz clock
        Core::init();
        setCPUFrequency(CPUFreq::FREQ_12MHZ);

        if (autoBootloaderReset) {
            // Init the USB port start the bootloader when requested
            USB::initDevice();
            USB::setControlHandler(usbControlHandler);
        }

        // Init the leds and button
        initLeds();
        initButton();

        // Set error handlers
        Error::setHandler(Error::Severity::WARNING, warningHandler);
        Error::setHandler(Error::Severity::CRITICAL, criticalHandler);
    }

    void setCPUFrequency(CPUFreq frequency) {
        switch (frequency) {
            case CPUFreq::FREQ_4MHZ:
                SCIF::enableRCFAST(SCIF::RCFASTFrequency::RCFAST_4MHZ);
                PM::setMainClockSource(PM::MainClockSource::RCFAST);
                break;

            case CPUFreq::FREQ_8MHZ:
                SCIF::enableRCFAST(SCIF::RCFASTFrequency::RCFAST_8MHZ);
                PM::setMainClockSource(PM::MainClockSource::RCFAST);
                break;

            case CPUFreq::FREQ_12MHZ:
                SCIF::enableRCFAST(SCIF::RCFASTFrequency::RCFAST_12MHZ);
                PM::setMainClockSource(PM::MainClockSource::RCFAST);
                break;

            case CPUFreq::FREQ_24MHZ:
                SCIF::enableDFLL(24000000UL);
                PM::setMainClockSource(PM::MainClockSource::DFLL);
                break;

            case CPUFreq::FREQ_36MHZ:
                BPM::setPowerScaling(BPM::PowerScaling::PS2);
                SCIF::enableDFLL(36000000UL);
                PM::setMainClockSource(PM::MainClockSource::DFLL);
                break;

            case CPUFreq::FREQ_48MHZ:
                BPM::setPowerScaling(BPM::PowerScaling::PS2);
                SCIF::enableDFLL(48000000UL);
                PM::setMainClockSource(PM::MainClockSource::DFLL);
                break;
        }

        // Wait 100ms to make sure the clocks have stabilized
        Core::sleep(100);
    }

    // Enable an handler to be called by interrupt when the button the button is
    // either pressed or realeased
    void onButtonPressed(void (*handler)(), bool released) {
        GPIO::enableInterrupt(PIN_BUTTON, handler, (released ? GPIO::Trigger::RISING : GPIO::Trigger::FALLING));
    }

}