STM32F030F4 does not start I2C1 correctly in Bare Metal - i2c

STM32F030F4 does not start I2C1 correctly.
this is my code:
//Clocks------------------------------------------------
RCC->AHBENR = 0x200000;
RCC->APB1ENR = 0x4040 ;
//------------------------------------------------------
//GPIO--------------------------------------------------
//Configure Alternate Fucntion GPIO to I2C1
GPIOA->AFR[1] = 0x440 //(0b0100)<<4 | (0b0100)<<8 //AF4;
//Set GPIOA Pin 9-10 as Alternate Function
GPIOA->MODER = 0x280000;
//Set GPIOA pin 9-10 as speed as High
GPIOA->OSPEEDR = 0x3c0000;
//Set GPIOA pin 9-10 as open drain
GPIOA->OTYPER = 0x600;
//Set GPIOA pin 9-10 as no Pull-up Pull-down
GPIOA->PUPDR = 0x0;
//------------------------------------------------------
//I2C Resgisters
I2C1->TIMINGR = 0x00201D2D;
I2C1->CR1 = 0x1;
I2C1->CR2 = 0x307001c;
I2C1->TXDR = 0x111;
//Start I2C
I2C1->CR2 |= (0b1) << 13;
But output is not true.
It is like this:
SDA port is not working.

This line seems to be problematic.
GPIOA->AFR[1] = GPIO_Aternate_Function_I2C1;
You should set AFSEL9 and AFSEL10 value if I2C pins are 9 and 10. But you set AFSEL8 (which is the lowest in AFR[1]), and clear others.
This code should do the thing:
GPIOA->AFR[1] = GPIO_Aternate_Function_I2C1 * 0x00000110;

Problem Solved..
Clocks are problematic:
changes:(
//Enable PORTA clocks
RCC->AHBENR |= 0x20000;
//Enable I2C1 clocks
RCC->APB1ENR |= 0x200000 ;
)

Related

ADC results change but input voltage is same. STM32 - Proteus

I can't figure out how to properly set up an AD convertor for my college project.
I am supposed to use TRGO to trigger the conversion and I am trying to do it using TIM3. Every time I call ADC_Init I get the result but it differs depending on the PWM signal which does not have anything to do with it, I am trying to change the CCR of the PWM signal according to the measure of ADC. Any help on how to properly do this? Thank you!
#include <stdio.h>
#include "stm32f103x6.h"
volatile int adc_dr;
void ADC_General_Init(ADC_TypeDef * ADCx){
ADCx->CR2 |= ADC_CR2_ADON; // ukljucivanje ADC (izlazak iz rezima power-down) - ADON set
delay(20);
//kalibracija
ADCx->CR2 |= ADC_CR2_CAL;
while ((ADCx->CR2 & ADC_CR2_CAL)==ADC_CR2_CAL);
// iskljucivanje ADC dok se ne podese svi parametri (usteda energije) - ADON reset
ADCx->CR2 &= ~ADC_CR2_ADON;
ADCx->CR2 &= ~ADC_CR2_CONT; // CONT = 0: Single conversion mode - jedna konverzija
// ukljucivanje internog temperaturnog senzora i referentnog napona (strana 235)
ADCx->CR2 |= ADC_CR2_TSVREFE; // TSVREFE bit mpra biti setovan da dozvoli oba interna kanala: ADCx_IN16 (temperature sensor) and ADCx_IN17 (VREFINT) conversion.
// podesavanje konverzije jednog kanala u regularnoj grupi (strana 247)
ADCx->SQR1 &= ~ADC_SQR1_L; // setovanje bitova L[3:0]=0000: 1 konverzija, ADC_SQR1
// odredjivanje kanala 1 (Channel 1) za (prvu) konverziju u regularnoj grupi (strana 249)
ADCx->SQR3 &= ~ADC_SQR3_SQ1; // brisanje bitova
ADCx->SQR3 |= (1U << ADC_SQR3_SQ1_Pos); // setovanje bitova SQ1[4:0] u ADC_SQR3 registru - SQ1 = Channel 1; prvi kanal je u trecem registru
//"Pos" kaze da upisujem 1 (ovde) u bit (pozicija) gde pocinje SQ1 - bit 0;
//Bits 4:0 SQ1[4:0]: first conversion in regular sequence
// hardversko okidanje konverzije (strana 241)
ADCx->CR2 &= ~ADC_CR2_EXTSEL;
ADCx->CR2 |= (0b100U << ADC_CR2_EXTSEL_Pos); // EXTSEL[2:0] = 100: Timer 3 TRGO event
/*----------------ili----------------------
ADC1 -> CR2 &= ~ADC_CR2_EXTSEL;
ADC1 -> CR2 |= ADC_CR2_EXTSEL_2;
---------------------------------------------*/
ADC1 -> CR2 |= ADC_CR2_EXTTRIG; // 1: Conversion on external event enabled (strana 241)
ADCx->CR2 |= ADC_CR2_ADON;// ukljucivanje ADC
}
void ADC_GPIO_Init(void) {
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // uključivanje takta za GPIO Port A
// strana 26 u STM32F103_Datasheet.pdf
GPIOA->CRL &= ~(GPIO_CRL_MODE1 | GPIO_CRL_CNF1); // brisanje podešavanja za PA6
GPIOA->CRL |= (GPIO_CRL_MODE1_0 | GPIO_CRL_CNF1_0); //input mode - 00: Analog mode
}
void ADC_Init(void) {
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // ukljucivanje ADC1 clock-a
ADC_GPIO_Init();
ADC_General_Init(ADC1);
ADC1->CR1 |= ADC_CR1_EOCIE; // omogucivanje prekida (strana 240)
// postavlja se signal EOC: End of conversion
NVIC_SetPriority(ADC1_IRQn, 1); // dodela prioriteta na 1
//NVIC->ISER[0] |= NVIC_ISER_SETENA_18;
//NVIC_EnableIRQ(ADC1_2_IRQn); // drugi nacin
NVIC_EnableIRQ(ADC1_IRQn); // dozvola prekida ADC 1 & 2 - ista prekidna rutina broj 18 (strana 199)
}
void ADC1_2_IRQHandler(void) {
uint16_t statReg = (ADC1->SR & 0xFFFFU); // citanje statusnog registra
if ((statReg & ADC_SR_EOC) == ADC_SR_EOC) {
adc_dr = (ADC1 -> DR & 0xFFFFU); //cuvamo vrednost data registra u neku promenljivu
//TIM2->CCR4 = adc_dr/10; // treba menjati ovo deljenje
// delimo kako bismo dobili srazmernu promenu
delay(10);
ispisbroja(adc_dr);
delay(1000);
if(adc_dr <=1045){
TIM2->CCR4 = 999;
//ispisbroja(TIM2->CCR4);
}
else if( adc_dr >= 1084){
TIM2->CCR4 = 0;
//ispisbroja(TIM2->CCR4);
}
else{
TIM2->CCR4 = (int)999-(adc_dr-1045)*27,75;
//ispisbroja(TIM2->CCR4);
}
}
}
void TIM3_IRQHandler(void){
ADC_Init();
TIM3 -> SR &= ~TIM_SR_UIF;
}
void adtim1(void){
RCC->APB2ENR |= RCC_APB2ENR_IOPAEN; // Enable the peripheral clock of GPIO Port A
// Hocemo da koristimo tajmer TIM3, u rezimu Toggle Output
//TIM2->CR1 &= ~TIM_CR1_CEN;
// Enable timer 2 clock
RCC->APB1ENR |= RCC_APB1ENR_TIM3EN;
// Counting direction: 0 = up-counting, 1 = down-counting
//TIM2->CR1 &= ~TIM_CR1_DIR;
TIM3->CR2 &= ~TIM_CR2_MMS; // brisanje master mode selection bitova
TIM3->CR2 |= TIM_CR2_MMS_2; // setovanje OC1REF as trigger output TRGO (OC1REF=100)
// Clock prescaler (16 bits, up to 65,535)
TIM3->PSC = 7999;
//Auto-reload: up-counting (0-->ARR), down-counting (ARR-->e
TIM3->ARR = 2000;
//Upisujemo vrednost u Capture/Compare Register 1 i time podesavamo sirinu impulsa,
// tj. izlaz ce biti 1, dok je vrednost brojaca manja od vrednosti u CCR4
TIM3->CCR2 = 50;
//TIM2->CCR3 = 90;
// Output compare se podešava u registru TIMx_CCMR1 - Capture/Compare Mode Register (za kanale 1 i 2), ili TIMx_CCMR2 (za kanale 3 i 4)
// Polje OC3M[2:0], 011: Toggle - OC4REF toggles when TIMx_CNT=TIMx_CCR4.
TIM3->CCMR1 &= ~TIM_CCMR1_OC2M; // Prvo brisemo bitove za to polje
TIM3->CCMR1 |= (0b110 << TIM_CCMR1_OC2M_Pos); // Upisujemo vrednost 011 od pozicije na kojoj pocinje polje TIM_CCMR2_OC4M
//TIM3->CCMR1 |= 0b01 << TIM_CCMR1_CC2S_Pos;
//TIM3->CCMR2 &= ~TIM_CCMR2_OC4PE;
// Select output polarity: 0 = active high, 1 = active Low
TIM3->CCER &= ~TIM_CCER_CC2P; //OC4 active high
TIM3->CCER |= TIM_CCER_CC2P;
// Enable output for channel 1 , CC4E: Capture/Compare 1 output enable
TIM3->CCER |= TIM_CCER_CC2E; // 1: On - OC4 signal is output on the corresponding output pin.
//TIM3->SMCR |= 0b110 << TIM_SMCR_SMS_Pos;
//TIM3->SMCR |= 0b110 << TIM_SMCR_TS_Pos;
/*
TIM2->CCMR2 &= ~TIM_CCMR2_OC3M; // KANAL 3
TIM2->CCMR2 |= (0b011 << TIM_CCMR2_OC3M_Pos); // Upisujemo vrednost 011 od pozicije na kojoj pocinje polje TIM_CCMR2_OC4M
TIM2->CCMR2 &= ~TIM_CCMR2_OC3PE;
// Select output polarity: 0 = active high, 1 = active Low
TIM2->CCER &= ~TIM_CCER_CC3P; //OC4 active high
// Enable output for channel 1 , CC4E: Capture/Compare 1 output enable
TIM2->CCER |= TIM_CCER_CC3E; // 1: On - OC4 signal is output on the corresponding output pin. KANAL 3
*/
// Enable timer 2
//TIM2->CR1 |= (0b01 << TIM_CR1_CMS_Pos);
TIM3->CR1 &= ~TIM_CR1_ARPE;
TIM3->CR1 |= TIM_CR1_CEN;
TIM3->DIER |=TIM_DIER_UIE; //Update Interrupt Enable
TIM3 -> SR &= ~TIM_SR_UIF; //Update Interrupt Flag
//TIM3->DIER |=TIM_DIER_TIE; //Update Interrupt Enable
//TIM3 -> SR &= ~TIM_SR_TIF; //Update Interrupt Flag
NVIC_EnableIRQ(TIM3_IRQn); //Interrupt Set-Enable Register
__enable_irq(); //Enable Interrupt
}

STM32 enable ADXL345 via SPI

I am trying to connect to the ADXL345, which is on another board with GPIOs connected to it (PA5, PA6, PA7, PA12). Also, i am using PulseViewer from [sigrok][1]
The Software shows, that the Sensor is NOT being enabled, yet it seems the functionality is correct. Is there something I'm missing?
#include "stm32l0xx.h"
#include "pmi_stddefs.h"
int32_t spi_init_adxl345(void)
{
/* enable SPI1 clock */
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
/* Enable clock for port A */
//RCC->IOPENR |= RCC_IOPENR_GPIOAEN;
RCC->IOPENR |= RCC_IOPENR_IOPAEN;
/* CPHA at 1, CPOL at 1 */
SPI1->CR1 |= SPI_CR1_CPOL;
SPI1->CR1 |= SPI_CR1_CPHA;
/* enable master mode */
SPI1->CR1 |= SPI_CR1_MSTR;
/* baud rate Maximum (5MHZ ADXL = 011) */
// SPI1->CR1 &= ~SPI_CR1_BR_0;
// SPI1->CR1 |= SPI_CR1_BR_1;
// SPI1->CR1 |= SPI_CR1_BR_2;
SPI1->CR1 |= (SPI_CR1_BR_0)| (SPI_CR1_BR_1);
/* Internal Slave Select */
/* soft. slave management */
SPI1->CR1 |= SPI_CR1_SSM;
/* select slave */
SPI1->CR1 |= SPI_CR1_SSI;
/* Frame Format MSB */
SPI1->CR1 &= ~SPI_CR1_LSBFIRST;
/* Receive only mode enable (0 = full-duplex (Transmit and receive)) */
SPI1->CR1 &= ~SPI_CR1_RXONLY;
/* Data frame format 8 */
SPI1->CR1 &= ~SPI_CR1_DFF;
//SPI1->CR2 = SPI_CR2_SSOE | SPI_CR2_RXNEIE;
SPI1->CR2 = 0;
/* Set AF,OUTPUT modes */
/* MISO AF*/
GPIOA->MODER &= ~GPIO_MODER_MODE6_0;
GPIOA->MODER |= GPIO_MODER_MODE6_1;
/* MOSI AF*/
GPIOA->MODER &= ~GPIO_MODER_MODE7_0;
GPIOA->MODER |= GPIO_MODER_MODE7_1;
/* SCLK AF*/
GPIOA->MODER &= ~GPIO_MODER_MODE5_0;
GPIOA->MODER |= GPIO_MODER_MODE5_1;
// TODO: PUPDR for PA4
// /* CS OUTPUT*/
GPIOA->MODER |= GPIO_MODER_MODE12_0;
GPIOA->MODER &= ~GPIO_MODER_MODE12_1;
/* Set I/O output speed (11=very high speed) */
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEED5;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEED6;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEED7;
GPIOA->OSPEEDR |= GPIO_OSPEEDER_OSPEED12;
/* Alternate Function Low Register 9.3.2*/
/* Pin 5,6,7 (in AFRL) */
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL5;
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL6;
GPIOA->AFR[0] |= GPIO_AFRL_AFSEL7;
return RC_SUCC;
}
int32_t spi_txrx (uint8_t *buf, uint32_t size)
{
GPIOA->BSRR |= GPIO_BSRR_BR_12;
/* TXE == 1 : the buffer is empty */
while (!(SPI1->SR & SPI_SR_TXE))
{
/* wait untill TXE is empty */
}
for (uint8_t i = 0; i < size; i++)
{
SPI1->DR = buf[i];
}
while (!(SPI1->SR & SPI_SR_TXE))
{
/* mandatory wait until TXE is set */
}
/* detect the end of a transfer */
while ((SPI1->SR & SPI_SR_BSY) != 0)
{
/* 1:busy, 0:not busy */
}
/* random noise */
uint8_t temp = SPI1->DR;
temp = SPI1->SR;
/* Disable sensor */
GPIOA->BSRR |= GPIO_BSRR_BS_12;
/* Diable the SPI <-- No need (Master in full-duplex or transmit
only mode can finish any transaction when it stops providing data
for transmission. In this case, the clock stops after the last
data transaction.*/
return RC_SUCC;
}
Im stuck on this for a reasonable amount of time, yet i didn't find an answer for this issue. Any ideas? Thanks in advance!
[1]: https://www.sigrok.org/wiki/PulseView
SPI nCS lines are typicaly active-low, so PA12 must be set during board startup. In your code ADXL345 is always selected.
This GPIOA->BSRR |= GPIO_BSRR_BR_12; is reset (BR = Bit Reset, BS = Bit Set). Also, there is no need to use the |= operator, because BSRR is write-only register, designed to change port state without use of the read-modify-write sequence.
The SPE bit in SPI_CR1 regisger is not set, SPI is disabled and not transmitting anything. spi_txrx would stuck in the second SPI_SR_TXE while-loop.
Not related to the question, but still - spi_txrx looks FIFO-oriened, yet STM32L0-series SPI have only shifter and DR register, and exchange must be implemented on the byte-by-byte basis, or with DMA. Perhaps this code would work for a 1- or 2-byte long transfer, and you'll even get a correct RX result if only last the byte is valuable. But generaly, it's a code smell.
To me it looks like you are choosing the wrong alternate functions for PA5, PA6 and PA7. Notice that GPIO_AFRL_AFSEL5 is defined as (0xFUL << GPIO_AFRL_AFSEL5_Pos), so when you write it in the AFR[0] register you are choosing alternate function 15 (which is not available). However what you want to choose for SPI1 is alternate function 1.
You could try something like
GPIOA->AFR[0] |= (1 << GPIO_AFRL_AFSEL5_Pos);
GPIOA->AFR[0] |= (1 << GPIO_AFRL_AFSEL6_Pos);
GPIOA->AFR[0] |= (1 << GPIO_AFRL_AFSEL7_Pos);

STM32 ADC initialization without HAL

I'm trying to write some basic code without HAL to initialize and read from an ADC, but I can't get it respond properly. I'm using an STM32F103C8T6 on a blue pill dev board. Function is the following:
Pin B1 (ADC9) is connected to a 0-3.3V signal (a 12V supply voltage through a voltage divider)
When the signal goes below a constant that i've called SUPP_V_MIN_VAL, pin B9 is set. If the signal is above that value, pin B9 is cleared.
I defined SUPP_V_MIN_VAL to be 2606.0. I am casting everything as a float because, in a separate section in my code, i need to display the current ADC value as a float).
The issue is that I think I am not initializing my ADC properly, or not casting my types properly. I'm finding that the ADC reading is always 0. My initialization is below:
RCC->APB2ENR |= 0x1UL << 3; //Initialize clock for GPIOB (bit 3), if it hasn't been initialized yet
RCC->APB2ENR |= 0x1UL << 9; //Initialize clock for ADC1 (bit 9), if it hasn't been initialized yet
GPIOB->CRL &= ~(0xF0UL); //SetBar pin PB1 to analog input
//ADC1->CR1 |= 0x0UL; //Default settings are correct - ignore
ADC1->CR2 |= 0x2UL; //Enable ADC1_CR2_CONT for continuous conversion
//ADC1->SMPR1; //ADC channels in SMPR1 are not used - ignore
ADC1->SMPR2 |= 0x38000000UL; // Set SMP9 to 239.5 cycles (set bits 27-29 to 1)
ADC1->CR2 |= 0x1UL; //Set CR2_ADON to wake up ADC from sleep mode
ADC1->CR2 |= 0x1UL << 3; //Initialize calibration register
while ((ADC1->CR2 >> 3) & 0x1UL); //Wait until calibration register is initialized
ADC1->CR2 |= 0x1UL << 2; //Enable calibration
while ((ADC1->CR2 >> 2) & 0x1UL); //Wait until calibration completed
ADC1->CR2 |= 0x1UL; //Set CR2_ADON again to turn on ADC and start converting
I am reading the pin with:
void pollSUPP(void)
{
uint16_t ADC_Reading = ADC1->DR;
suppVolt.float_var = (float) ADC_Reading;
if( suppVolt.float_var < SUPP_V_MIN_VAL)
{
// digitalWrite(SUPP_LOW, HIGH); PB9
GPIOB->BSRR = 0X1 << 9;
}
else {
GPIOB->BRR = 0X1 << 9;
}
}
SuppVolt is defined as
union {
float float_var;
uint8_t chars[4];
} suppVolt; //ADC reading for supply voltage
I may also be doing something stupid (I haven't actually confirmed that Pin B9 is on ADC1, but I am assuming it is since the blue pill only has one ADC??) or referencing the wrong bit in a register.
Can anyone help me spot my error? Thank you in advance!!

2 stm32 adress does not match in proteus

I am trying to communicate 2 STM32 with I2C.My configuration is as followed:
7-bit addressing mode (no dual address, only OAR1)
100khz speed
ACK enabled (on slave)
ACK disabled (on master, since only 1 byte is transferred between master/slave at any time)
on both master/slave, datasheet says GPIOB (PB6) as SCL as AF and GPIOB (PB7) as SDA as AF but PB8 and PB9 becomes logic 1 in Proteus so I use PB8 and PB9.Adress does not match.Could it be because of Proteus?
Master code:
#include "stm32f10x.h" // Device header
#include "delay.h"
void pinConfig(void);
void i2c_Master_Config(void);
void sendAdress();
int main() {
pinConfig();
i2c_Master_Config();
sendAdress();
while(1) {}
}
void pinConfig() {
RCC->APB1ENR |= 1<<21;//Enable I2C 1 clock
RCC->APB2ENR |= 1<<2;//Enable GPIOA clock
RCC->APB2ENR |= 1<<3;//Enable GPIOB clock
RCC->APB2ENR |= 1<<0;//Enable AFIO clock
GPIOB->CRH |= 0x000000FF; //SCL ve SDA AF Open Drain SCL => PB8
SDA =>PB9
}
void i2c_Master_Config() {
RCC->APB1ENR |= 1<<21; //I2C 1 Clock Enable.
I2C1->CR1 |= (1 << 15);
I2C1->CR1 &= ~(1 << 15);
I2C1->CR2 |= 0x08; //36 Mhz peripheral clock.
I2C1->CCR = 0x28; //100 khz clock
I2C1->TRISE = 0x09; //1/8MHZ= 125 ns => 1000ns/125ns =8 => 8+1=9
I2C1->CR1 |= (1<<0); //Peripheral enable.
}
void sendAdress() {
volatile int temp;
I2C1->CR1 |= 1<<8; //START bit.
while(!(I2C1->SR1 & (1<<0))); //wait until start flag is set
I2C1->DR = 0x0B; //7 bit adress.
while(!(I2C1->SR1 & (1<<1))); //wait until addr flag is set
temp = I2C1->SR2; //clear addr flag.
}
Slave code:
#include "stm32f10x.h" // Device header
void pinConfig(void);
void i2c_Slave_Config(void);
uint8_t data;
void I2C1_EV_IRQHandler() {
volatile int temp;
if(I2C1->SR1 &(1<<1)) { //wait until addr flag is set
temp = I2C1->SR1; //clear addr
temp = I2C1->SR2; //clear addr
GPIOA->BRR |= 1<<3;
}
}
int main() {
pinConfig();
i2c_Slave_Config();
while(1) {}
}
void pinConfig() {
RCC->APB1ENR |= 1<<21; //I2C 1 Clock enable.
RCC->APB2ENR |= 1<<2; //Enable GPIOA clock
RCC->APB2ENR |= 1<<3; //Enable GPIOB clock
RCC->APB2ENR |= 1<<0; //Enable AFIO clock
GPIOA->CRL |= 0x00002000; //PA3 led.
GPIOB->CRH |= 0x000000FF; //SCL ve SDA AF Open Drain SCL => PB8 SDA =>PB9
GPIOA->BSRR |= 1<<3; //Turn off the led.
}
void i2c_Slave_Config() {
RCC->APB1ENR |= 1<<21; //I2C 1 Clock Enable.
I2C1->CR1 |= (1 << 15);
I2C1->CR1 &= ~(1 << 15);
I2C1->CR2 |= 0x08;
I2C1->CCR = 0x28; //100 khz clock
I2C1->OAR1 &= ~(1<<15); //7-bit slave adress.
I2C1->CR2 |= 1<<9; //Interrupt enable.
NVIC->ISER[0] |= 1<<31; //i2c1 interrupt enable.
I2C1->OAR1 = (0x05 << 1); //Slave adress
I2C1->CR1 |= (1<<0); //Peripheral enable.
I2C1->CR1 |= 1<<10; //ACK bit.
}

STM32F7: ADC DMA transfer only works once

I want to continuously read ADC values and write them into an array using the DMA. The board I am using is a Nucleo board with the STM32F767ZI.
To keep stuff like outputting data simple I am using the Arduino IDE with the STM32 board package.
I was able to get the ADC to work in continuous mode, but when I add the DMA it will not work. Only one single value seems to be transferred. The NDTR-register containing the amount of data to be transferred stays at the value I set it to minus one.
Here is the little program:
volatile static bool dma_active = 1;
#define maxSamples 512
int16_t dataPoints[maxSamples];
void setup() {
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; // GPIOA clock enable
GPIOA->MODER |= (0b11 << 6); // PA3 as analog input
Serial.begin(115200);
Serial.println("starting");
initADC();
initDMA();
}
void initADC() {
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // ADC1 clock enable
ADC1->CR2 |= ADC_CR2_ADON; // ADC on
ADC1->CR2 |= ADC_CR2_CONT; // continuous conversion mode
ADC1->CR1 |= ADC_CR1_EOCIE; // EOC interrupt
ADC1->CR1 &= ~ADC_CR1_DISCEN; // discontinuous mode disabled
ADC1->CR1 &= ~ADC_CR1_SCAN; // scan mode disabled
ADC1->CR2 |= ADC_CR2_DMA; // DMA mode
ADC1->CR2 |= ADC_CR2_DDS; // continuous DMA request
ADC1->SQR3 |= 0b11; // ADC1_IN3 = PA3
ADC1->SQR1 &= ~(0b1111 << ADC_SQR1_L); // 1 conversion
ADC1->CR2 |= ADC_CR2_SWSTART; // Start conversion
}
void initDMA() {
// DMA2 Stream4 : Channel 0 is ADC1
RCC->AHB1ENR |= (1 << 22); // DMA2 clock enable
DMA2_Stream4->CR &= ~DMA_SxCR_EN; // Disable
while (DMA2_Stream4->CR & (1 << 0));
DMA2_Stream4->CR |= (0b0100 << DMA_SxCR_CHSEL); // Channel 4
DMA2_Stream4->CR |= (0b11 << DMA_SxCR_PL); // Very high priority
DMA2_Stream4->PAR = (uint32_t)&ADC1->DR; // Data source register
DMA2_Stream4->M0AR = uint32_t(&dataPoints); // Buffer 1
// DMA2_Stream4->M1AR = uint32_t(&dataPoints1); // Buffer 2
DMA2_Stream4->NDTR = maxSamples; // Number of transferred data
DMA2_Stream4->CR |= (0b01 << DMA_SxCR_PSIZE); // Source data size (00 = byte, 01 = half word, 10 = word)
DMA2_Stream4->CR |= (0b01 << DMA_SxCR_MSIZE); // Memory data size (00 = byte, 01 = half word, 10 = word)
DMA2_Stream4->CR |= DMA_SxCR_TCIE; // Transfer complete interrupt enable
DMA2_Stream4->CR |= DMA_SxCR_CIRC; // circular mode
DMA2_Stream4->CR &= ~DMA_SxCR_PINC; // no peripheral increment mode
DMA2_Stream4->CR |= DMA_SxCR_MINC; // memory increment mode
// DMA2_Stream4->CR |= DMA_SxCR_DBM; // double buffer mode
DMA2->HIFCR |= 0b111101; // clear flags
NVIC_EnableIRQ(DMA2_Stream4_IRQn);
delay(20);
DMA2_Stream4->CR |= DMA_SxCR_EN; // Enable
}
void loop() {
Serial.print(ADC1->DR);
Serial.print(" ");
Serial.print(dataPoints[0]);
Serial.print(" ");
Serial.print(dma_active);
Serial.print(" ");
Serial.println(DMA2_Stream4->NDTR);
delay(100);
}
void DMA2_Stream4_IRQHandler(void) {
dma_active = 0;
}
I used ADC+DMA on STM32F3's successfully, but I cannot get it to work on this F7.
The clock for GPIOA gets enabled, and PA3 is set to analog input.
The clock for the ADC gets enabled. The ADC is set to continuous mode with DMA mode and continuous DMA requests. The input is PA3. The ADC conversion is started.
The DMA stream 4 is set to the correct channel for ADC1 (channel 0). The input and output addresses are set as well as the number of data to transfer and the memory increment mode gets enabled. Then the stream gets enabled.
I am not sure what I step I am missing here.
I would really appreciate your help!
EDIT #2
I accidently mistook channel for stream and so I had the wrong channel selected for the DMA (channel 4 instead of channel 0 for ADC1 in DMA2 Stream 4). That was the main issue why it did not work.
Now it is working fine in double buffer mode, except for one thing:
When I enable the transfer complete interrupt, the program is no longer working. It is only writing one letter via Serial.print, the "s" from starting. No values are transmitted.
I made the interrupt so that it should just disable the DMA for now, but for some reason the interrupt seems to not work at all.
volatile static bool dma_active = 1;
#define maxSamples 512
int16_t dataPoints[maxSamples];
int16_t dataPoints2[maxSamples];
void setup() {
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN_Msk; // GPIOA clock enable
GPIOA->MODER |= (0b11 << GPIO_MODER_MODER3_Pos); // PA3 as analog input
Serial.begin(115200);
Serial.println("starting");
initDMA();
initADC();
}
void initADC() {
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN_Msk; // ADC1 clock enable
ADC1->CR2 |= ADC_CR2_ADON_Msk; // ADC on
ADC1->CR2 |= ADC_CR2_CONT_Msk; // continuous conversion mode
ADC1->CR1 |= ADC_CR1_EOCIE_Msk; // EOC interrupt
ADC1->CR1 &= ~(ADC_CR1_DISCEN_Msk); // discontinuous mode disabled
ADC1->CR1 &= ~(ADC_CR1_SCAN_Msk); // scan mode disabled
ADC1->CR2 |= ADC_CR2_DMA_Msk; // DMA mode
ADC1->CR2 |= ADC_CR2_DDS_Msk; // continuous DMA request
ADC1->SQR3 |= 0b11; // ADC1_IN3 = PA3
ADC1->SQR1 &= ~(0b1111 << ADC_SQR1_L_Pos); // 1 conversion
ADC1->CR2 |= ADC_CR2_SWSTART_Msk; // Start conversion
}
void initDMA() {
// DMA2 Stream4 : Channel 0 is ADC1
RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN_Msk; // DMA2 clock enable
DMA2_Stream4->CR &= ~(DMA_SxCR_EN_Msk); // Disable
while (DMA2_Stream4->CR & (1 << 0));
DMA2->HIFCR |= 0b111101; // clear flags
DMA2_Stream4->CR |= (0b11 << DMA_SxCR_PL_Pos); // Very high priority
DMA2_Stream4->PAR = (uint32_t)&(ADC1->DR); // Data source register
DMA2_Stream4->M0AR = uint32_t(&dataPoints); // Buffer 1
DMA2_Stream4->M1AR = uint32_t(&dataPoints2); // Buffer 2
DMA2_Stream4->NDTR = maxSamples; // Number of transferred data
DMA2_Stream4->CR &= ~(0b1111 << DMA_SxCR_CHSEL_Pos); // Channel 4
DMA2_Stream4->CR |= (0b01 << DMA_SxCR_PSIZE_Pos); // Source data size (00 = byte, 01 = half word, 10 = word)
DMA2_Stream4->CR |= (0b01 << DMA_SxCR_MSIZE_Pos); // Memory data size (00 = byte, 01 = half word, 10 = word)
// DMA2_Stream4->CR |= DMA_SxCR_TCIE_Msk; // Transfer complete interrupt enable
// DMA2_Stream4->CR |= DMA_SxCR_CIRC_Msk; // circular mode
DMA2_Stream4->CR |= DMA_SxCR_DBM_Msk; // double buffer mode
DMA2_Stream4->CR &= ~(DMA_SxCR_PINC_Msk); // no peripheral increment mode
DMA2_Stream4->CR |= DMA_SxCR_MINC_Msk; // memory increment mode
NVIC_EnableIRQ(DMA2_Stream4_IRQn);
DMA2_Stream4->CR |= DMA_SxCR_EN_Msk; // Enable
}
void loop() {
for (int i = 0; i < maxSamples; i++)
{
Serial.print(dataPoints[i]);
Serial.print(" ");
// Serial.print(dataPoints2[i]);
Serial.print(" ");
Serial.print(dma_active);
Serial.println("");
}
delay(2000);
}
void DMA2_Stream4_IRQHandler(void) {
if ((DMA2->HISR) & DMA_HISR_TCIF4_Msk)
{
DMA2_Stream4->CR &= ~DMA_SxCR_EN_Msk; // Disable
dma_active = 0;
DMA2->HIFCR |= 0b111101; // clear flags
}
}
First of all you do not clear the interrupt flag and the interrupt is being called all the time.
Same errors:
ADC1->SQR1 &= ~(0b1111 << ADC_SQR1_L); does not cleat the L in the SQR1 register.
It should be ADC1->SQR1 &= ~(ADC_SQR1_L_Msk << ADC_SQR1_L_Pos);
Same error everywhere: (for example)
0b01 << DMA_SxCR_PSIZE
in my .h file DMA_SxCR_PSIZE is 0x00001800 :)
And many many more :)
It's due to D and I Cache. Disable it.
Maybe you can put DMA initialization before ADC initialization