Interrupt handling (Arduino vs. Stm32dunio)

Post here first, or if you can't find a relevant section!
Post Reply
neo_nmik
Posts: 20
Joined: Thu Feb 04, 2021 7:09 pm
Answers: 2

Interrupt handling (Arduino vs. Stm32dunio)

Post by neo_nmik »

Hey guys,

I'm coming from pretty much exclusively Arduino territory as far as microprocessors are concerned, but I've run out of power.

I'm trying to port my Arduino code over to the STM32 family. I've got my board (Just an eBay BluePill) uploaded with the HID 2.2 bootloader, and I've been able to do the whole blink test, and some serial communication, but my main aim is to bring over some code for a 12-bit SPI DAC (MCP4921) as I'm messing around with a synthesiser.

I based my Arduino design off of the Critter and Guitari Arduino Piano (https://github.com/critterandguitari/Ar ... e-Sketches), which is basically using some 8-bit wavetable synthesis, and I've been adding more voices and adding some basic ADSR, but I've obviously run out of processing power.

To the problem! I've had a google around and searched the forum, but I couldn't really find an answer... It's probably more than obvious to those of you who are rarely running these boards.

My code (at the bottom of the post) relies on an ISR (on TIMER2 on the Uno) to calculate and send out the DAC code via SPI and is currently using some Port Manipulation to do this. I'm sure I'm going to have to change the port manipulation stuff, but I'll figure that later.

My first hurdle is the ISR code (along with all the definitions/setup for the timer) spits out:
error: expected constructor, destructor, or type conversion before '(' token
53 | ISR(TIM2_COMPA_vect) {
I'm pretty sure there's a different call out for the timer stuff, and I tried searching around with some terms, but wasn't quite sure what I was looking for. I'm pretty sure the port manipulation stuff will need rewriting (and maybe even just being brought up to digitalWrites or something else now I have more juice?), and I read some stuff about HAL and the DMA, but I'd like to just ease my way into this whole STM32 thing...

As I'm coming from using Arduino's I'm starting off by using Arduino IDE 1.8.13, on MacOS Mojave (10.14.6) and the stm32duino (STM Cores 1.9.0).

I think I'm going to move to the PlatformIO environment, but one thing at a time.

TLDR; adapting interrupts from Arduino to STM32duino.

Cheers for all the help!

Nick

Code: Select all

// Here is the sine wave.  It is a one cycle of sine tone, 255 8 bit samples, in hex values
// you could put any wave form you line in here...
uint8_t sineTable[]={
  0x80,0x83,0x86,0x89,0x8c,0x8f,0x92,0x95,0x98,0x9c,0x9f,0xa2,0xa5,0xa8,0xab,0xae,
  0xb0,0xb3,0xb6,0xb9,0xbc,0xbf,0xc1,0xc4,0xc7,0xc9,0xcc,0xce,0xd1,0xd3,0xd5,0xd8,
  0xda,0xdc,0xde,0xe0,0xe2,0xe4,0xe6,0xe8,0xea,0xec,0xed,0xef,0xf0,0xf2,0xf3,0xf5,
  0xf6,0xf7,0xf8,0xf9,0xfa,0xfb,0xfc,0xfc,0xfd,0xfe,0xfe,0xff,0xff,0xff,0xff,0xff,
  0xff,0xff,0xff,0xff,0xff,0xff,0xfe,0xfe,0xfd,0xfc,0xfc,0xfb,0xfa,0xf9,0xf8,0xf7,
  0xf6,0xf5,0xf3,0xf2,0xf0,0xef,0xed,0xec,0xea,0xe8,0xe6,0xe4,0xe2,0xe0,0xde,0xdc,
  0xda,0xd8,0xd5,0xd3,0xd1,0xce,0xcc,0xc9,0xc7,0xc4,0xc1,0xbf,0xbc,0xb9,0xb6,0xb3,
  0xb0,0xae,0xab,0xa8,0xa5,0xa2,0x9f,0x9c,0x98,0x95,0x92,0x8f,0x8c,0x89,0x86,0x83,
  0x80,0x7c,0x79,0x76,0x73,0x70,0x6d,0x6a,0x67,0x63,0x60,0x5d,0x5a,0x57,0x54,0x51,
  0x4f,0x4c,0x49,0x46,0x43,0x40,0x3e,0x3b,0x38,0x36,0x33,0x31,0x2e,0x2c,0x2a,0x27,
  0x25,0x23,0x21,0x1f,0x1d,0x1b,0x19,0x17,0x15,0x13,0x12,0x10,0x0f,0x0d,0x0c,0x0a,
  0x09,0x08,0x07,0x06,0x05,0x04,0x03,0x03,0x02,0x01,0x01,0x00,0x00,0x00,0x00,0x00,
  0x00,0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x02,0x03,0x03,0x04,0x05,0x06,0x07,0x08,
  0x09,0x0a,0x0c,0x0d,0x0f,0x10,0x12,0x13,0x15,0x17,0x19,0x1b,0x1d,0x1f,0x21,0x23,
  0x25,0x27,0x2a,0x2c,0x2e,0x31,0x33,0x36,0x38,0x3b,0x3e,0x40,0x43,0x46,0x49,0x4c,
  0x4f,0x51,0x54,0x57,0x5a,0x5d,0x60,0x63,0x67,0x6a,0x6d,0x70,0x73,0x76,0x79,0x7c};

// final sample that goes tote DAC
uint16_t sample;

// the two bytes that go to the DAC over SPI
uint8_t dacSPI0;
uint8_t dacSPI1;

// variables for oscillators
uint16_t accumulator = 0;  // large number holds phase
uint8_t index = 0;        // index for wave lookup (the upper 8 bits of the accumulator)
uint16_t oscil = 8;        // oscillator output

#define PP_LED 8   // LED on pocket piano

// holds frequency value used for oscillator in phase steps
// this is an integer proportial to Hertz in the following way:
// frequency  = (FrequencyInHertz * 65536) / SampleRate, here sample rate is 15625
uint32_t frequency = 0;

uint8_t gain = 0xff;      // gain of oscillator

int pitchScale;

void setup(){

  //Timer2 setup  This is the audio rate timer, fires an interrupt at 15625 Hz sampling rate
  TIMSK2 = 1<<OCIE2A;  // interrupt enable audio timer
  OCR2A = 127;
  TCCR2A = 2;               // CTC mode, counts up to 127 then resets
  TCCR2B = 0<<CS22 | 1<<CS21 | 0<<CS20;   // different for atmega8 (no 'B' i think)
  SPCR = 0x50;   // set up SPI port
  SPSR = 0x01;
  DDRB |= 0x2E;       // PB output for DAC CS, and SPI port
  PORTB |= (1<<1);
  //led
  pinMode(PP_LED, OUTPUT);

  Serial.begin(9600);
  Serial.println("hello");
  Serial.println("welcome to synthesizer");
//
  sei();			// global interrupt enable
}


void loop(void)
{
  
  float frequencyInHertz = 440.0;   // use this to hold a frequency in hertz
                                    // normally we wouldn't use floats, and just use integers,
                                    // but it is convinient here to show freqency calculation

  // using formula above, calculate correct oscillator frequency value
  frequency =  (frequencyInHertz * 65536.0) / 15625.0; 
  Serial.println("playing");
}

// timer 2 is audio interrupt timer
ISR(TIM2_COMPA_vect) {
  OCR2A = 127;
  
  // calculate frequency mod
  accumulator = accumulator + frequency;  // add in pith, the higher the number, the faster it rolls over, the more cycles per second
  index = accumulator >> 8;   // use top 8 bits as wavetable index

  oscil = sineTable[index];    // get sample from wave table

  sample = oscil << 8;   // sample format for DAC is 12 bit, left justified

  // format sample for SPI port
  dacSPI0 = sample >> 8;
  dacSPI0 >>= 4;
  dacSPI0 |= 0x30;
  dacSPI1 = sample >> 4;

  // transmit value out the SPI port
  PORTB &= ~(1<<1); // Frame sync low
  SPDR = dacSPI0;
  while (!(SPSR & (1<<SPIF)));
  SPDR = dacSPI1;
  while (!(SPSR & (1<<SPIF)));
  PORTB |= (1<<1); // Frame sync high
}
by ag123 » Thu Feb 04, 2021 10:48 pm
for the official STM core
https://github.com/stm32duino/Arduino_Core_STM32
hardware timer api is here
https://github.com/stm32duino/wiki/wiki ... er-library

libmaple (roger's) core
https://github.com/rogerclarkmelbourne/Arduino_STM32
hardware timer examples look like this
http://docs.leaflabs.com/static.leaflab ... dwaretimer
quoted from the led blink example

Code: Select all

#define LED_RATE 500000    // in microseconds; should give 0.5Hz toggles

// We'll use timer 2
HardwareTimer timer(2);

void setup() {
    // Set up the LED to blink
    pinMode(BOARD_LED_PIN, OUTPUT);

    // Pause the timer while we're configuring it
    timer.pause();

    // Set up period
    timer.setPeriod(LED_RATE); // in microseconds

    // Set up an interrupt on channel 1
    timer.setChannel1Mode(TIMER_OUTPUT_COMPARE);
    timer.setCompare(TIMER_CH1, 1);  // Interrupt 1 count after each update
    timer.attachCompare1Interrupt(handler_led);

    // Refresh the timer's count, prescale, and overflow
    timer.refresh();

    // Start the timer counting
    timer.resume();
}

void loop() {
    // Nothing! It's all in the handler_led() interrupt:
}

void handler_led(void) {
    toggleLED();
}
these are quite similar on the official core as well more examples here
https://github.com/stm32duino/wiki/wiki ... 4-examples

if you are using a board such as the stm32f103 pill boards
do review the specs sheet
https://www.st.com/en/microcontrollers- ... 103c8.html
and reference manual
https://www.st.com/resource/en/referenc ... ronics.pdf
stm32f103 and stm32f4xx etc are little rather complicated mcus with rich on chip pheriperials / hardware, timers (multiple), uart, spi, i2c, adc, dma, rtc, usb etc

it is quite possible to drive a pwm dac using the hardware timers, but u'd need to use the specific pins for the hardware timers and set them up.
To drive a timer isr handler, the above example generally suffice. you can figure out the literal hardware aspects reviewing the specs sheet and manual. and checking the api / codes to see how to use them.
Go to full post
User avatar
Bakisha
Posts: 140
Joined: Fri Dec 20, 2019 6:50 pm
Answers: 5
Contact:

Re: Interrupt handling (Arduino vs. Stm32dunio)

Post by Bakisha »

As for interrupt handler, check
https://github.com/stm32duino/STM32Exam ... llback.ino

As for SPI,

Code: Select all

#include <SPI.h>
void setup() {
   SPI.begin();
  SPI.beginTransaction(SPISettings(36000000, MSBFIRST, SPI_MODE0)); // must be called after SPI.begin() 
}
void loop() {
   SPI.transfer(x);  // send byte
}
will do the job. Around 4uS overhead when sending individual bytes. Some time ago i played with 74HC595 shift registers, and i collected some macros for SPI. You can check it at https://github.com/Bakisha/USCL/blob/ma ... #L121-L209

As for port manipulation, i think digitalWrite is fast enough for your purpose.

You might also check info at:

https://github.com/stm32duino/wiki/wiki/API#spi
https://github.com/stm32duino/wiki/wiki ... er-library
neo_nmik
Posts: 20
Joined: Thu Feb 04, 2021 7:09 pm
Answers: 2

Re: Interrupt handling (Arduino vs. Stm32dunio)

Post by neo_nmik »

Bakisha wrote: Thu Feb 04, 2021 10:08 pm As for interrupt handler, check
https://github.com/stm32duino/STM32Exam ... llback.ino

As for SPI,

Code: Select all

#include <SPI.h>
void setup() {
   SPI.begin();
  SPI.beginTransaction(SPISettings(36000000, MSBFIRST, SPI_MODE0)); // must be called after SPI.begin() 
}
void loop() {
   SPI.transfer(x);  // send byte
}
will do the job. Around 4uS overhead when sending individual bytes. Some time ago i played with 74HC595 shift registers, and i collected some macros for SPI. You can check it at https://github.com/Bakisha/USCL/blob/ma ... #L121-L209

As for port manipulation, i think digitalWrite is fast enough for your purpose.

You might also check info at:

https://github.com/stm32duino/wiki/wiki/API#spi
https://github.com/stm32duino/wiki/wiki ... er-library
This is great info, thank you very much Bakisha!
ag123
Posts: 1668
Joined: Thu Dec 19, 2019 5:30 am
Answers: 25

Re: Interrupt handling (Arduino vs. Stm32dunio)

Post by ag123 »

for the official STM core
https://github.com/stm32duino/Arduino_Core_STM32
hardware timer api is here
https://github.com/stm32duino/wiki/wiki ... er-library

libmaple (roger's) core
https://github.com/rogerclarkmelbourne/Arduino_STM32
hardware timer examples look like this
http://docs.leaflabs.com/static.leaflab ... dwaretimer
quoted from the led blink example

Code: Select all

#define LED_RATE 500000    // in microseconds; should give 0.5Hz toggles

// We'll use timer 2
HardwareTimer timer(2);

void setup() {
    // Set up the LED to blink
    pinMode(BOARD_LED_PIN, OUTPUT);

    // Pause the timer while we're configuring it
    timer.pause();

    // Set up period
    timer.setPeriod(LED_RATE); // in microseconds

    // Set up an interrupt on channel 1
    timer.setChannel1Mode(TIMER_OUTPUT_COMPARE);
    timer.setCompare(TIMER_CH1, 1);  // Interrupt 1 count after each update
    timer.attachCompare1Interrupt(handler_led);

    // Refresh the timer's count, prescale, and overflow
    timer.refresh();

    // Start the timer counting
    timer.resume();
}

void loop() {
    // Nothing! It's all in the handler_led() interrupt:
}

void handler_led(void) {
    toggleLED();
}
these are quite similar on the official core as well more examples here
https://github.com/stm32duino/wiki/wiki ... 4-examples

if you are using a board such as the stm32f103 pill boards
do review the specs sheet
https://www.st.com/en/microcontrollers- ... 103c8.html
and reference manual
https://www.st.com/resource/en/referenc ... ronics.pdf
stm32f103 and stm32f4xx etc are little rather complicated mcus with rich on chip pheriperials / hardware, timers (multiple), uart, spi, i2c, adc, dma, rtc, usb etc

it is quite possible to drive a pwm dac using the hardware timers, but u'd need to use the specific pins for the hardware timers and set them up.
To drive a timer isr handler, the above example generally suffice. you can figure out the literal hardware aspects reviewing the specs sheet and manual. and checking the api / codes to see how to use them.
Last edited by ag123 on Fri Feb 05, 2021 8:14 am, edited 2 times in total.
neo_nmik
Posts: 20
Joined: Thu Feb 04, 2021 7:09 pm
Answers: 2

Re: Interrupt handling (Arduino vs. Stm32dunio)

Post by neo_nmik »

Thanks guys! Super appreciate the help.

I’d tried looking for a wiki but couldn’t dive deeper on it!

I should be able to figure it out from here!

Cheers,

Nick
Post Reply

Return to “General discussion”