STM32H747 Регистр реализовал АЦП с DMA
Я использую H747 на Arduino Portenta H7. Я пытаюсь реализовать АЦП и DMA вместе. АЦП должен работать в непрерывном режиме и сканировать 2 канала.
void ADC_Init (void){
/******Enable ADC Voltage regulator******/
ADC1->CR&= ~(1<<29); //DEEPPWD set to zero to set adc not deep power down
ADC1->CR|= ADC_CR_ADVREGEN; //Turn on voltage regulator ADC |=(1<<28)
digitalWrite(LEDR,LOW);
//while (!(ADC1->ISR & (1<<12))); //LDORDY: ADC LDO output voltage ready bit
/******Enable ADC CLOCK******/
RCC->AHB1ENR|=(1<<5);//ADC peripheral clock enable
//RCC->D3CCIPR&= ~(7<<16)// ADCSEL[1:0]: SAR ADC kernel clock source selection default
ADC1->CR|=(3<<8);
/******Set the prescalar******/
ADC12_COMMON->CCR &= ~(15<<18);// PRESC[3:0]: ADC prescaler 0000: input ADC clock not divided
ADC12_COMMON->CCR &= ~(3<<16);// CKMODE[3:0]: ADC clock mode
/******Set Scan Mode Data Management and resolution******/
ADC1->CFGR|=(6<<2); //RES[2:0]: Data resolution 110=12bits
ADC1->CFGR|=(3<<0); //DMNGT[1:0]: Data Management configuration 11: DMA Circular Mode selected
//ADC regular sequence register 1
ADC1->SQR1|=(1<<0); // L[3:0]: Regular channel sequence length 1=2 conversions
ADC1->SQR1&= ~(0<<6);// SQ1[4:0]: 1st conversion in regular sequence channel number =0
ADC1->SQR1&= ~(1<<12);// SQ1[4:0]: 2st conversion in regular sequence channel number =1
/******Set the Continuous Conversion, ******/
ADC1->CFGR|=(1<<13); //CONT: Single / continuous conversion mode for regular conversions
/******Set the Sampling Time for the channels in ADC_SMPRx******/
ADC1->SMPR1&= ~(7<<0);//SMP0[2:0]: Channel 0 sampling time selection 000: 1.5 ADC clock cycles
ADC1->SMPR1&= ~(7<<3);//SMP1[2:0]: Channel 1 sampling time selection 000: 1.5 ADC clock cycles
ADC1->PCSEL|= (1<<0);//PCSEL[19:0] :Channel 0 (VINP[i]) pre selection
ADC1->PCSEL|= (1<<1);//PCSEL[19:0] :Channel 1 (VINP[i]) pre selection
/******Set singleEnded Input******/
ADC1->DIFSEL&= ~(1<<0); //DIFSEL[19:0]: Differential mode for channels 19 to 0
ADC1->DIFSEL&= ~(1<<1); //DIFSEL[19:0]: Differential mode for channels 19 to 0
/******Enable ADC******/
ADC1->ISR|=(1<<0); // Reset ADC ready flag
ADC1->CR|= ADC_CR_ADEN; //Enable ADC
while (!(ADC1->ISR & (1<<0))); //Wait for ready flag
ADC1->ISR|=(1<<0);}
DMA должен перехватывать выходные данные adc и перемещать их в память.
void DMA_Init (void){
RCC->AHB1ENR |= (1<<0);
DMA1_Stream0->CR &= ~(0x1 << 0);
while (DMA1_Stream0->CR & 0x1);
DMA1_Stream0->CR |= (0x3 << 16);
DMA1_Stream0->CR &= ~(3<<6); //Bits 7:6 DIR[1:0]: data transfer direction
DMA1_Stream0->CR |= (1<<8); //CIRC: circular mode
DMA1_Stream0->CR |= (1<<10); // MINC = 1;
DMA1_Stream0->CR |= (1<<11)|(1<<13); // MSIZE[1:0]: memory data size PSIZE[1:0]: peripheral data size
DMA1_Stream0->CR &= ~(7<<25); // Channel 0 selected
DMAMUX1_Channel0->CCR |=(9<<0);
DMA1_Stream0->CR |= (0x1 << 4);
DMA1_Stream0->CR |= (0x1 << 3);
NVIC_EnableIRQ(DMA1_Stream0_IRQn);
}
void DMA_Config (uint32_t srcAdd, uint32_t destAdd, uint16_t s){
DMA1_Stream0->NDTR = s; // Set the size of the transfer
DMA1_Stream0->PAR = srcAdd; // Source address is peripheral address
DMA1_Stream0->M0AR = destAdd; // Destination Address is memory address
// Enable the DMA Stream
DMA1_Stream0->CR |= (1<<0); // EN =1
}
При запуске кода он зависает в ADCStart() на ADC1->CR|= ADC_CR_ADON;
void setup() {
SystemClock_Config();
ADC_Init();
DMA_Init();
DMA_Config ((uint32_t ) &ADC1->DR, (uint32_t) RxData, 3);
ADC_start();
digitalWrite(LEDB,LOW);
Serial.begin(9600); //for debuging
}
void ADC_start (void){
while ((ADC1->CR & (1<<1)));
ADC1->CR|= ADC_CR_ADON;
}
Моя процедура неверна?
- настроить адк
- настроить прямой доступ к памяти
- начать adc
Я все еще новичок в stm32, поэтому я использовал https://controllerstech.com/dma-with-adc-using-registers-in-stm32/ в качестве руководства и справочного руководства по stm32h7.
1 ответ
Не уверен, что вам все еще нужен ответ на ваш вопрос. Вот что я сделал с вашим кодом:(у меня не установлена библиотека для портента, поэтому я объявил все регистры выше, я француз, поэтому некоторые из моих комментариев написаны на французском языке, я извиняюсь за это...).
#include <Arduino.h>
#include <string.h>
#define HWREG(x) (*((volatile uint32_t *)(x)))
//REGISTRE DEF---------------------------------------------------
//REG DMA1---------------------------------------------
volatile uint32_t* const DMA_LISR = (uint32_t *) 0x40020000;
volatile uint32_t* const DMA_HISR = (uint32_t *) 0x40020004;
volatile uint32_t* const DMA_LIFCR = (uint32_t *) 0x40020008;
volatile uint32_t* const DMA_HIFCR = (uint32_t *) 0x4002000C;
volatile uint32_t* const DMA_S1CR = (uint32_t *) 0x40020028;
volatile uint32_t* const DMA_S1NDTR = (uint32_t *) 0x4002002C;
volatile uint32_t* const DMA_S1PAR = (uint32_t *) 0x40020030;
volatile uint32_t* const DMA_S1M0AR = (uint32_t *) 0x40020034;//Mem 1 no use since no double buffer in mem2mem
volatile uint32_t* const DMA_S1FCR = (uint32_t *) 0x4002003C;
uint32_t S1CR = *DMA_S1CR;
uint32_t S1FCR = *DMA_S1FCR;
uint32_t S1PAR = *DMA_S1PAR;
uint32_t S1M0AR = *DMA_S1M0AR;
uint32_t S1NDTR = *DMA_S1NDTR;
//REG DMAMUX-------------------------------------------
volatile uint32_t* const DMAMUX1_C1CR = (uint32_t *) 0x40020804;//adresse dmamux1_chan1
uint32_t C1CR = *DMAMUX1_C1CR;
//REG ADC----------------------------------------------
//--------------------ADC REGISTRES-------------------//
volatile uint32_t* const ADC_ISR = (uint32_t *) 0x40022000;//Interupt and status register
uint32_t ISR = *ADC_ISR;
volatile uint32_t* const ADC_IER = (uint32_t *) 0x40022004;//Interupt enable register
uint32_t IER = *ADC_IER;
volatile uint32_t* const ADC_CR = (uint32_t *) 0x40022008;//Control register
uint32_t CR = *ADC_CR;
volatile uint32_t* const ADC_CFGR = (uint32_t *) 0x4002200C;//COnfiguration register
uint32_t CFGR = *ADC_CFGR;
volatile uint32_t* const ADC_CFGR2 = (uint32_t *) 0x40022010;//2eme conf regrister
uint32_t CFGR2 = *ADC_CFGR2;
volatile uint32_t* const ADC_SMPR1 = (uint32_t *) 0x40022014; //Sample time reg (directement lié au temps de calc de l'ADC)
uint32_t SMPR1 = *ADC_SMPR1;
volatile uint32_t* const ADC_PCSEL = (uint32_t*) 0x4002201C;//channel preselection register on choisis un chan pour la conv
uint32_t PCSEL = *ADC_PCSEL;
volatile uint32_t* const ADC_DR = (uint32_t *) 0x40022040;//Registre où l'on stocke le resultat des conv
uint32_t DR = *ADC_DR;
volatile uint32_t* const ADC_DIFSEL = (uint32_t *) 0x400220C0;
uint32_t DIFSEL = *ADC_DIFSEL;
volatile uint32_t* const ADC_SQR1 = (uint32_t *) 0x40022030;
uint32_t SQR1 = *ADC_SQR1;
volatile uint32_t* const ADC1_CSR = (uint32_t *) 0x40022300;//ADC1 common status register
uint32_t CSR = *ADC1_CSR;
volatile uint32_t* const ADC1_CCR = (uint32_t *)0x40022308; //ADC1 Common Control Register
uint32_t CCR = *ADC1_CCR;
//REG RCC-----------------------------------------------
volatile uint32_t* const RCC_APB4ENR = (uint32_t *) 0x580244F4;//to enable sysconf
volatile uint32_t* const RCC_AHB4ENR =(uint32_t *) 0x580244E0;
volatile uint32_t* const RCC_AHB1ENR =(uint32_t *) 0x580244D8;
volatile uint32_t* const RCC_CR = (uint32_t *) 0x58024400;
volatile uint32_t* const RCC_CFGR = (uint32_t *) 0x58024410;
volatile uint32_t* const RCC_D1CFGR = (uint32_t *) 0x58024418;
volatile uint32_t* const RCC_D2CFGR = (uint32_t *) 0x5802441C;
volatile uint32_t* const RCC_D3CFGR = (uint32_t *) 0x58024420;
volatile uint32_t* const RCC_PLLCKSELR = (uint32_t *) 0x58024428;
volatile uint32_t* const RCC_PLLCFGR = (uint32_t *) 0x5802442C;
volatile uint32_t* const RCC_PLL1DIVR = (uint32_t *) 0x58024430;
volatile uint32_t* const RCC_PLL1FRACR = (uint32_t *) 0x58024434;
volatile uint32_t* const RCC_PLL2DIVR = (uint32_t *) 0x58024438;
volatile uint32_t* const RCC_PLL2FRACR = (uint32_t *) 0x5802443C;
volatile uint32_t* const RCC_PLL3DIVR = (uint32_t *) 0x58024440;
volatile uint32_t* const RCC_PLL3FRACR = (uint32_t *) 0x58024444;
volatile uint32_t* const RCC_CIER = (uint32_t *) 0x58024460;
//setup---------------------------------------------
int dataFromRegister = 0;
void SystemClock_Config();
void ADC_start();
void ADC_Init();
void DMA_Init();
void DMA_Config();
void setup(){
// put your setup code here, to run once:
Serial.begin(9600); //for debuging
digitalWrite(LEDR, LOW);
//Serial.println("Début");
SystemClock_Config();
//Serial.println("ClockConf");
ADC_Init();
//Serial.println("Init_ADC");
DMA_Init();
//Serial.println("Init_DMA");
DMA_Config ();
//Serial.println("Config_DMA");
ADC_start();
//Serial.println("ADC_Start");
digitalWrite(LEDB,LOW);
}
void loop() {
// put your main code here, to run repeatedly:
//
}
void ADC_start (void){
CR&=~(1 << 30);
*ADC_CR=CR;
//On a deja ADCALDIF en single ended inputs mode par nos initialisations précédentes
CR=(1<<16);//ADCALLIN calibration linéaire ET offset
*ADC_CR=CR;
//CR=0x90010001;
*ADC_CR|=(1<<31);//Lancer une calibration ADCAL=1
//dataFromRegister=*ADC_CR;
while(*ADC_CR & 0x80000000!=0x00000000){
digitalWrite(LEDR,HIGH);
delay(1000);
digitalWrite(LEDR,LOW);
delay(1000);
}digitalWrite(LEDR,HIGH);
//On attends que la calibration soit complète par un reset de ADCAL
//Processus de calibration terminé (Serial.println(/*dataFromRegister,BIN*/"calibration");)
/******Enable ADC******/
*ADC_ISR |= (1 << 0); // Reset ADC ready flag
*ADC_CR |=(1 << 0); //Enable ADC
while (!(*ADC_ISR & (1 << 0))); //Wait for ready flag
*ADC_ISR |= (1 << 0);
*ADC_CR |= (3 << 8);//11 boost if ADC Clock entre 25 et 50MHz
//ADSTART
CR|=(1<<2);
*ADC_CR=CR;
}
void ADC_Init (void) {
/******Enable ADC Voltage regulator******/
CR=0x00000000;//Fin du deep power down
*ADC_CR=CR;
CR=0x10000000;//ADC voltage regulator Enable
*ADC_CR=CR;
while(*ADC_CR & 0x00001000!=0x00001000){}//check volatage enable (peut etre remplacé par un :
//delayMicroseconds(5); mais c'est moins safe)
//Petit interlude Differentiel ou single ended (a faire avant ADEN)---------
DIFSEL=0x00000000;
*ADC_DIFSEL=DIFSEL;
//digitalWrite(LEDR, LOW);
//while (!(ADC1_ISR & (1<<12))); //LDORDY: ADC LDO output voltage ready bit
/******Calibrate ADC*********/
/******Enable ADC CLOCK******/
*RCC_AHB1ENR |= (1 << 5); //ADC peripheral clock enable
//RCC_D3CCIPR&= ~(7<<16)// ADCSEL[1:0]: SAR ADC kernel clock source selection default
digitalWrite(LEDB,LOW);
delay(2000);
digitalWrite(LEDB,HIGH);
delay(2000);
digitalWrite(LEDB,LOW);
delay(2000);
digitalWrite(LEDB,HIGH);
/******Set the prescalar******/
CCR=0x000F0000;
*ADC1_CCR=CCR;
/******Set Scan Mode Data Management and resolution******/
CFGR |= (6 << 2); //RES[2:0]: Data resolution 110=12bits
CFGR |= (3 << 0); //DMNGT[1:0]: Data Management configuration 11: DMA circular mode
*ADC_CFGR=CFGR;
//ADC regular sequence register 1
SQR1=0x00000000;//1st conv correspond au chan 0 (00000) et on réalise une conv par sequence de 1 conv(0000)
*ADC_SQR1=SQR1;
/******Set the Continuous Conversion, ******/
CFGR |= (1 << 13); //CONT: Single / continuous conversion mode for regular conversions
*ADC_CFGR=CFGR;
/******Set the Sampling Time for the channels in ADC_SMPRx******/
SMPR1 &= ~(7 << 0); //SMP0[2:0]: Channel 0 sampling time selection 000: 1.5 ADC clock cycles
*ADC_SMPR1=SMPR1;
PCSEL |= (1 << 0); //PCSEL[19:0] :Channel 0 (VINP[i]) pre selection
*ADC_PCSEL=PCSEL;
/******Set singleEnded Input******/
DIFSEL &= ~(1 << 0); //DIFSEL[19:0]: single ended mode for channel 0
*ADC_DIFSEL=DIFSEL;
}
void DMA_Init (void){
*RCC_AHB1ENR |= (1<<0);
*DMA_S1CR &= ~(1 << 0);
while (*DMA_S1CR & 0x1);
*DMA_S1CR |= (3 << 16);
*DMA_S1CR &= ~(3<<6); //Bits 7:6 DIR[1:0]: data transfer direction
*DMA_S1CR |= (1<<8); //CIRC: circular mode
*DMA_S1CR |= (1<<10); // MINC = 1;
*DMA_S1CR |= (1<<11)|(1<<13); // MSIZE[1:0]: memory data size PSIZE[1:0]: peripheral data size
*DMA_S1CR &= ~(7<<25); // Channel 0 selected
*DMAMUX1_C1CR |=(9<<0);
//*DMA_S1CR |= (1 << 4);//IT TC
//*DMA_S1CR |= (1 << 3);//IT HTC
//NVIC_EnableIRQ(DMA1_IRQn);
}
void DMA_Config (void){
*DMA_S1NDTR = 0x00000003; // Set the size of the transfer
*DMA_S1PAR = 0x40022040; // Source address is peripheral address
*DMA_S1M0AR = 0x30000000; // Destination Address is memory address
// Enable the DMA Stream
*DMA_S1CR |= (1<<0); // EN =1
}
void SystemClock_Config(void) {
/* Enable the floating-point unit. Any configuration of the
floating-point unit must be done here prior to it being enabled */
HWREG(0xE000ED88) = ((HWREG(0xE000ED88) & ~0x00F00000) | 0x00F00000);
/*Var reg declaration*/
uint32_t CR=*RCC_CR;
uint32_t CFGR=*RCC_CFGR;
uint32_t D1CFGR=*RCC_D1CFGR;
uint32_t D2CFGR=*RCC_D2CFGR;
uint32_t D3CFGR=*RCC_D3CFGR;
uint32_t PLLCKSELR=*RCC_PLLCKSELR;
uint32_t PLLCFGR=*RCC_PLLCFGR;
uint32_t PLL1DIVR=*RCC_PLL1DIVR;
uint32_t PLL1FRACR=*RCC_PLL1FRACR;
uint32_t PLL2DIVR=*RCC_PLL2DIVR;
uint32_t PLL2FRACR=*RCC_PLL2FRACR;
uint32_t PLL3DIVR=*RCC_PLL3DIVR;
uint32_t PLL3FRACR=*RCC_PLL3FRACR;
uint32_t CIER=*RCC_CIER;
/*------- Reset the RCC clock configuration to the default reset state -------*/
/* Set HSION bit */
CR = 0x00000001;
*RCC_CR = CR;
/* Reset CFGR register */
CFGR = 0x00000000;
*RCC_CFGR = CFGR;
/* Reset HSEON, CSSON , CSION,RC48ON, CSIKERON PLL1ON, PLL2ON and PLL3ON bits */
CR &= (uint32_t)0xEAF6ED7F;
*RCC_CR = CR;
/* Reset D1CFGR register */
D1CFGR = 0x00000000;
*RCC_D1CFGR = D1CFGR;
/* Reset D2CFGR register */
D2CFGR = 0x00000000;
*RCC_D2CFGR = D2CFGR;
/* Reset D3CFGR register */
D3CFGR = 0x00000000;
*RCC_D3CFGR = D3CFGR;
/* Reset PLLCKSELR register */
PLLCKSELR = 0x00000000;
*RCC_PLLCKSELR = PLLCKSELR;
/* Reset PLLCFGR register */
PLLCFGR = 0x00000000;
*RCC_PLLCFGR = PLLCFGR;
/* Reset PLL1DIVR register */
PLL1DIVR = 0x00000000;
*RCC_PLL1DIVR = PLL1DIVR;
/* Reset PLL1FRACR register */
PLL1FRACR = 0x00000000;
*RCC_PLL1FRACR = PLL1FRACR;
/* Reset PLL2DIVR register */
PLL2DIVR = 0x00000000;
*RCC_PLL2DIVR = PLL2DIVR;
/* Reset PLL2FRACR register */
PLL2FRACR = 0x00000000;
*RCC_PLL2FRACR = PLL2FRACR;
/* Reset PLL3DIVR register */
PLL3DIVR = 0x00000000;
*RCC_PLL3DIVR = PLL3DIVR;
/* Reset PLL3FRACR register */
PLL3FRACR = 0x00000000;
*RCC_PLL3FRACR = PLL3FRACR;
/* Reset HSEBYP bit */
CR &= (uint32_t)0xFFFBFFFF;
*RCC_CR = CR;
/* Disable all interrupts */
CIER = 0x00000000;
*RCC_CIER = CIER;
/* Change the switch matrix read issuing capability to 1 for the AXI SRAM target (Target 7) */
HWREG(0x51008108) = 0x000000001;
}
То, что я сделал не так, как вы, в основном откалибровал мой АЦП и добавлял ADSTART (мой код предназначен для несимметричного входа только на одном канале АЦП1). Я искренне думаю, что проблема связана с тем, что в ADC_Start() вы проверяете бит ADDIS вместо того, чтобы работать с битом ADSTART в регистре ADC_CR. В любом случае спасибо, вы помогли мне лучше понять конфигурацию DMA с помощью регистра, просто нуб :)