Bluepill project transferring to Blackpill

Post here all questions related to STM32 core if you can't find a relevant section!
Post Reply
Tardishead
Posts: 12
Joined: Sun Dec 27, 2020 10:07 am

Bluepill project transferring to Blackpill

Post by Tardishead »

Hi there
I downloaded a Bluepill project and I can't work out why this won't work out on a Blackpill STM401
It is a quadrature LFO for analogue synthesisers
I have verified my connections to the OLED and the SPI DAC - both work fine
Is it something to do with the Timer commands? Sorry I am a bit of a novice. Learning fast though!
Can someone help

Code: Select all

/*
   Ed Droog
   18-01-2021

   explanation belongs to schematic.
   The main two components are the experiment board known as Blue Pill, which holds a STM32F103C8T6 processor
   and an AD5664 4 channel, 16 bits Digital to Analog Converter.
   The four outputs of the DAC are buffered and sent to a connector placed on the front panel.
   Also they go via four 2k resistors to four bi-color LED's (RED and GREEN) which are connected with their cathode to a 2.5 volt source.
   So if the output of one of the DAC channels reach a voltage of about 3 volts, the corresponding LED lights up green, below 2 volt the
   LED will light up RED.
   SW1 and SW2 form a rotary encoder to travel thru the menu where SW1 is the encoder part and SW2 is the pushbutton activated by pressing the shaft.

*/
#include <Arduino.h>
#include <math.h>
#include <U8g2lib.h>
#include <SPI.h>
#include <Wire.h>
#include "sinlookup.h"
#define SPI_CS0 PA4
#define SPI_CLK PA5
#define SPI_MOSI PA7
#define sinewave      0
#define trianglewave  1
#define sawwave       2
#define squarewave    3
#define quad_one        0
#define quad_two        1
#define quad_three      2
#define quad_four       3
volatile uint8_t DACchannel1, DACchannel2, DACchannel3, DACchannel4;
volatile uint8_t display_row = 1;
volatile bool rotate_right, switched, rotated;
volatile uint8_t column, which_channel, active_channel;
volatile uint16_t SAWtemp, dacdata, SAW;
bool turned = false;
bool pushed = false;
bool left = false;
bool next = false;
bool chan_change = false;
bool wave_change = false;
bool gain_change = false;
bool timing_change = false;
volatile bool timer_tick = true;
volatile uint16_t timer_value = 10;
int prescalefactor = 0xFF;
HardwareTimer timer(TIM2);
const float pi = 3.14159267;
void overflowISR();

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0,/* reset=*/ PB5, /* clock=*/ 42, /* data=*/ 43);

void encoderISR()
{
  turned = true;
}

void encoderButtonISR()
{
  switched = true;
}

void overflowISR()
{
  timer_tick = true;
}

float dacgain[5] = {
  0.00, 0.00, 0.00, 0.00, 0.00
};

uint8_t wavearray[4][4] = {
  {1 , 2, 3, 4 },
  {5, 6, 7, 8 },
  {9, 10, 11, 12},
  {13, 14, 15, 16}
};

uint8_t addquadrant[] = {
  0, 0, 0, 0, 0
};

// Lookup table Small-waves 4*32 points
// Max. amplitude =  16
uint8_t smallookup [128] = {
  //start sine wave
  8, 10, 11, 12, 14, 15, 15, 16,
  16, 16, 15, 15, 14, 12, 11, 10,
  8, 6, 5, 4, 2, 1, 1, 0,
  0, 0, 1, 1, 2, 4, 5, 6,
  //start triangle wave
  8, 9, 10, 11, 12, 13, 14, 15,
  16, 15, 14, 13, 12, 11, 10, 9,
  8, 7, 6, 5, 4, 3, 2, 1,
  0, 1, 2, 3, 4, 5, 6, 7,
  //start saw wave
  8, 8, 9, 9, 10, 10, 11, 11,
  12, 12, 13, 13, 14, 14, 15, 15,
  0, 0, 1, 1, 2, 2, 3, 3,
  4, 4, 5, 5, 6, 6, 7, 7,
  //start square wave
  16, 16, 16, 16, 16, 16, 16, 16,
  0, 0, 0, 0, 0, 0, 0, 0,
  16, 16, 16, 16, 16, 16, 16, 16,
  0, 0, 0, 0, 0, 0, 0, 0
};

uint8_t channel1 [4] = {1, 2, 3, 4}; //startup values sinewave
uint8_t channel2 [4] = {4, 1, 2, 3}; //all 90 degrees
uint8_t channel3 [4] = {3, 4, 1, 2}; //shifted
uint8_t channel4 [4] = {2, 3, 4, 1};

uint16_t get_dacdata (uint8_t part_of_signal)
{
  switch (part_of_signal) {
    //sinewave
    case 1:
      dacdata = pgm_read_word (&sinlookup[SAW]);    //1st quadrant
      break;
    case 2:
      dacdata = pgm_read_word (&sinlookup[4095 - SAW]);  //2nd quadrant
      break;
    case 3:
      dacdata = 65535 - pgm_read_word (&sinlookup[SAW]);  //3rd quadrant
      break;
    case 4:
      dacdata = 65535 - pgm_read_word (&sinlookup[4095 - SAW]); //4th quadrant
      break;
    //trianglewave
    case 5:
      dacdata = 32768 + (8 * SAW); //1st quadrant
      break;
    case 6:
      dacdata = 65535 - (SAW * 8); //2nd quadrant
      break;
    case 7:
      dacdata = 32768 - (SAW * 8); //3rd quadrant
      break;
    case 8:
      dacdata = SAW * 8;  //4th quadrant
      break;
    //sawwave
    case 9:
      dacdata = 32768 + (4 * SAW); //1st quadrant
      break;
    case 10:
      dacdata = 49152 + (4 * SAW); //2nd quadrant
      break;
    case 11:
      dacdata = SAW * 4;        //3rd quadrant
      break;
    case 12:
      dacdata = 16384 + (4 * SAW); //4th quadrant
      break;
    //squarewave
    case 13:
      dacdata = 65535;    //1st quadrant
      break;
    case 14:
      dacdata = 65535;    //2nd quadrant
      break;
    case 15:
      dacdata = 0;        //3rd quadrant
      break;
    case 16:
      dacdata = 0;        //4th quadrant
      break;
    default:
      dacdata = 32768;
      break;
  }
  return dacdata;
}

/*
  void ext_triggerISR()
  {
  // set your trigger flag here
  }
*/

void setup_welcome_screen (void)
{
  u8g2.firstPage();
  do {
    for (uint8_t n = 0; n < 32; n++)
    {
      u8g2.drawPixel(n, smallookup[n]);
      u8g2.drawPixel(n, 30 + smallookup[32 + n]);
      u8g2.drawPixel(80 + n, smallookup[64 + n]);
      u8g2.drawPixel(80 + n, 30 + smallookup[96 + n]);
    }
    u8g2.setFont(u8g2_font_Born2bSportySlab_tr);
    u8g2.drawStr(8, 30, "QUADRO");
  } while ( u8g2.nextPage() );
}

void setup_working_screen (uint8_t display_channel) {
  uint8_t q = addquadrant[display_channel] * 8; // 1-sine,2=tri,3=saw,4=sqr
  u8g2.firstPage();
  do {
    for (uint8_t n = 0; n < 32; n++)
    {
      u8g2.drawPixel(112 - n, smallookup[n + q]);
    }
    u8g2.setFont(u8g2_font_crox1cb_tr);
    if (wave_change) {
      u8g2.drawStr(0, 14, "*");
    };
    u8g2.drawStr(8, 14, " Wave:");
    if (next) {
      u8g2.drawStr(0, 28, "*");
    };
    u8g2.drawStr(8, 28, " Channel:");
    u8g2.setCursor(90, 28);
    u8g2.print(display_channel);
    if (gain_change) {
      u8g2.drawStr(0, 40, "*");
    };
    u8g2.drawStr(8, 40, " Gain: ");
    u8g2.setCursor(90, 40);
    u8g2.print(dacgain[display_channel]);
    if (timing_change) {
      u8g2.drawStr(0, 54, "*");
    };
    u8g2.drawStr(8, 54, " Timing: ");
    u8g2.setCursor(90, 54);
    u8g2.print(timer_value - 10);
  } while ( u8g2.nextPage() );
}

void analog_out (uint8_t channel_to_write, uint16_t analog_data)
{
  // writing to the DAC
  // take the SS pin low to select the chip:
  digitalWrite(SPI_CS0, LOW);
  //  send in the address and value via SPI:
  SPI.transfer(channel_to_write);
  SPI.transfer(analog_data >> 8);
  SPI.transfer(analog_data & 0xFF);
  // take the SS pin high to de-select the chip:
  digitalWrite(SPI_CS0, HIGH);//DAC done
  //SPI.endTransaction();
}

void setup(void)
{
  timer.pause();
  timer.setPrescaleFactor(prescalefactor);
  timer.setOverflow(timer_value);
  timer.refresh();
  timer.attachInterrupt(4, overflowISR);
  pinMode(PB5, OUTPUT);//SCL1 Display
  pinMode(PB6, OUTPUT);//SCL1 Display
  pinMode(PB7, OUTPUT);//SDA1 Display
  pinMode(PB13, INPUT_PULLUP);//rotary encoder
  pinMode(PB14, INPUT_PULLUP);//rotary encoder
  pinMode(PB15, INPUT_PULLUP);//rotary pusbutton
  pinMode(SPI_CS0, OUTPUT);
  u8g2.begin();
  pinMode(PA4, OUTPUT);//Chip Select DAC
  pinMode(PA5, OUTPUT);//CLK
  pinMode(PA7, OUTPUT);//MOSI
  attachInterrupt(digitalPinToInterrupt(PB14),  encoderISR,      FALLING);  //call encoderISR()   every high->low change
  attachInterrupt(digitalPinToInterrupt(PB15), encoderButtonISR, FALLING); //call pushButtonISR() every high->low change
  rotated = false;
  switched = false;
  digitalWrite(SPI_CS0, HIGH);
  delay(10);
  digitalWrite(SPI_CS0, HIGH);
  SPISettings settingsA(70000000, MSBFIRST, SPI_MODE2);
  SPI.beginTransaction(settingsA);
  analog_out(0x30, 0x000F); // LDAC setup DAC
}

void loop(void) {
  setup();
  setup_welcome_screen();
  int n = 0;
  uint16_t dacvalue = 0;
  active_channel = 1;
  next = true;
  timer.resume();//start the interrupt by timer2
  while (1) {
    for (uint8_t quadrant_number = 0; quadrant_number < 4; quadrant_number++)
    {
      for (SAW = 0; SAW < 4095; SAW++)
      {
        /*
           To ensure all DAC channels output their data at the same time
           the last write to the DAC is #13 to produce a software LDAC
           (see table 7 datasheet page 16 of 23)

           Table 7.
           Command Definition C2 C1 C0 Command
           0 0 0 Write to input register n
           0 0 1 Update DAC register n
           0 1 0 Write to input register n, update all (software LDAC)
           0 1 1 Write to and update DAC channel n
           1 0 0 Power down DAC (power-up) 1 0 1 Reset
           1 1 0 Load LDAC register
           1 1 1 Reserved

        */
        dacvalue = dacgain[1] * ((int)get_dacdata (channel1[quadrant_number] + addquadrant[1]));
        analog_out(0x10, dacvalue); //write data to channel 1
        dacvalue = dacgain[2] * ((int)get_dacdata (channel2[quadrant_number] + addquadrant[2]));
        analog_out(1, dacvalue); //write data to channel 2
        dacvalue = dacgain[3] * ((int)get_dacdata (channel3[quadrant_number] + addquadrant[3]));
        analog_out(2, dacvalue); //write data to channel 3
        dacvalue = dacgain[4] * ((int)get_dacdata (channel4[quadrant_number] + addquadrant[4]));

        if (switched) {
          if (wave_change & switched) {
            next = true;
            wave_change = false;
            gain_change = false;
            timing_change = false;
            setup_working_screen (active_channel);
            switched = false;
          }
          if (next & switched) {
            next = false;
            wave_change = false;
            gain_change = true;
            timing_change = false;
            setup_working_screen (active_channel);
            switched = false;
          }
          if (gain_change & switched) {
            next = false;
            wave_change = false;
            gain_change = false;
            timing_change = true;
            setup_working_screen (active_channel);
            switched = false;
          };
          if (timing_change & switched) {
            next = false;
            wave_change = true;
            gain_change = false;
            timing_change = false;
            setup_working_screen (active_channel);
            switched = false;
          };
        };
        if (turned) {
          if (turned & next) {
            if (GPIOB->IDR & (1 << 13)) {
              active_channel++; if (active_channel > 4) {
                active_channel = 1;
              };
            }
            else
            { active_channel--; if (active_channel < 1) {
                active_channel = 4;
              };
            }
          }
          if (turned & wave_change)
          {
            addquadrant[active_channel] += 4;
            if (addquadrant[active_channel] > 15) {
              addquadrant[active_channel] = 0;
            }
          }
          if (turned & gain_change)
          {
            if (GPIOB->IDR & (1 << 13)) {
              dacgain[active_channel] += 0.05; if (dacgain[active_channel] > 1) {
                dacgain[active_channel] = 1;
              }
            }
            else
            {
              dacgain[active_channel] -= 0.05; if (dacgain[active_channel] < 0.05) {
                dacgain[active_channel] = 0;
              };
            }
          };

          if (turned & timing_change)
          {
            if (GPIOB->IDR & (1 << 13)) {
              if (timer_value < 100) {
                timer_value += 1;
              }
              else timer_value += 10;
            }
            else
            {
              if (timer_value > 100) {
                timer_value -= 10;
              }
              else timer_value -= 1;
              if (timer_value < 10) {
                timer_value = 10;
              }
            }
            timer.pause();
            timer.setPrescaleFactor(0xff);
            timer.setOverflow(timer_value);
            timer.refresh();
            timer.resume();
          };
          setup_working_screen (active_channel);
          turned = false;
        }
        while (!timer_tick);
        analog_out(0x13, dacvalue); //write data to channel 4 and update all channels
        timer_tick = false;
      }
    }
  }
}
User avatar
Bakisha
Posts: 139
Joined: Fri Dec 20, 2019 6:50 pm
Answers: 5
Contact:

Re: Bluepill project transferring to Blackpill

Post by Bakisha »

- Better re-check pinout for you board. On STM32F401CC TIM5 is connected to PA0/1/2/3. TIM2 is alternate function, not default.

You could compare to bluepill:
F401 https://github.com/stm32duino/Arduino_C ... L_F401CC.c
F103c8 https://github.com/stm32duino/Arduino_C ... eralPins.c

- What's max SPI speed for your DAC?
Your

Code: Select all

SPISettings settingsA(70000000, MSBFIRST, SPI_MODE2);
will set actual SPI speed at 38000000 at bluepill, but on F401 will be 42000000.

- Also,

Code: Select all

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0,/* reset=*/ PB5, /* clock=*/ 42, /* data=*/ 43);
did you mean:

Code: Select all

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0,/* reset=*/ PB5, /* clock=*/ PB6, /* data=*/ PB7);
Tardishead
Posts: 12
Joined: Sun Dec 27, 2020 10:07 am

Re: Bluepill project transferring to Blackpill

Post by Tardishead »

Bakisha wrote: Wed Sep 15, 2021 9:39 pm - Better re-check pinout for you board. On STM32F401CC TIM5 is connected to PA0/1/2/3. TIM2 is alternate function, not default.

You could compare to bluepill:
F401 https://github.com/stm32duino/Arduino_C ... L_F401CC.c
F103c8 https://github.com/stm32duino/Arduino_C ... eralPins.c

- What's max SPI speed for your DAC?
Your

Code: Select all

SPISettings settingsA(70000000, MSBFIRST, SPI_MODE2);
will set actual SPI speed at 38000000 at bluepill, but on F401 will be 42000000.

- Also,

Code: Select all

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0,/* reset=*/ PB5, /* clock=*/ 42, /* data=*/ 43);
did you mean:

Code: Select all

U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0,/* reset=*/ PB5, /* clock=*/ PB6, /* data=*/ PB7);
So is it that if I am using a Hardware Timer I cannot use the pins associated with the Timer unless it is for the appropriate timer function
So if the encoder pins coincide with Timer 2 pins I will have to move to pins not used by Timer 2?
In which case in the sketch my encoder uses PB14 which is T2_CH2N. So I need to not have the encoder on this pin right?
Or use a timer with no complimentary pins? Like Timer 9 for instance?

Also I am not sure of the syntax used for Hardware Timer library. Does it look correct to you?
I am using this core https://github.com/stm32duino/Arduino_Core_STM32
I have used

Code: Select all

HardwareTimer timer(TIM2);
but should I use this

Code: Select all

  HardwareTimer *timer= new HardwareTimer(2);
and is this ok

Code: Select all

            timer.setPrescaleFactor(0xff);
or should it be

Code: Select all

            timer->setPrescaleFactor(0xff);
Does this depend on what core you are using? And if so I can't find the documentation for this

Code: Select all

SPISettings settingsA(70000000, MSBFIRST, SPI_MODE2);
I am not understanding this. I thought you set the SPI speed with 70000000. Why is that not standard across all boards?

The U8G2 constructor works fine with 42,43 as opposed to PB6,PB7. I checked with other sketches
User avatar
Bakisha
Posts: 139
Joined: Fri Dec 20, 2019 6:50 pm
Answers: 5
Contact:

Re: Bluepill project transferring to Blackpill

Post by Bakisha »

Sorry, my mistake. Just because you posted in forum's section for STM32 core , i assumed you want to use STM32 core.
Anyway, you are right, it doesn't matter what timer are you using, since it is only for interrupt, not for PWM on a pin.

Code: Select all

SPISettings settingsA(70000000, MSBFIRST, SPI_MODE2);
will set 8MHz on Arduino UNO, 38MHz on bluepill, 42Mhz on 84Mhz blackpill. It is hardware specific what is maximum speed of SPI port. But it also doesn't matter in your case, because ( i checked), maximum speed for your DAC is 50MHz.

Try some examples for U8g2 library only (without timers, encoders, DACs...).
Tardishead
Posts: 12
Joined: Sun Dec 27, 2020 10:07 am

Re: Bluepill project transferring to Blackpill

Post by Tardishead »

ok thanks
Can you check my syntax for the hardware timer class
I am not sure what is correct
see my last post

U2G8 OLED is working fine I have checked on other sketches
User avatar
Bakisha
Posts: 139
Joined: Fri Dec 20, 2019 6:50 pm
Answers: 5
Contact:

Re: Bluepill project transferring to Blackpill

Post by Bakisha »

For timer (interrupt only):

Code: Select all

HardwareTimer *timer = new HardwareTimer(TIM5);

uint16_t  prescalefactor = 0xFF;
volatile uint16_t timer_value = 10;
volatile bool timer_tick = true;

void overflowISR()
{
  timer_tick = true;
}

void setup() {

  timer->pause();
  timer->setPrescaleFactor(prescalefactor);
  timer->setOverflow(timer_value, TICK_FORMAT);
  timer->attachInterrupt(overflowISR);
  timer->resume();
}

void loop() {
}
I recommend replacing

Code: Select all

GPIOB->IDR & (1 << 13)
with

Code: Select all

digitalRead(PB13)
You might want to check examples at https://github.com/stm32duino/STM32Examples
and you could see core's capabilities with timers at https://github.com/stm32duino/Arduino_C ... areTimer.h
Post Reply

Return to “General discussion”