Skip to content

Instantly share code, notes, and snippets.

@malja
Last active May 12, 2023 09:03
Show Gist options
  • Save malja/9ef92b0180a8f5666483666ec7b52db6 to your computer and use it in GitHub Desktop.
Save malja/9ef92b0180a8f5666483666ec7b52db6 to your computer and use it in GitHub Desktop.
MSP430FR50431 millis() implementation

Implementation of millis() function froom Arduino on MSP430FR50431 processor

This gits implements function millis() known from Arduino ecosystem. What it does is that it counts number of milliseconds since the program started. It uses timer TA3, set to UP mode with custom TA3CCR0 value.

When user runs function millis(), it returns value from the counter.

Timer TA3

This timer is set to UP mode. It means that the timer counts up to the value stored in TA3CCR0 register.

When the value stored in TA3CCR0 is reached, an interrupt is generated. In interrupt service routine, counter TIMER_MS_COUNT storing number of ms since the program started is incremented.

Since TA3 is sourced from SMCLK (which itself is sourced from DCO set to 1MHz) with divider set to 1, it means that each second, timer is incremented by 1 000 000. And each millisecond, timer is incremented by 1 000. This is why setting TA3CCR0 to 1 000 means, that each millisecond, and interrupt will be generated.

UART

UART part of this Gist is just to show, that millis() function is working properly.

Delay

In this Gist, delay() function was implemented in a very naive way. It supports only most used CPU frequencies.

Contributors

Thank you Keith Barkley and Bruce McKenney for helping me figure out why my code did not run.

Surces

  1. https://e2e.ti.com/support/microcontrollers/msp-low-power-microcontrollers-group/msp430/f/msp-low-power-microcontroller-forum/1030645/msp430fr50431-implementing-millis-function/3810669#3810669
  2. http://software-dl.ti.com/trainingTTO/trainingTTO_public_sw/MSP430_LaunchPad_Workshop/v4/Chapters/MSP430m06_TIMERS.pdf
// Note: BIT_SET is macro for REGISTER |= BIT
// Note: BIT_CLEAR is macro for REGISTER &= ~(BIT)
// Number of the milliseconds since the program started
static volatile uint32_t TIMER_MS_COUNT = 0;
// Initialize clock and timer
void clock_init() {
// Set DCO to 1MHz
CS_setDCOFreq(CS_DCORSEL_0,CS_DCOFSEL_0);
// Set external frequency to 32kHz
CS_setExternalClockSource(32768, 0);
// Set the source for SMCLK to DCO with the divider 1
CS_initClockSignal(CS_SMCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1);
// Set the source for MLCK to DCO with the divider 1
CS_initClockSignal(CS_MCLK, CS_DCOCLK_SELECT, CS_CLOCK_DIVIDER_1);
// Reset timer
TA3CTL = 0;
// Reset timer settings
BIT_SET(TA3CTL, TACLR);
// Turn the timer off
BIT_CLEAR(TA3CTL, MC_3);
// Set the clock source to SMCLK (1MHz)
BIT_SET(TA3CTL, TASSEL_2);
// Set divider to 1
BIT_SET(TA3CTL, ID_0);
// Turn of capture mode
BIT_CLEAR(TA3CCTL0, CM);
// Enable compare mode
BIT_CLEAR(TA3CCTL0, CAP);
// Set target value for the interruption
// 1_000_000 hz / 1000 = 1000 ticks per ms
TA3CCR0 = 1000;
// Enable interrupt when reaching CCR0 value
BIT_SET(TA3CCTL0, CCIE);
// Clean interrupt flag, if any
BIT_CLEAR(TA3CCTL0, CCIFG);
// Start the timer in UP mode
BIT_SET(TA3CTL, MC_1);
}
uint32_t millis() {
uint32_t ms = 0;
ms = TIMER_MS_COUNT;
return ms;
}
#pragma vector=TIMER3_A0_VECTOR
__interrupt void millis_isr() {
// Increment the counter
TIMER_MS_COUNT++;
}
// 1_000_000 MHz / 1000 = 1000 ticks per ms
#define MS_TO_CYCLE (1000)
void delay(uint32_t ms) {
const uint8_t cor = DCORSEL;
const uint8_t cof = DCOFSEL;
uint8_t multiplier = 1;
// Note: Supporting only 1, 8, 16, 24 MHz.
if (cor == 0 && cof == 6 || cor == 1 && cof == 3) {
multiplier = 8;
} else if (cor == 1 && cof == 4) {
multiplier = 16;
} else if (cor == 1 && cof == 6) {
multiplier = 24;
}
ms = ms * multiplier;
while(--ms) {
// Počkám zadaný počet cyklů.
__delay_cycles(MS_TO_CYCLE);
}
}
#include <clock.h>
#include <delay.h>
#include <uart.h>
#include <msp430.h>
#include <driverlib.h>
int main() {
WDTCTL = WDTPW + WDTHOLD;
// Enable interrupts, so TIMER ISR is called when needed
__enable_interrupt();
clock_init();
// init UART
uart_init();
// Values for uart
char text[20] = {0};
uint8_t text_length = 0;
while (1) {
memset(text, 0, sizeof(text));
// Requires full prinntf support!
// Project -> Properties -> Build ->> MSP430 Compiler -> Advanced Options -> Language Options
// Set "Level of printf/scanf support required (--printf_support)" to "full"
text_length = sprintf(text, "ms: %lu\n", millis());
uart_write(text, text_length);
delay(3000);
}
}
#include <stdbool.h>
bool uart_init() {
// Set PINs:
// RX: pin 16 (PJ.3)
// TX: pin 15 (PJ.2)
GPIO_setAsPeripheralModuleFunctionInputPin(
GPIO_PORT_PJ,
GPIO_PIN2 + GPIO_PIN3,
GPIO_PRIMARY_MODULE_FUNCTION
);
EUSCI_A_UART_initParam config = {0};
config.selectClockSource = EUSCI_A_UART_CLOCKSOURCE_SMCLK;
// Calculated with: http://software-dl.ti.com/msp430/msp430_public_sw/mcu/msp430/MSP430BaudRateConverter/index.html
config.clockPrescalar = 8;
config.firstModReg = 0;
config.secondModReg = 214;
config.overSampling = EUSCI_A_UART_LOW_FREQUENCY_BAUDRATE_GENERATION;
config.parity = EUSCI_A_UART_NO_PARITY;
config.msborLsbFirst = EUSCI_A_UART_LSB_FIRST;
config.numberofStopBits = EUSCI_A_UART_ONE_STOP_BIT;
config.uartMode = EUSCI_A_UART_MODE;
if (!EUSCI_A_UART_init(EUSCI_A2_BASE, &config)) {
return false;
}
EUSCI_A_UART_enable(EUSCI_A2_BASE); // void
// Clear interrupt flags, if any
EUSCI_A_UART_clearInterrupt(EUSCI_A2_BASE, EUSCI_A_UART_TRANSMIT_COMPLETE_INTERRUPT); // void
return true;
}
void uart_write(uint8_t *data, uint16_t length){
uint16_t index = 0;
for(index = 0; index < length; index++) {
EUSCI_A_UART_transmitData(EUSCI_A2_BASE, data[index]);
// Wait for send to finish
while(!(UCA2IFG & UCTXCPTIFG))
; // do nothing
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment