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;
}
}
}
}