STM32F4 AmpLib: I2C Example, DS3231 Real Time Clock

GitHub Link

So it turns out I2C was one of my pain points for a bit. One of my first interactions with a more complex serial communication besides SPI. However, I now have a working project using an I2C peripheral, namely the DS3231 RTC module.

For those of you wanting to get ahold of that hardware, there are several examples on Adafruit or eBay if you want to save money while taking on some minor risk and don’t mind waiting, which is what I did.

This also marks the first module example for expanding the AmpLib project into a bit of a Board Support Package. It ultimately didn’t make sense to me to have AmpLib reduce all the code I write in example when the hardware interfaces require complex peripheral interactions and can be made completely reusable. You’ll see the code written for mod_ds3231.c here.

Some of you may notice that the board for the DS3231 I’m using also has another IC, namely an AT24C32 EEPROM from Atmel, and yes, that will likely be the next module I add driver code for.

Posted in stm32f4-amperture-periphlib | Leave a comment

STM32F4 AmpLib: SPI Example, Shift Register

This page will detail one usage of the stm32f4-amperture-periphlib SPI Library in order to drive a 74HC595 Shift Register using the STM32-NUCLEO-F401 board.

Full Library Page

Please click here to find the link and description of the Library package.

To see a working example project using these configurations and code, please click here.

Init Config

First, import spi.c and spi.h into your project accordingly.

A 74HC595 shift register follows these rules:

  • 8 bit register
  • Rising Edge Clocking
  • First Edge is data
  • Receive Only
  • Needs Data “Latch” outside of SPI.

So, according to those rules, we must configure our init function properly.

For this example, we will use the SPI1 peripheral connected to GPIO port B. We will also configure the SPI with the MSTR (Master) and SSM+SSI (Internal Slave Select: 1) options enabled.

Since we do not need MISO functionality, we can disable those settings in the init function using comments, we can use that GPIO pin for a data latch later.

void spiInit(SPI_TypeDef *SPIx, GPIO_TypeDef *GPIOx, uint8_t mosiPin,
        uint8_t misoPin, uint8_t sckPin, uint8_t afMode){

    // Enable RCC APB Register for SPI Peripheral.
    // Uncomment the desired SPI register enable.

    //RCC -> APB1ENR |= RCC_APB1ENR_SPI2EN;
    //RCC -> APB1ENR |= RCC_APB1ENR_SPI3EN;
    //RCC -> APB2ENR |= RCC_APB2ENR_SPI0EN;
    RCC -> APB2ENR |= RCC_APB2ENR_SPI1EN;
    //RCC -> APB2ENR |= RCC_APB2ENR_SPI4EN;

    // Enable RCC AHB Register for GPIO
    // Please replace RCC_AHB1ENR_GPIOxEN, x = (A, B, C, ... ,I)
    RCC -> AHB1ENR |= RCC_AHB1ENR_GPIOBEN;

    // Disable Internal SPI Peripheral Clock.
    SPIx -> CR1     &=  ~(SPI_CR1_SPE);

    SPIx -> CR1     =   (0
                        | SPI_CR1_CPHA  // Clock Phase, 0 = 1st Edge, 1 = 2nd
                        // | SPI_CR1_CPOL  // Clock Polariy, 0=Rising, 1=Falling
                        | SPI_CR1_MSTR  // Master Enable
                        // | SPI_CR1_LSBFIRST  // Lowest Significant Bit First
                        | SPI_CR1_SSM   // Software Slave Management
                        | SPI_CR1_SSI   // Internal Slave Select Bit
                        // | SPI_CR1_RXONLY    // Receive Only
                        // | SPI_CR1_DFF   // Data Frame Format, 0=8-bit, 1=16-bit
                        // | SPI_CR1_CRCNEXT   // CRC Next (TODO:Learn about this)
                        // | SPI_CR1_CRCEN // Enable CRC Calculation
                        // | SPI_CR1_BIDIMODE  // 2-direc data mode, 0=2-line
                        // | SPI_CR1_DIDIOE    // 2-direc Output Enable
                        | (SPI_CR1_BR & (AMP_SPI_BAUD_RATE << 3)) // Baud Rate
                        );

    SPIx -> CR2     =   (0
                        // | SPI_CR2_RXDMAEN   // Receive DMA Buffer Enable
                        // | SPI_CR2_TXDMAEN   // Transmit DMA Buffer Enable
                        // | SPI_CR2_SSOE      // SS Output Enable (Multimaster=0)
                        // | SPI_CR2_ERRIE     // Error Interrupt Enable
                        // | SPI_CR2_RXNEIE    // Receive Buffer Not Empty IntEnab
                        // | SPI_CR2_TXEIE     // Transmit Buffer Empty IntEnable
                        );

    // Re-Enable SPI Peripheral
    SPIx -> CR1 |= SPI_CR1_SPE; 

    // Set GPIO pins to AF, High Speed, NoPull
    GPIOx -> MODER      |=  (0x02 << (2 * mosiPin)) | /*(0x02 << (2 * misoPin))*/
        (0x02 << (2 * sckPin)); 
    GPIOx -> OSPEEDR    |=  (0x03 << (2 * mosiPin)) | (0x03 << (2 * misoPin))
        | (0x03 << (2 * sckPin)); 
    GPIOx -> PUPDR      &=  ~((0x03 << (2 * mosiPin)) | (0x03 << (2 * misoPin))
        | (0x03 << (2 * sckPin)));

    // Set GPIO Pins to Alternate Function Setting
    // Please refer to the datasheet for which numbers to use.
    // http://www.st.com/st-web-ui/static/active/en/resource/technical/document/datasheet/DM00102166.pdf
    // Page 45
    if(mosiPin > 7) GPIOx -> AFR[1] |= (afMode << (4 * (mosiPin - 7)));
    else GPIOx -> AFR[0] |= (afMode << (4 * mosiPin));
    if(misoPin > 7) GPIOx -> AFR[1] |= (afMode << (4 * (misoPin - 7)));
    else GPIOx -> AFR[0] |= (afMode << (4 * misoPin));
    if(sckPin > 7) GPIOx -> AFR[1] |= (afMode << (4 * (sckPin - 7)));
    else GPIOx -> AFR[0] |= (afMode << (4 * sckPin));
}

Now, with our SPI peripheral configured, we need to set up our data latch for the shift register, which we will connect to Port B, pin 4. Thankfully the RCC has already been enabled for that port, so we only need to set the GPIOB.MODER register to set PB4 to output.

// Initialize PB_5 to be the Shift Register Latch
GPIOB -> MODER &= ~(3 << (2*4));
GPIOB -> MODER |= (1 << (2*4));
void shiftRegisterLatch(){
    GPIOB -> ODR           |=   (1 << 4);
    Delay(10);
    GPIOB -> ODR           &=   ~(1 << 4);
    Delay(10);
}
/*
 * ===========================================================================
 *
 *       Filename:  main.c
 *
 *    Description:  Example of SPI peripheral use to drive a 74HC595 Shift
 *                  Register to achieve a crawling light bar.
 *
 *      Libs Used:  SPI
 *
 *        Version:  1.0
 *        Created:  01/12/2016 
 *       Revision:  none
 *       Compiler:  arm-none-eabi-gcc
 *
 * ===========================================================================
 */

#include <stm32f4xx.h>
#include <stdint.h>
#include <spi.h>

void shiftRegisterSendData(uint8_t data){
    spiByteSend(SPI1, data);
}

void Delay(uint32_t nCount){
    for(; nCount != 0; nCount--);
}

void shiftRegisterLatch(){
    GPIOB -> ODR           |=   (1 << 4);
    Delay(10);
    GPIOB -> ODR           &=   ~(1 << 4);
    Delay(10);
}

void initHeartbeat(){
    RCC -> AHB1ENR |= RCC_AHB1ENR_GPIOAEN;
    GPIOA -> MODER = (1 << 10); 
    GPIOA -> ODR &= ~(1 << 5); 
}

int main(void){
    // Initialize SPI1 Clock to be connected to GPIO:
    // SPI1_MOSI      -> PB_5
    // SPI1_SCK       -> PB_3
    // GPIO AF MODE   -> 5
    spiInit(SPI1, GPIOB, 5, 4, 3, 5);


    // Initialize PB_5 to be the Shift Register Latch
    GPIOB -> MODER &= ~(3 << (2*4));
    GPIOB -> MODER |= (1 << (2*4));

    initHeartbeat();

    int j = 1;
    for(;;){
        int i = 0;

        // Turn Heartbeat Light ON
        GPIOB -> ODR           |=   (1 << 4);

        for(i = 0; i < 3; i++){
            shiftRegisterSendData(j);
        }
        
        shiftRegisterLatch();

        // Heartbeat
        GPIOA -> ODR ^= (1 << 5);

        // Delay Function, sets speed of crawling light.
        // NOTE: Watch what happens when you set delay length to 0.
        Delay(300000);

        j = j << 1;
        if (j >= 0xFF) j = 1;
    }
}
Posted in stm32f4-amperture-periphlib | Tagged , , | Leave a comment

MSP430 Binary LED Clock: Code Log 1 — Keeping Time

For the full project logs, you could also see my project page on Hackaday.io, here.


INITIAL SETUP

First up, let’s set up the base environment and initialize the internal timer so that we can actually keep track of time on the MSP430.

#include <msp430.h>
int main(void){
    //Disable Watchdog
    WDTCTL = WDTPW + WDTHOLD;

    // Set clock to 1 MHz
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;
}

This is the base environment we’ll be working with, and should be the base for most MSP430 projects.

So far, we set the MSP430 to a rather slow 1 MHz, because we are using the internal clocks to keep track of time for our RTC (Real Time Clock). The slower the better, since then we can use smaller numbers for the passage of time.


TIMER INITIALIZATION

Now, let’s write the initialization function for the timer peripheral.

void timerInit(void){
    // Enable Timer Interrupts
    TACCTL0 = CCIE;

    // SMCLK, Count to CCR0 Value, Divide by 8
    TA0CTL = TASSEL_2 + MC_1 + ID_3;

    // 1,000,000 / 8 / 25 = 5000
    TACCR0 = 5000;
}

The first line enables the system to perform interrupt functions, which we will set up later.

The second line sets up the way the timer will count:

  • TASSEL_2 defines that the timer clock will be based on SMCLK, sourced from the main clock, which we set to 1 MHz earlier.
  • MC_1 defines that the timer counter register will count up to the value stored in the TACCR0, then flag an interrupt, and reset to 0.
  • ID_3 defines that the timer counter will count up once for every eighth clock cycle of the SMCLK.

This effectively means that the timer counter register will increment at 125 kHz.

The last line defines the TACCR0 register as 5000. Meaning that the Timer Peripheral will call an interrupt at a frequency of (125 kHz / 5000) = 25 Hz.

Finally, we just have to implement this function in our main function during initialization phase.

int main(void){
    //Disable Watchdog
    WDTCTL = WDTPW + WDTHOLD;

    // Set clock to 1 MHz
    BCSCTL1 = CALBC1_1MHZ;
    DCOCTL = CALDCO_1MHZ;

    timerInit();
}

TIMER INTERRUPT

Let’s move on to the actual contents of our timer interrupt.

First, we’re using global variables to store the clock data of our RTC. It’s not the most elegant or safe solution, but hey, good enough for a quick weekend project!

 

#include <msp430.h>

#define RTC_STATE_SECONDS 0
#define RTC_STATE_MINUTES 1
#define RTC_STATE_HOURS 2

volatile uint8_t timerCount = 0;
volatile uint8_t rtcState[3] = {0, 0, 0};

int main(void){
   /* ... */
}

The current state for the RTC is stored in an array of 8-bit values known as rtcState[].

Now we define our actual interrupt function, get ready for a long one.

// Timer Interrupt
#pragma vector TIMER0_A0_VECTOR
interrupt void Timer_A(void){
    timerCount++;
    if (timerCount >= 25){
        timerCount = 0;
        rtcState[RTC_STATE_SECONDS]++; 
        if (rtcState[RTC_STATE_SECONDS] >= 60){
            rtcState[RTC_STATE_SECONDS] = 0;
            rtcState[RTC_STATE_MINUTES]++;
            if (rtcState[RTC_STATE_MINUTES] >= 60){
                rtcState[RTC_STATE_MINUTES] = 0;
                rtcState[RTC_STATE_HOURS]++;
                if (rtcState[RTC_STATE_HOURS] >= 24) {
                    rtcState[RTC_STATE_HOURS] = 0;
                }
            }  
        }
    }
}

Since the timer is flagging an interrupt at 25 Hz, first we increment the timerCount variable. When it reaches 25, we reset it to 0, and increment the rtcState variable denoting the seconds. From there we do the typical cascading check to see if we increment the minutes count, or hours count. Note our use of 24-Hour time keeping, rather than having a seperate variable for AM/PM checking. We can use this as we please later.


I believe that covers most of the functionality for actually having an RTC internal to the MSP430G2553 chip, without relying on any external peripherals.

It’s worth noting that the accuracy of the RTC is not very high, and the clock is likely to drift and be inaccurate. In order to improve accuracy, we can include external hardware, such as a dedicated hardware RTC, like the DS1307, which I may actually include on a future iteration of this project.

Be sure to keep an eye out for the next code log, where I describe the process to manually control and set the clock variables.

Posted in Uncategorized | Leave a comment

Building Our Own Power Supply, Part 1: Laying Down the Ground Rules

Alright, as long as I’m pretending to actually take care of this blog, let’s at least do something useful with it.

Like a project build log!

So, yes, I’m going to take on one of the standard first-timer projects, the hand-made benchtop power supply!

Every electronics lab should be outfitted with a good power supply for running tests on your circuits at different voltage and current inputs, and I’d like to design one myself with the idea in mind that I should be challenging my own abilities to do so.

So, this first post will be covering the ground rules and specs, as says the wise Dave Jones of EEVblog: “If you don’t have specs to work with, it’s gonna be a dogs breakfast.

You may notice that I’m linking to Dave Jones’ own Power Supply Design video, and while I learned a good bit from it, I will be changing my own specs to be more robust, as he reveals later in his video series he would like to be able to battery-power his design in order to be mobile. While this is a cool idea, I think I’d rather stick to something more beefy that will remain at home, for now.

So, on to my requirements:

  • Dual Rail
  • Adjustable Current Control: 0 -> 5A
  • Adjustable Voltage Control: 1.25 -> 24VDC
  • USA Standard Line Voltage Input
  • Transformation* and Rectification* to be handled in design
    • …at least on paper, line voltage is still terrifying, and there are plenty of 30V 10A supplies for plenty cheap to be found on Amazon.

So there we have it, the first rules of my power supply design are written. It should be noted that these rules are to be considered in the “…- of thumb” category, and they can, and likely will be broken as design calls for it.

As a secondary point, the design files will be drawn in KiCAD and project files will be provided on a GitHub repo with a GPLv3 license.

*For those playing the home game, these are two terms that will be explained in more detail later on in the process.

Posted in Uncategorized | Leave a comment

So many random ideas…

Man, I’ve just got so many ideas for projects.

  • Re-write my resume/CV in LaTeX instead of Word/LibreOffice Writer, accompanied with blog.
  • PIC32MX “Launchpad-Like” Development Board, accompanied with build logs and reasoning
  • Home-made bench top power supply + build logs/blog series
  • Consider buying an FPGA dev kit
  • Consider buying an oscilloscope
  • Blog on the unfortunate parts of trying to get a start on being an embedded systems and/or electronics design as opposed to generic “programming”
  • Updates and blogs on partitipation in UTexas Solar Decathlon

I really would like to start taking this blog site a bit more seriously, but let’s see how that pans out.

Posted in Uncategorized | Leave a comment

Live Data Logging from the Silicon Labs Sensor Puck (Data collected via a BeagleBone Black)

It feels a little weird that I’m allowing you all to see the data of my personal bedroom, but hey, it all works.

I’ll eventually do a full write-up, hopefully with fixed formatting so that my code is legible, and since I’ve managed to find and decode the Light Intensity data from the BTLE packets, but for now, here’s a visualization of the raw data being logged.

Big shoutouts to Luke Beno, owner/operator of analog.io, for running this service, based off of SparkFun’s Phant.

Posted in Uncategorized | 1 Comment

Capturing Temperature and Humidity Data from a Silicon Labs Sensor Puck on a BeagleBone Black.

Full Disclosure: Silicon Labs is a benefactor of a current project I am working on, and I was provided with a Sensor Puck for collecting ambient data.

The Silicon Labs Sensor Puck is certainly a neat little piece of kit. It can take readings on Temperature, Humidity, Light Intensity, and UV Radiation all on a board not much bigger than an Arduino Mini, and that’s not counting the Heart Rate Monitor mode, and sends all this data out through Bluetooth 4.0 Low Energy Advertising Packets.

Unfortunately, either I’m bad at finding the documenation, or it simply doesn’t exist at the moment. For those of us with not a whole lot of experience in BTLE, reading the device from anything that isn’t their official mobile app was certainly an exercise. This post will aim to make that process just a bit simpler, or put you on the right track.

For this example you will need:

  • A single board computer running a Debian derivative Linux operating system. For this example, I’m using a BeagleBone Black RevC.
  • A Bluetooth 4.0-capable USB module.

I am assuming you have a working Bluetooth daemon service running on your board, if you do not, then you can find instructions on setting up BlueZ on your single board computer here.

Once you have a running Bluetooth service, you can turn on your Sensor Puck and start sniffing for its advertising packets.

[shell]# hcitool lescan –duplicates &[/shell]

This will have your BeagleBone pulling down all the packets of nearby BTLE devices, but not exactly putting them anywhere.

To read the raw data, we will use hcidump.

[shell]# hcidump –raw[/shell]

You should see data flying in that look something like this.

[raw]> 04 3E 1F 02 01 03 00 XX XX XX XX XX 13 02 01 06 0F FF 35
12 00 11 84 29 1C 02 01 01 05 00 00 1E BF
> 04 3E 1F 02 01 03 00 XX XX XX XX XX 13 02 01 06 0F FF 35
12 00 11 84 29 1C 02 01 01 05 00 00 1E BC
> 04 3E 1F 02 01 03 00 XX XX XX XX XX 13 02 01 06 0F FF 35
12 00 11 84 29 1C 02 01 01 05 00 00 1E C2 [/raw]

Don’t worry so much about the XX‘s, they are broadcasting the device’s MAC address, so I’ve censored them.

This raw hexadecimal data may look scary, but your temperature and humidity are in there, in fact, they’re these four bytes:
[raw]> 04 3E 1F 02 01 03 00 XX XX XX XX XX 13 02 01 06 0F FF 35
12 00 11 84 29 1C 02 01 01 05 00 00 1E C2 [/raw]

The humidity is stored in the bold bytes, and temperature stored in the italics, but there’s something a little odd, the bytes are backwards! To explain better, the current humidity is 0x021C, and temperature is 0x0101. They also are encoded such that the temperature is in tenths of degrees Celcius, and humidity is in tenths of percent, so you must take that into account as well.

So this means that the humidity is 0x021C = 540, divide by ten, and we get 54.0%.

Likewise, temperature is  0x0101 = 257, divide by ten here, and we get 25.7 °C.

So, now that we know where the data is, I’ve written a quick Python script as an example to filter out other devices, as well as decode and print the temperature to a terminal.

[python]import subprocess

proc = subprocess.Popen([‘hcidump –raw’], stdout=subprocess.PIPE, shell=True)
while True:
line = proc.stdout.readline()
if “84 29” in line and “>” not in line:
humidity = (int(line.split()[6], 16) << 8) + int(line.split()[5], 16)
humidity = float(humidity)/10
print “Humidity: %f” %humidity
temp = (int(line.split()[8], 16) << 8) + int(line.split()[7], 16)
temp = float(temp)/10
print “Temperature: %f” %temp[/python]

Running this, you should start to see data like this output on the terminal:
[raw]Humidity:56.000000
Temperature:26.200000
Humidity: 56.000000
Temperature: 26.200000
Humidity: 56.000000
Temperature: 26.200000
Humidity: 56.000000
Temperature: 26.200000
[/raw]

Hopefully this quick-and-dirty example should help you out in collecting data off of the Silicon Labs Sensor Puck in your projects! Keep in mind that when the device is in Heart Rate Reading mode this data will be completely invalid, but it’s only meant to be the first example, sooner or later I should get around to having more robust data and scripts, but this should set you on your way.

Credit and thanks goes out to the #beagle IRC channel on Freenode for helping out with other resources on BTLE.

Posted in Uncategorized | 6 Comments

EE445L Lab: Wireless Communication

Posted in Uncategorized | Leave a comment

LM3S1968 Lab: Custom Tone Generation

Posted in Uncategorized | Tagged , , | Leave a comment