STM32F429 Discovery SPI Registers - cpu-registers

I am trying to use the STM32F429 Discovery board in order to communicate in a transmit only mode to an LCD over SPI. However, every time I write to the data register to output, I see nothing get loaded in my debugging view (CrossStudio).
int main(void) {
int j;
SET_BIT(RCC->AHB1ENR,(RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIOAEN) );
MODIFY_REG(GPIOC->MODER,
(GPIO_MODER_MODER10 | GPIO_MODER_MODER12) ,
(GPIO_MODER_MODER10_1 | GPIO_MODER_MODER12_1) ); // Sets to alternate function
SET_BIT(GPIOC->OTYPER, (GPIO_OTYPER_OT_10 | GPIO_OTYPER_OT_12)); // Set output to push-pull type
CLEAR_BIT(GPIOC->PUPDR, (GPIO_PUPDR_PUPDR10 | GPIO_PUPDR_PUPDR12) ); // Disable push pull resistors
SET_BIT(GPIOC->OSPEEDR, (GPIO_OSPEEDER_OSPEEDR10 | GPIO_OSPEEDER_OSPEEDR12) ); // Set speed high
/* Set the Alternate Function for the SPI3 pins */
// Port C Pins 7-0
GPIOC->AFR[0] = 0x00000000; // PTC lower order pins are default
// Port C Pins 15-8
GPIOC->AFR[1] = 0x00066600; // PTC pins 10, 11, and 12 are for SPI (AF6 = 0x6)
// Set up SS for screen
MODIFY_REG(GPIOA->MODER, GPIO_MODER_MODER4 , GPIO_MODER_MODER4_0 );
SET_BIT(GPIOA->BSRRL, 0b10000); //Sets pin 4 high
// Enable the peripheral clock for SPI3
SET_BIT(RCC->APB1ENR, RCC_APB1ENR_SPI3EN);
CLEAR_BIT(SPI3->CR1, SPI_CR1_CPHA);
CLEAR_BIT(SPI3->CR1, SPI_CR1_CPOL);
//SET_BIT(SPI3->CR1, SPI_CR1_BIDIMODE);
//SET_BIT(SPI3->CR1, SPI_CR1_BIDIOE);
SET_BIT(SPI3->CR1, SPI_CR1_SSM);//
SET_BIT(SPI3->CR1, SPI_CR1_SSI);
SET_BIT(SPI3->CR1, SPI_CR1_MSTR); // Set Master configuration
/* TODO: Set Baud to 2 MHz (not just a random prescaler) */
MODIFY_REG(SPI3->CR1, SPI_CR1_BR, SPI_CR1_BR); //Set to fCLK/256
CLEAR_BIT(SPI3->CR1, SPI_CR1_LSBFIRST); // write out MSB first
SET_BIT(SPI3->CR1, SPI_CR1_SPE); // Enable SPI3
transmitData[0]= MODE_CLEAR;
transmitData[1]=0x00;
for(j = 0; j < 2; j++) {
//TM_SPI_Send(SPI3, transmitData[j]);
SPI3->DR = transmitData[j]; // Write data to the data register
while( !(READ_BIT(SPI3->SR, SPI_SR_TXE)) ); // Wait until transmit is complete
while( READ_BIT(SPI3->SR, SPI_SR_BSY) );
}
SET_BIT(GPIOA->BSRRH, 0b10000); //Sets pin 4 low
Pin 4 is my chip select pin, and my oscilloscope shows that is working just fine. I just cant get anything out of the SPI pins.

Turns out I had the output accidentally set to open drain, used a SET_BIT when I needed CLEAR_BIT
CLEAR_BIT(GPIOC->OTYPER, (GPIO_OTYPER_OT_10 | GPIO_OTYPER_OT_12)); // Set output to push-pull type

Related

DFPlayer not responding to myDFPlayer.playFolder instruction

I am trying to complete an arduino project which uses RFID cards to prompt a DFPlayer mini to play tracks. (The original project is called Juuke).
I have near-on everything working but am having a problem calling the DFPlayer to play from specific folders. It seems the playFolder instruction does not work at all (neither does the playLargeFolder or the playMP3folder). Currently the only instructions I seem to be able to get it to respond to are myDFPlayer.play and myDFPlayer.loopFolder
The project is supposed to work as follows:
each RFID card is programmed with a number,
when you scan and RFID card, it reads the number
it plays the mp3 track from the SDcard with that number (4-digit file name prefixed with zeros)
As far as I can tell the myDFPlayer.play instruction is working, but that seems to play the file based on what order it was added to the SD card, rather than by reading the file name?
The actual situation I am trying account for is that some of the mp3 files are 'singles' so it only needs to play one track. This works fine with the myDFPlayer.play instruction (as long as they were added to the SDcard in the order expected by the filename).
However, other mp3 files are 'albums' so when I scan the RFID I'd like it to open a folder with a group of mp3 files, and loop through all the files in that folder once before stopping.
My plan is to have all the 'singles' in folder with the name 80, if the RFID card number is above 80, the DFPlayer will open folder 80 and play the file with the corresponding filename to the number of the RFID card e.g. if I present RFID card '93', the DFPlayer will open folder 80 and play file 0093.mp3
All the folders below 80 will be 'album folders'. So if I present an RFID card with number below 80 , the DFPlayer will open the folder with the corresponding file name and play track 1 before looping through each file in that folder once, then stopping. e.g. if I present RFID card '42', the DFPlayer will open Folder 42 and play track 1.
Currently, I cannot get the DFPlayer to respond to the instruction myDFPlayer.playFolder in any format. Neither does it respond to myDFPlayer.playMP3Folder or myDFPlayer.playLargeFolder.
Things I have considered and accounted for...
I am adding the mp3 files from my Mac, I am using dot_clean to clean the SD card before putting it into the DFPlayer.
I have tried every variation of folder and file names (4-digit zero prefixed e.g. 0034; 3-digit zero prefixed e.g. 034; folders with 2digit zero prefixed e.g. 07) Nothing seems to work.
Rather than use the number from the RFID card I have just hard coded it to play a specific folder whatever card is there (i.e. myDFPlayer.playFolder(2,1) and the DFPlayer does not respond.
Moving to a Windows machine, formatting the SD card (exFAT is the only format option I get?) an re-adding the mp3 files.
putting the mp3 files in a root file labelled 'mp3'.
having only files on the SD card that are labelled 01 , 02 , 03 etc
The serial monitor shows that the code is working fine except for the bit where the DFPlayer is supposed to find the folder and play the file. There is a small LED on the DF Player that seems to indicate when it is playing a file, that LED does not turn on at all when the playFolder instruction is being given.
This is the code... I have tried a lot of variations in the //PLAY SONG section.
/*
Thanks to Original Author: ryand1011 (https://github.com/ryand1011)
Edited by Ananords -
See: https://github.com/miguelbalboa/rfid/tree/master/examples/rfid_write_personal_data
Uses MIFARE RFID card using RFID-RC522 reader
Uses MFRC522 - Library
-----------------------------------------------------------------------------------------
MFRC522 Arduino Arduino Arduino Arduino Arduino
Reader/PCD Uno/101 Mega Nano v3 Leonardo/Micro Pro Micro
Signal Pin Pin Pin Pin Pin Pin
-----------------------------------------------------------------------------------------
RST/Reset RST 9 5 D9 RESET/ICSP-5 RST
SPI SS SDA(SS) 10 53 D10 10 10
SPI MOSI MOSI 11 / ICSP-4 51 D11 ICSP-4 16
SPI MISO MISO 12 / ICSP-1 50 D12 ICSP-1 14
SPI SCK SCK 13 / ICSP-3 52 D13 ICSP-3 15
PS: IF THE PLAYER FAILS TO START WHEN THE SERIAL MONITOR IS NOT OPEN, TRY TO COMMENT OUT Serial.begin(115200);
*/
#include <SPI.h>
#include <MFRC522.h>
#include "Arduino.h"
#include "SoftwareSerial.h"
#include "DFRobotDFPlayerMini.h"
#define RST_PIN 9 // Configurable, see typical pin layout above
#define SS_PIN 10 // Configurable, see typical pin layout above
const int playPauseButton = 4;
const int shuffleButton = 3;
const byte volumePot = A0;
int prevVolume;
byte volumeLevel = 0; //variable for holding volume level
boolean isPlaying = false;
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance
SoftwareSerial mySoftwareSerial(5, 6); // RX, TX
DFRobotDFPlayerMini myDFPlayer;
void printDetail(uint8_t type, int value);
//*****************************************************************************************//
void setup() {
Serial.begin(115200); // Initialize serial communications with the PC, COMMENT OUT IF IT FAILS TO PLAY WHEN DISCONNECTED FROM PC
mySoftwareSerial.begin(9600);
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card
pinMode(playPauseButton, INPUT_PULLUP);
pinMode(shuffleButton, INPUT_PULLUP);
Serial.println(F("Initializing DFPlayer ... (May take 3~5 seconds)"));
if (!myDFPlayer.begin(mySoftwareSerial)) { //Use softwareSerial to communicate with mp3.
Serial.println(F("Unable to begin:"));
Serial.println(F("1.Please recheck the connection!"));
Serial.println(F("2.Please insert the SD card!"));
}
Serial.println(F("DFPlayer Mini online. Place card on reader to play a specific song"));
//myDFPlayer.volume(15); //Set volume value. From 0 to 30
volumeLevel = map(analogRead(volumePot), 0, 1023, 0, 30); //scale the pot value and volume level
myDFPlayer.volume(volumeLevel);
prevVolume = volumeLevel;
//----Set different EQ----
myDFPlayer.EQ(DFPLAYER_EQ_NORMAL);
// myDFPlayer.EQ(DFPLAYER_EQ_POP);
// myDFPlayer.EQ(DFPLAYER_EQ_ROCK);
// myDFPlayer.EQ(DFPLAYER_EQ_JAZZ);
// myDFPlayer.EQ(DFPLAYER_EQ_CLASSIC);
// myDFPlayer.EQ(DFPLAYER_EQ_BASS);
}
//*****************************************************************************************//
void loop() {
volumeLevel = map(analogRead(volumePot), 0, 1023, 0, 30); //scale the pot value and volume level
if (prevVolume != volumeLevel){
myDFPlayer.volume(volumeLevel);
}
prevVolume = volumeLevel;
// Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
MFRC522::MIFARE_Key key;
for (byte i = 0; i < 6; i++) key.keyByte[i] = 0xFF;
//some variables we need
byte block;
byte len;
MFRC522::StatusCode status;
if (digitalRead(playPauseButton) == LOW) {
if (isPlaying) {
myDFPlayer.pause();
isPlaying = false;
Serial.println("Paused..");
}
else {
isPlaying = true;
myDFPlayer.start();
Serial.println("Playing..");
}
delay(500);
}
if (digitalRead(shuffleButton) == LOW) {
myDFPlayer.next();
Serial.println("next Play");
isPlaying = true;
delay(1000);
}
//-------------------------------------------
// Reset the loop if no new card present on the sensor/reader. This saves the entire process when idle.
if ( mfrc522.PICC_IsNewCardPresent()) {
// Select one of the cards
if ( ! mfrc522.PICC_ReadCardSerial()) {
return;
}
Serial.println(F("**Card Detected:**"));
//-------------------------------------------
mfrc522.PICC_DumpDetailsToSerial(&(mfrc522.uid)); //dump some details about the card
//mfrc522.PICC_DumpToSerial(&(mfrc522.uid)); //uncomment this to see all blocks in hex
//-------------------------------------------
Serial.print(F("Number: "));
//---------------------------------------- GET NUMBER AND PLAY THE SONG
byte buffer2[18];
block = 1;
len = 18;
status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, 1, &key, &(mfrc522.uid)); //line 834
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Authentication failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
status = mfrc522.MIFARE_Read(block, buffer2, &len);
if (status != MFRC522::STATUS_OK) {
Serial.print(F("Reading failed: "));
Serial.println(mfrc522.GetStatusCodeName(status));
return;
}
//PRINT NUMBER
String number = "";
for (uint8_t i = 0; i < 16; i++)
{
number += (char)buffer2[i];
}
number.trim();
Serial.print(number);
//PLAY SONG
if (number.toInt()>79){
myDFPlayer.playFolder(80, number.toInt());
isPlaying = true;
delay(1000);
}
else {
myDFPlayer.playFolder(number.toInt(), 1);
isPlaying = true;
delay(1000);
}
//----------------------------------------
Serial.println(F("\n**End Reading**\n"));
delay(1000); //change value if you want to read cards faster
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
}
}
//*****************************************************************************************//
I have mentioned above a range of things I have tried, nothing seems to work. I am a beginner so I am not sure what there is left that could be the problem!

setting right ADC prescaler on the Arduino Due in timer and interrupt driven multi-channel ADC acquisition

I am trying to follow, adapt, understand (and clean up a bit) a variation around the code available there, for the Arduino Due: https://forum.arduino.cc/index.php?topic=589213.0 . I do not like the forum format, as things end up buried deep, so asking here instead. Unfortunately this means that there is quite a lot of explanations before the question. If you think this is wrong to post it here, let me know, and I can move.
Basically, the idea is to log several ADC channels in a buffer, using timer-based triggering. There is a bit of setup:
// sample rate in Hz
constexpr int sample_rate = 1000;
constexpr uint8_t channels[] = {7, 6, 5, 4, 3};
constexpr int nbr_channels = sizeof(channels);
Then time counter 0 channel 2 is set at the right frequency for triggering the ADC conversion:
// use time counter 0 channel 2 to generate the ADC start of conversion signal
// i.e. this sets a rising edge with the right frequency for triggering ADC conversions corresponding to sample_rate
// for more information about the timers: https://github.com/ivanseidel/DueTimer/blob/master/TimerCounter.md
// NOTE: TIOA2 should not be available on any due pin https://github.com/ivanseidel/DueTimer/issues/11
void tc_setup() {
PMC->PMC_PCER0 |= PMC_PCER0_PID29; // TC2 power ON : Timer Counter 0 channel 2 IS TC2
TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK2 // clock 2 has frequency MCK/8, clk on rising edge
| TC_CMR_WAVE // Waveform mode
| TC_CMR_WAVSEL_UP_RC // UP mode with automatic trigger on RC Compare
| TC_CMR_ACPA_CLEAR // Clear TIOA2 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA2 on RC compare match
constexpr int ticks_per_sample = F_CPU / 8 / sample_rate; // F_CPU / 8 is the timer clock frequency, see MCK/8 setup
constexpr int ticks_duty_cycle = ticks_per_sample / 2; // duty rate up vs down ticks over timer cycle; use 50%
TC0->TC_CHANNEL[2].TC_RC = ticks_per_sample;
TC0->TC_CHANNEL[2].TC_RA = ticks_duty_cycle;
TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable
}
Finally this can be used to trigger the ADC:
// start ADC conversion on rising edge on time counter 0 channel 2
// perform ADC conversion on several channels in a row one after the other
// report finished conversion using ADC interrupt
void adc_setup() {
PMC->PMC_PCER1 |= PMC_PCER1_PID37; // ADC power on
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR |= ADC_MR_TRGEN_EN | // Hardware trigger select
ADC_MR_PRESCAL(1) | // the pre-scaler: as high as possible for better accuracy, while still fast enough to measure everything
// see: https://arduino.stackexchange.com/questions/12723/how-to-slow-adc-clock-speed-to-1mhz-on-arduino-due
ADC_MR_TRGSEL_ADC_TRIG3; // Trigger by TIOA2 Rising edge
ADC->ADC_IDR = ~(0ul);
ADC->ADC_CHDR = ~(0ul);
for (int i = 0; i < nbr_channels; i++)
{
ADC->ADC_CHER |= ADC_CHER_CH0 << channels[i];
}
ADC->ADC_IER |= ADC_IER_EOC0 << channels[nbr_channels - 1];
ADC->ADC_PTCR |= ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS; // Disable PDC DMA
NVIC_EnableIRQ(ADC_IRQn); // Enable ADC interrupt
}
and the ADC output can be captured in the corresponding ISR:
void ADC_Handler() {
for (size_t i = 0; i < nbr_channels; i++)
{
SOME_BUFFER[i] = static_cast<volatile uint16_t>( * (ADC->ADC_CDR + channels[i]) & 0x0FFFF ); // get the output
}
}
I think this is quite understandable, but I have one question: the setting of the pre-scaler.
if I understand well discussions online, the pre-scaler should be set so that frq_ADC >= sample_rate * nbr_channels, basically because the chip is just multiplexing the ADC through several channels
if I understand well, we want to set such pre-scaler value as high as possible given the previous constraint, so that the ADC frequency is as low as possible, because this improves ADC conversion quality
Is that right?
The problem is that I am confused about how to set the pre-scaler, and what value corresponds to what, because what I find in the datasheet disagree with some other online responses I read.
From the datasheet https://ww1.microchip.com/downloads/en/DeviceDoc/Atmel-11057-32-bit-Cortex-M3-Microcontroller-SAM3X-SAM3A_Datasheet.pdf : "The ADC clock range is between MCK/2, if PRESCAL is 0, and MCK/512, if PRESCAL is set to 255 (0xFF).". This is consistent with what I find on page 1334: "ADCClock = MCK / ( (PRESCAL+1) * 2 )". But page 1318, it is written that the conversion rate is 1MHz. Then how is that compatible with having a MCK frequency of 84MHz on the Due? 84/2 = 48MHz, 84/512 = 0.164MHz, the high freq value is too high.
Then to add to the confusion I have found this issue: https://arduino.stackexchange.com/questions/12723/how-to-slow-adc-clock-speed-to-1mhz-on-arduino-due/21054#21054 that also seem to conflict with the 1MHz upper bound.
Any idea where I misunderstand something? (and any more comments around the general working of the program?).
Ok, so I did some tests with the code, checking when I was missing some conversions depending on the timer frequency and the prescaler value. The code is a bit long, so I post it at the end of the answer. Basically:
// pre-scalor analysis using 5 channels;
// quantities indicated are sampling frequency of the 5 channels
// i.e. necessary ADC sampling frequency is 5 x higher, and value
// of the prescaler ps
// --------------------
// 100kHz ps 1 ok
// 100kHz ps 2 ok
// 100kHz ps 3 fail
// 100kHz ps 255 fail
// 100kHz ps 256 ok
// this indicates: prescaler is 8 bits from 0 to 255, after this wraps up
// ADC frequency is max something like 1MHz in practice: 5 * 100 * 2 (may loose a bit
// due to other interrupts hitting ours?)
// --------------------
// 10kHz ps 38 ok
// 10kHz ps 39 fail
// 10 * 5 * 40 = 2000kHz: ADC is lower than 2MHz
// --------------------
// 1kHz ps 255 ok
// --------------------
I think this indicates that:
the pre-scaler value is well an 8 bits int, between 0 and 255, as it wraps up at 256
I have difficultie matching the results to the formula in the datasheet. I guess this is because there is some overhead switching channels etc (?). For example:
the results are consistent with ```ADC_freq = 1MHz / ( ps ) at the highest frequencies, but I suppose this is because there is a bit of overhead switching channels
the results are consistent with ```ADC_freq = 2MHz / ( ps ) at 10 kHz, and at 1kHz, even using the highest prescaler is fine.
The code I was using is the following, and the criterion for deciding that things fail is that the code reports a drop in the effective sample frequency over the 5 channels:
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// timer driven ADC convertion captured by interrupt on n adc_channels for Arduino Due
//
// this is for Arduino Due only!
//
// the interrupt based ADC measurement is adapted from:
// https://forum.arduino.cc/index.php?topic=589213.0
// i.e. adc_setup(), tc_setup(), ADC_handler() are inspired from the discussion there.
//
// written with VSCode + Platformio and Due board setup
// -------------------------------------------------------------------------------------------------
// -------------------------------------------------------------------------------------------------
// make my linter happy
#include "Arduino.h"
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// some vital ADC grabbing setup
// sample rate in Hz, should be able to go up to several 10s ok kHz at least
constexpr int adc_sample_rate = 1000;
// size of the data buffers "in time"
// i.e. how many consecutive measurements we buffer for each channel
constexpr size_t adc_buffer_nbr_consec_meas = 5;
// the adc_channels to read, in uC reference, NOT in Arduino Due pinout reference
// for a mapping, see: https://components101.com/microcontrollers/arduino-due
// i.e. A0 is AD7
// A1 AD6
// A2 AD5
// A3 AD4
// A4 AD3
// A5 AD2
// A6 AD1
// A7 AD0
constexpr uint8_t adc_channels[] = {7, 6, 5, 4, 3};
constexpr int nbr_adc_channels = sizeof(adc_channels);
// the buffer containing the measurements for all adc_channels over several measurements in time
volatile uint16_t adc_meas_buffer[adc_buffer_nbr_consec_meas][nbr_adc_channels];
// flag when a full vector of conversions is available
volatile bool adc_flag_conversion = false;
// time index of the current measurement in the adc reads buffer
volatile size_t crrt_adc_meas_buffer_idx = 0;
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// some non-vital printing config
// a bit of time tracking, just to analyze how good performance
unsigned long current_us = 0;
unsigned long previous_us = 0;
unsigned long delta_us = 0;
float delta_us_as_s = 0;
float delta_us_as_ms = 0;
int nbr_readings_since_reduced_time_stats = 0;
unsigned long current_reduced_time_stats_us = 0;
unsigned long previous_reduced_time_stats_us = 0;
float delta_reduced_time_stats_us_as_s = 0;
float effective_logging_frequency = 0;
// decide what to print on serial
constexpr bool print_reduced_time_stats = true;
constexpr bool print_time_stats = false;
constexpr bool print_full_buffer = false;
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// low level functions for setting clock and ADC
// start ADC conversion on rising edge on time counter 0 channel 2
// perform ADC conversion on several adc_channels in a row one after the other
// report finished conversion using ADC interrupt
// tests about pre-scaler: formula should be:
// pre-scalor analysis using 5 channels;
// quantities indicated are sampling frequency of the 5 channels
// i.e. necessary ADC sampling frequency is 5 x higher, and value
// of the prescaler ps
// --------------------
// 100kHz ps 1 ok
// 100kHz ps 2 ok
// 100kHz ps 3 fail
// 100kHz ps 255 fail
// 100kHz ps 256 ok
// this indicates: prescaler is 8 bits from 0 to 255, after this wraps up
// ADC frequency is max something like 1MHz in practice: 5 * 100 * 2 (may loose a bit
// due to other interrupts hitting ours?)
// --------------------
// 10kHz ps 38 ok
// 10kHz ps 39 fail
// 10 * 5 * 40 = 2000kHz: ADC is lower than 2MHz
// --------------------
// 1kHz ps 255 ok
// --------------------
// CCL: use ps 2 at 100kHz with 5 channels, 20 at 10kHz, 200 at 1kHz
void adc_setup()
{
PMC->PMC_PCER1 |= PMC_PCER1_PID37; // ADC power on
ADC->ADC_CR = ADC_CR_SWRST; // Reset ADC
ADC->ADC_MR |= ADC_MR_TRGEN_EN | // Hardware trigger select
ADC_MR_PRESCAL(200) | // the pre-scaler: as high as possible for better accuracy, while still fast enough to measure everything
// see: https://arduino.stackexchange.com/questions/12723/how-to-slow-adc-clock-speed-to-1mhz-on-arduino-due
// unclear, asked: https://stackoverflow.com/questions/64243073/setting-right-adc-prescaler-on-the-arduino-due-in-timer-and-interrupt-driven-mul
ADC_MR_TRGSEL_ADC_TRIG3; // Trigger by TIOA2 Rising edge
ADC->ADC_IDR = ~(0ul);
ADC->ADC_CHDR = ~(0ul);
for (int i = 0; i < nbr_adc_channels; i++)
{
ADC->ADC_CHER |= ADC_CHER_CH0 << adc_channels[i];
}
ADC->ADC_IER |= ADC_IER_EOC0 << adc_channels[nbr_adc_channels - 1];
ADC->ADC_PTCR |= ADC_PTCR_RXTDIS | ADC_PTCR_TXTDIS; // Disable PDC DMA
NVIC_EnableIRQ(ADC_IRQn); // Enable ADC interrupt
}
// use time counter 0 channel 2 to generate the ADC start of conversion signal
// i.e. this sets a rising edge with the right frequency for triggering ADC conversions corresponding to adc_sample_rate
// for more information about the timers: https://github.com/ivanseidel/DueTimer/blob/master/TimerCounter.md
// NOTE: TIOA2 should not be available on any due pin https://github.com/ivanseidel/DueTimer/issues/11
void tc_setup()
{
PMC->PMC_PCER0 |= PMC_PCER0_PID29; // TC2 power ON : Timer Counter 0 channel 2 IS TC2
TC0->TC_CHANNEL[2].TC_CMR = TC_CMR_TCCLKS_TIMER_CLOCK2 // clock 2 has frequency MCK/8, clk on rising edge
| TC_CMR_WAVE // Waveform mode
| TC_CMR_WAVSEL_UP_RC // UP mode with automatic trigger on RC Compare
| TC_CMR_ACPA_CLEAR // Clear TIOA2 on RA compare match
| TC_CMR_ACPC_SET; // Set TIOA2 on RC compare match
constexpr int ticks_per_sample = F_CPU / 8 / adc_sample_rate; // F_CPU / 8 is the timer clock frequency, see MCK/8 setup
constexpr int ticks_duty_cycle = ticks_per_sample / 2; // duty rate up vs down ticks over timer cycle; use 50%
TC0->TC_CHANNEL[2].TC_RC = ticks_per_sample;
TC0->TC_CHANNEL[2].TC_RA = ticks_duty_cycle;
TC0->TC_CHANNEL[2].TC_CCR = TC_CCR_SWTRG | TC_CCR_CLKEN; // Software trigger TC2 counter and enable
}
// ISR for the ADC ready readout interrupt
// push the current ADC data on all adc_channels to the buffer
// update the time index
// set flag conversion ready
void ADC_Handler()
{
for (size_t i = 0; i < nbr_adc_channels; i++)
{
adc_meas_buffer[crrt_adc_meas_buffer_idx][i] = static_cast<volatile uint16_t>(*(ADC->ADC_CDR + adc_channels[i]) & 0x0FFFF);
}
crrt_adc_meas_buffer_idx = (crrt_adc_meas_buffer_idx + 1) % adc_buffer_nbr_consec_meas;
adc_flag_conversion = true;
}
//--------------------------------------------------------------------------------------------------
//--------------------------------------------------------------------------------------------------
// a simple script: setup and print information
void setup()
{
Serial.begin(115200);
delay(100);
adc_setup();
tc_setup();
}
void loop()
{
if (adc_flag_conversion == true)
{
adc_flag_conversion = false;
if (print_reduced_time_stats)
{
nbr_readings_since_reduced_time_stats += 1;
if (nbr_readings_since_reduced_time_stats == adc_sample_rate)
{
current_reduced_time_stats_us = micros();
delta_reduced_time_stats_us_as_s = static_cast<float>(current_reduced_time_stats_us - previous_reduced_time_stats_us) / 1000000.0;
effective_logging_frequency = static_cast<float>(adc_sample_rate) / delta_reduced_time_stats_us_as_s;
previous_reduced_time_stats_us = current_reduced_time_stats_us;
Serial.print(F("Effective logging freq over nbr spls that should correspond to 1 second: "));
Serial.println(effective_logging_frequency);
nbr_readings_since_reduced_time_stats = 0;
}
}
if (print_time_stats)
{
current_us = micros();
delta_us = current_us - previous_us;
delta_us_as_s = static_cast<float>(delta_us) / 1000000.0;
delta_us_as_ms = static_cast<float>(delta_us) / 1000.0;
Serial.println(F("ADC avail at uS"));
Serial.println(micros());
Serial.println(F("elapsed us"));
Serial.println(delta_us);
Serial.println(F("elapsed ms"));
Serial.println(delta_us_as_ms);
Serial.println(F("elapsed s"));
Serial.println(delta_us_as_s);
Serial.println(F("updated idx:"));
size_t last_modified_buffer_idx;
if (crrt_adc_meas_buffer_idx > 0){
last_modified_buffer_idx = crrt_adc_meas_buffer_idx - 1;
}
else{
last_modified_buffer_idx = nbr_adc_channels - 1;
}
Serial.println(last_modified_buffer_idx);
previous_us = current_us;
}
if (print_full_buffer)
{
for (size_t i = 0; i < nbr_adc_channels; i++)
{
Serial.print(F(" ADC "));
Serial.print(adc_channels[i]);
Serial.println(F(" meas in time:"));
for (size_t j = 0; j < adc_buffer_nbr_consec_meas; j++)
{
Serial.print(adc_meas_buffer[j][i]);
Serial.print(F(" "));
}
Serial.println();
}
}
}
}

How to speed update time of IDR Register STM32?

I have a question about the IDR Register of STM32
I set the pin0 of the ODR Register to 1 in my code below and I want to check, if the pin0 in the IDR register is set to 1. The problem is, after setting the pin to 1, I must wait 1 second until the pin in the IDR register is set to 1.
How can I read the pin state without waiting?
How can I solve this problem?
void init_pins(void)
{
GPIOG->OTYPER |= (0x01 << 0); // Pin 0 as open drain
GPIOG->OTYPER &= ~(0x01 << 1); // Pin 1 as push - pull
GPIOG->BSRRL = 0x01 << 1; // Pin 1 to High
GPIOG->BSRRL = 0x01 << 0; // Pin 0 to High
microsecond_Delay(1000000);
if( !(GPIOG->IDR & 0x01) )
{
errorCode = -1 ; //Error Code for Bus not floating
}
}
Use CMSIS header files provided by the STM.
If configure pin as open drain you can't set it HIGH. It has to be done by the external pull-up. Your configuration is just wrong. If you do not have external pull-up (or at leat the internal one) the state of the pin if you do not set it to zero is unpredictable.

Arduino timer4 custom PWM issue

I made a nice code which generates fast PWM with 50% duty cycle and I can change the frequency with a potentiometer. It outputs straight and inverted channels with some dead time. I am using Arduino Micro aka ATmega32U4. The code is actually "Atmel" code. Code is working fine until I power Arduino Micro off and then on again.
I have programmed the code and registers so that the frequency is changeable from 10kHz to 100kHz. But after power on/off the frequency changes from 5kHz to 50kHz. After this has happened I have to program the board again using Arduino IDE, to make it work correctly. Again after power on/off it has changed. I am quite sure that one of the registers is overwritten by the "Arduino hardware abstraction layer" or however we should name it. I have not yet read out all the registers so I do not know which one is overwritten. I guess it's the prescaler.
How do I prevent this from happening? Should I write the register contents somewhere else? Or should I write it few times to be sure?
Why or how this is happening anyway?
Here's the code:
#define OSC1 5
#define OSC2 13
uint8_t read_reg1;
uint8_t read_reg2;
int pot, freq;
void setup() {
pinMode(OSC1, OUTPUT);
pinMode(OSC2, OUTPUT);
Serial.begin(9600);
cli(); // disable global interrupts
TCCR4A=0; // clear register
TCCR4B=0x06; // configure prescaler to 64 (CK = CLK / 64 = 1.5 MHz)
TCCR4C=0;
TCCR4D=0; // select Fast PWM operation (0 << WGM41)|(0 << WGM40)
PLLFRQ=(PLLFRQ&0xCF)|0x30; // select clock source and frequency
OCR4C=150; // select PWM frequency
OCR4A=150/2; // set duty cycle
DT4 = 0x55; // set dead times. DT = (1 / 48Mhz) * 0...15
// enable interrupt on timer4 overflow
TIMSK4|=(1 << TOIE4);
// This register write has to be after others. Otherwise the PWM generation will not work. I do not know why.
TCCR4A=0x42; // COM4A1..0 = 01, OC4A and !OC4A connected. PWM4A = 1 (activate channel A PWM output)
sei(); // enable global interrupts
}
void loop() {
//cli();
pot = analogRead(A0);
freq = map(pot, 0, 1023, 14, 166);
//sei();
/*
Serial.print("Pot value: ");
Serial.print(pot);
Serial.print("\tFreq value: ");
Serial.println(1500000/freq);
*/
}
ISR(TIMER4_OVF_vect){
OCR4C = freq;
OCR4A = freq / 2;
}
I am not sure exactly why you got different behavior right after programming, but the bootloader that the Arduino Micro uses (Caterina) does not perform a full reset after it runs, so changes that the bootloader made to the AVR's registers are often visible to the user's sketch.
I was able to fix the problem by removing the line that modifies PLLFRQ. Here is a simplified version of your code that always produces 3.31 kHz PWM:
void setup()
{
pinMode(5, OUTPUT);
pinMode(13, OUTPUT);
TCCR4A = 0;
TCCR4B = 0x06; // configure prescaler to 64 (CK = CLK / 64 = 1.5 MHz)
TCCR4C = 0;
TCCR4D = 0; // select Fast PWM operation (0 << WGM41)|(0 << WGM40)
OCR4C = 150; // select PWM frequency
OCR4A = 150 / 2; // set duty cycle
DT4 = 0x55; // set dead times. DT = (1 / 48Mhz) * 0...15
// This register write has to be after others.
// Otherwise the PWM generation will not work. I do not know why.
// COM4A1..0 = 01, OC4A and !OC4A connected.
// PWM4A = 1 (activate channel A PWM output)
TCCR4A = 0x42;
}
void loop()
{
}
It's not a great idea to mess with the PLL postscaler since it will probably affect every other Arduino library that uses timers, including the USB stack.

“Read Analog Voltage” sample rate

I want to make sure my code looks like working, since I don't have a lot of time with a signal generator tomorrow and I want to know how to set the sample rate.
I want to sample a 2kHz signal with a samplerate of 6kHz with a Arduino MEGA 2560.
It, doesn't have to be in real time, so i'm thinking of filling a buffer and then sending those over the serial connection.
Can anyone say if this code defenitly wouldn't work for this?
And how could i set the samplerate to 6kHz?
void setup() {
Serial.begin(9600);
}
void loop() {
for(int x = 0; x < 1000; x++){
// read the input on analog pin 0:
int sensorValue[x] = analogRead(A0);
}
for( x = 0; x < 1000; x++){
// Convert the analog reading (which goes from 0 - 1023) to a voltage (0 - 5V):
float voltage[x] = sensorValue[x] * (5.0 / 1023.0);
// print out the value you read:
Serial.println(voltage[x]);
}
}
Thank you.
Well, as I've mentioned in another thread, you can use auto triggering mode of ADC (for UNO and ATMega328p based Arduinos):
void setup() {
Serial.begin(256000);
// ADC setup is done by arduino framework, but it's possible to change it slightly (for ATMega328) :
ADCSRB = _BV(ADTS2) | _BV(ADTS1) | _BV(ADTS0); // ADTS2..0 = 111, Timer 1 input capture event trigger source
ADCSRA |= _BV(ADATE); // enable auto trigger mode
ADCSRA |= _BV(ADIF); // reset conversion end flag (= interrupt flag)
// timer 1 setting:
TCCR1A = 0; // clear all
ICR1 = F_CPU/6000U; // 1 should be substracted here but result is about 2665.7 and it will be truncated to 2665
TCCR1B = _BV(WGM12) | _BV(WGM13) | _BV(CS10); // CTC mode with ICR1 as TOP value, enabled with no prescaling
TIMSK1 = _BV(ICF1); // not working without this... Flag must be cleaned up after the trigger ADC, otherwise it's stucked
analogRead(A0); // dummy read to set correct channel and to start auto trigger mode
pinMode(13, OUTPUT);
}
void loop() {
if (ADCSRA & _BV(ADIF)) {
ADCSRA |= _BV(ADIF); // reset flag by writing logic 1
Serial.println(ADC);
}
}
ISR(TIMER1_CAPT_vect) { // to clear flag
PINB = _BV(PB5); // and toggle d13 so frequency can be measured (it'd be half of real rate)
// it might be enabled on PWM pin too by setting force output compare and some compare register to half of value ICR1
}
This sketch uses baud rate 250000 but it's still too slow. The space character can be used as an separator, this'll save one character (as new line are usually two characters: \r\n). One value can be 1 to 4 characters long so for values:
0-9 - 3B you need baud rate 3*10*6000 = 180000
10-99 - 4B and you need baud rate 240000
and for the rest of cases you're too slow.
So the only way is sending those integers binary and without separator it'd be even better. The 2B per value results into minimal baud rate around 120000 baud/s.

Resources