Örnek:STM32 ST_Lib ADC + DMA (Çok kanal)

Başlatan Klein, 18 Kasım 2012, 23:08:00

skara1214

Alıntı yapılan: magnetron - 18 Eylül 2016, 02:58:10
hocam tek kanal

100 kere ölçüm yapılacak

yani  DMA_InitStructure.DMA_BufferSize = 100;

bitince ADC duracak interrupt verecek

interrupt nasıl kuracağım ? kod örneği verebilir misiniz ?
o dediğin olur. Cube mx kullaniyorsan dmayi kurduktan sonra interrupti zaten otomatik geliyor.
Herkes ölür ama herkes gerçekten yaşamaz

erdem54

Merhaba
benim bir proje için farklı sensörlerden veri almam gerekeiyor verileri tek tek alırken herhangi bir problemle karşılaşmıyorum fakat iki farklı sensörden veri alırken ilk sefer doğru sonuç geliyor ardından sonuçlar hep hatalı dönüyor verinin birini SPI ile diğerini ise I2C ile alıyorum
bu problemi dma ile aşabilirmiyim

skara1214

#47
Hiç alakası yok dma işi kolaylaştırır caliayam birşey düzeltme ilkönce düzgün çalıştırın daha sonra dma ile ugrasirsiniz
Herkes ölür ama herkes gerçekten yaşamaz

muhittin_kaplan

@Klein Hocam Aşağıdaki kodu embitz ve stm32f103c8t6 kullanarak çalıştıramadım.
8 kanal yapmışsınız ama sadece 2 kanalına potans bağladım. (PA0-PA1) hep 4095 ölçüyorum. Acaba diyerek pa0 ı Gnd ye bağladım 4095 ölçmeye devam ettim.
(kanal sayısı vs değiştirmedim.) kodlar aşağıda.

/*
**
**                           Main.c
**
**
**********************************************************************/
/*
   Last committed:     $Revision: 00 $
   Last changed by:    $Author: $
   Last changed date:  $Date:  $
   ID:                 $Id:  $

**********************************************************************/
#include "stm32f10x_conf.h"

uint16_t ADC_ValArray[8]; // ADC değerlerimiz bu diziye atılacak



void ADC1_DMAConfig(void){
DMA_InitTypeDef DMA_InitStructure;

  DMA_Cmd(DMA1_Channel1,DISABLE);
  DMA_DeInit ( DMA1_Channel1);

  DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)0x4001244C; // ADC->DR Adresi
  DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t) ADC_ValArray; // hedef adresimiz
  DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC; // ADC kaynak. Veri yönü ADC -> Hafıza
  DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable; // ADC adresi sabit kalacak
  DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable; // Her değer alındığına memory adresi 1 artırılacak
  DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;  // Kaynaktan alınacak veri 16 bit
  DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord; // Hedef büyüklüğü 16 bit
  DMA_InitStructure.DMA_Mode = DMA_Mode_Circular; // 8 veri alındıktan sonra başa dönülecek.
  DMA_InitStructure.DMA_Priority = DMA_Priority_High ; // Kanal Önceliği yüksek. ( bu bize kalmış)
  DMA_InitStructure.DMA_M2M = DMA_M2M_Disable; // hafızadan hafızaya transfer kapalı.
  DMA_InitStructure.DMA_BufferSize = 8; // Alacağımız verisayısı 8 ( 8 kanal adc okuyacağız)

  DMA_Init(DMA1_Channel1, &DMA_InitStructure);
  DMA_Cmd(DMA1_Channel1,ENABLE);

}


//Hangi kanalların çevirime gireceğini ve hangi sırada çevirime gireceklerini ayarlayalım.
void adc_channel_config(void){
	ADC_RegularChannelConfig(ADC1,ADC_Channel_0, 1, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_1, 2, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_2, 3, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_3, 4, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_4, 5, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_5, 6, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_6, 7, ADC_SampleTime_239Cycles5);
	ADC_RegularChannelConfig(ADC1,ADC_Channel_7, 8, ADC_SampleTime_239Cycles5);
}

void init_adc1(void)
{
ADC_InitTypeDef  ADC_InitStructure;

  RCC_ADCCLKConfig(RCC_PCLK2_Div2);
  ADC_DeInit(ADC1);

  ADC_InitStructure.ADC_ScanConvMode = ENABLE; // tarama modu açık
  ADC_InitStructure.ADC_ContinuousConvMode = ENABLE; // sürekli çevirim yapacağız
  ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
  ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
  ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
  ADC_InitStructure.ADC_NbrOfChannel = 8; // sekiz kanal tarayacağız.
  ADC_Init(ADC1, &ADC_InitStructure);

  ADC_Cmd(ADC1, ENABLE);

  ADC_ResetCalibration(ADC1);
  while(ADC_GetResetCalibrationStatus(ADC1));

  ADC_StartCalibration(ADC1);
  while(ADC_GetCalibrationStatus(ADC1));
}

void init_gpio(void){
GPIO_InitTypeDef  GPIO_InitStructure;

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0 |GPIO_Pin_1 | GPIO_Pin_2 |GPIO_Pin_3 | \
		 	   GPIO_Pin_4 |GPIO_Pin_5 | GPIO_Pin_6 |GPIO_Pin_7 ;
GPIO_Init(GPIOA, &GPIO_InitStructure);

}

void init_rcc(void){
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO,ENABLE);
	RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1,ENABLE);
}

void CycleDelay(int Value){
    while (Value){
        __NOP();//ASM kod
        --Value;
    }
}

int main(void)
{
    SystemInit();

    init_rcc();
    init_gpio();
    adc_channel_config();
    init_adc1();
    ADC1_DMAConfig();
    ADC_DMACmd(ADC1,ENABLE);
    ADC_SoftwareStartConvCmd(ADC1,ENABLE); // çevirimi başlatıyoruz.
    while(1)
    {
        __NOP();// Breakpoint koymak için eklenmiştir.
    }

}

muhittin_kaplan

Yapı Olarak Ne farkı var ? bu çalışıyor.

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_dma.h"
#include "stm32f10x_tim.h"
#include "misc.h"
 
volatile char buffer[80] = {'\0'};
 
volatile short FLAG_ECHO = 0;
 
void TIM4_IRQHandler(void)
{
    if (TIM_GetITStatus(TIM4, TIM_IT_Update) != RESET)
    {
        FLAG_ECHO = 1;
        TIM_ClearITPendingBit(TIM4, TIM_IT_Update);
    }
}
 
void usart_init(void)
{
        /* Enable USART1 and GPIOA clock */
        RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);
 
        /* NVIC Configuration */
        NVIC_InitTypeDef NVIC_InitStructure;
        /* Enable the USARTx Interrupt */
        NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
        NVIC_Init(&NVIC_InitStructure);
 
 
        /* Configure the GPIOs */
        GPIO_InitTypeDef GPIO_InitStructure;
 
        /* Configure USART1 Tx (PA.09) as alternate function push-pull */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
 
        /* Configure USART1 Rx (PA.10) as input floating */
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
        GPIO_Init(GPIOA, &GPIO_InitStructure);
 
        /* Configure the USART1 */
        USART_InitTypeDef USART_InitStructure;
 
      /* USART1 configuration ------------------------------------------------------*/
        /* USART1 configured as follow:
              - BaudRate = 115200 baud
              - Word Length = 8 Bits
              - One Stop Bit
              - No parity
              - Hardware flow control disabled (RTS and CTS signals)
              - Receive and transmit enabled
              - USART Clock disabled
              - USART CPOL: Clock is active low
              - USART CPHA: Data is captured on the middle
              - USART LastBit: The clock pulse of the last data bit is not output to
                               the SCLK pin
        */
        USART_InitStructure.USART_BaudRate = 115200;
        USART_InitStructure.USART_WordLength = USART_WordLength_8b;
        USART_InitStructure.USART_StopBits = USART_StopBits_1;
        USART_InitStructure.USART_Parity = USART_Parity_No;
        USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
        USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
 
        USART_Init(USART1, &USART_InitStructure);
 
        /* Enable USART1 */
        USART_Cmd(USART1, ENABLE);
 
        /* Enable the USART1 Receive interrupt: this interrupt is generated when the
             USART1 receive data register is not empty */
        //USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);
}
 
void USARTSend(const unsigned char *pucBuffer)
{
    while (*pucBuffer)
    {
        USART_SendData(USART1, *pucBuffer++);
        while(USART_GetFlagStatus(USART1, USART_FLAG_TC) == RESET)
        {
        }
    }
}
 
void SetSysClockTo72(void)
{
    ErrorStatus HSEStartUpStatus;
    /* SYSCLK, HCLK, PCLK2 and PCLK1 configuration -----------------------------*/
    /* RCC system reset(for debug purpose) */
    RCC_DeInit();
 
    /* Enable HSE */
    RCC_HSEConfig( RCC_HSE_ON);
 
    /* Wait till HSE is ready */
    HSEStartUpStatus = RCC_WaitForHSEStartUp();
 
    if (HSEStartUpStatus == SUCCESS)
    {
        /* Enable Prefetch Buffer */
        //FLASH_PrefetchBufferCmd( FLASH_PrefetchBuffer_Enable);
 
        /* Flash 2 wait state */
        //FLASH_SetLatency( FLASH_Latency_2);
 
        /* HCLK = SYSCLK */
        RCC_HCLKConfig( RCC_SYSCLK_Div1);
 
        /* PCLK2 = HCLK */
        RCC_PCLK2Config( RCC_HCLK_Div1);
 
        /* PCLK1 = HCLK/2 */
        RCC_PCLK1Config( RCC_HCLK_Div2);
 
        /* PLLCLK = 8MHz * 9 = 72 MHz */
        RCC_PLLConfig(0x00010000, RCC_PLLMul_9);
 
        /* Enable PLL */
        RCC_PLLCmd( ENABLE);
 
        /* Wait till PLL is ready */
        while (RCC_GetFlagStatus(RCC_FLAG_PLLRDY) == RESET)
        {
        }
 
        /* Select PLL as system clock source */
        RCC_SYSCLKConfig( RCC_SYSCLKSource_PLLCLK);
 
        /* Wait till PLL is used as system clock source */
        while (RCC_GetSYSCLKSource() != 0x08)
        {
        }
    }
    else
    { /* If HSE fails to start-up, the application will have wrong clock configuration.
     User can add here some code to deal with this error */
 
        /* Go to infinite loop */
        while (1)
        {
        }
    }
}
 
//=================================================================================
volatile uint16_t ADCBuffer[] = {0xAAAA, 0xAAAA, 0xAAAA, 0xAAAA};
 
void ADC_DMA_init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    ADC_InitTypeDef ADC_InitStructure;
    DMA_InitTypeDef DMA_InitStructure;
 
    RCC_ADCCLKConfig(RCC_PCLK2_Div6);
    /* Enable ADC1 and GPIOA clock */
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_AFIO | RCC_APB2Periph_GPIOA, ENABLE);
    RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1 , ENABLE );
 
    DMA_InitStructure.DMA_BufferSize = 4;
    DMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    DMA_InitStructure.DMA_M2M = DMA_M2M_Disable;
    DMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)ADCBuffer;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)&ADC1->DR;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_Init(DMA1_Channel1, &DMA_InitStructure);
    DMA_Cmd(DMA1_Channel1 , ENABLE ) ;
 
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_4 | GPIO_Pin_5 | GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    ADC_InitStructure.ADC_ContinuousConvMode = ENABLE;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_NbrOfChannel = 4;
    ADC_InitStructure.ADC_ScanConvMode = ENABLE;
    ADC_Init(ADC1, &ADC_InitStructure);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_4, 1, ADC_SampleTime_7Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_5, 2, ADC_SampleTime_7Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_6, 3, ADC_SampleTime_7Cycles5);
    ADC_RegularChannelConfig(ADC1, ADC_Channel_7, 4, ADC_SampleTime_7Cycles5);
    ADC_Cmd(ADC1 , ENABLE ) ;
    ADC_DMACmd(ADC1 , ENABLE ) ;
    ADC_ResetCalibration(ADC1);
 
    while(ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
 
    while(ADC_GetCalibrationStatus(ADC1));
    ADC_SoftwareStartConvCmd ( ADC1 , ENABLE ) ;
}
//=================================================================================
 
int main(void)
{
    SetSysClockTo72();
 
    const unsigned char mytext[] = " Hello World!\r\n";
 
    //USART
    usart_init();
    USARTSend(mytext);
 
    //ADC
    ADC_DMA_init();
 
    // TIMER4
    TIM_TimeBaseInitTypeDef TIMER_InitStructure;
    NVIC_InitTypeDef NVIC_InitStructure;
 
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4, ENABLE);
 
    TIM_TimeBaseStructInit(&TIMER_InitStructure);
    TIMER_InitStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIMER_InitStructure.TIM_Prescaler = 7200;
    TIMER_InitStructure.TIM_Period = 5000;
    TIM_TimeBaseInit(TIM4, &TIMER_InitStructure);
    TIM_ITConfig(TIM4, TIM_IT_Update, ENABLE);
    TIM_Cmd(TIM4, ENABLE);
 
    /* NVIC Configuration */
    /* Enable the TIM4_IRQn Interrupt */
    NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);
 
    while (1)
    {
        if (FLAG_ECHO == 1) {
            sprintf(buffer, "\r\n%d : %d : %d : %d\r\n", ADCBuffer[0], ADCBuffer[1], ADCBuffer[2], ADCBuffer[3]);
            USARTSend(buffer);
            FLAG_ECHO = 0;
        }
    }
}

halil82ist

Stm32 dmaları konusunda tecrübeli arkadaşlara bir sorum olacak . stm32f0 serisi kullanıyorum . Dmayı çalıştırdım herhangi bir sorun yok . Adc ölçümleri düzgün bir şekilde alabiliyorum . Sorun şurada başlıyor . Dma kesmesinde işlemcinin ne kadar süre kaldığını görmek istiyorum . bu sebeple bir çıkış pinini set edince işlemci reset atıyor . aynı pin ana programda düzgün çalışıyor . Dma kesmesi içinde herhangi bir çıkış pinini değiştirince aynı sorun bunun bir çözümü var mı neden olabilir ?

skara1214

Herkes ölür ama herkes gerçekten yaşamaz

halil82ist

debug'ta en son resete hangi komuttan sonra girdiğini görebiliyormuyuz ? veya nasıl bakacağız

skara1214

debugu açın adım adım yürütün. nerede reset attığını göreceksiniz
Herkes ölür ama herkes gerçekten yaşamaz

crazy

#54
#include "stm32f10x.h"                  // Device header

 uint16_t   ADC_ConvertedValue[8] = {0x00}; // adc degerlerini atacagımız dizi

static void  ADC1_GPIO_Config(void)
{
  RCC->APB2ENR |= 0x00000214;//PORTC ->PORTA  -> ADC1 Clock Enabled.
  GPIOC->CRH   &= 0x00000000;//
  GPIOA->CRL   &= ~0xCCCCCCCC; //PORTA 0-7 analog giris
  GPIOC->CRH   |= 0x00300000;//PC13 cikis 50Mhz
  
	
 
}

static void ADC1_Mode_Config(void)
{	
  RCC->AHBENR  |= 0x00000001; //DMA1 Clock aktif
  DMA1_Channel1->CCR     = 0x00000000; //DMA1 Channel1 kapali 
  DMA1_Channel1->CPAR   |= (uint32_t)0x4001244C;
  DMA1_Channel1->CMAR   |= (uint32_t) &ADC_ConvertedValue; //hedef adresimiz
  DMA1_Channel1->CNDTR   = 8; //Alacağımız verisayısı 8 ( 8 kanal adc okuyacağız)
  DMA1_Channel1->CCR    |= 0x000025A0;  
  DMA1_Channel1->CCR    |= 0x00000001; //DMA1 Channel1 acik
  ADC1->SMPR2 |= (7<<0)|(7<<3)|(7<<6)|(7<<9)|(7<<12)|(7<<15)|(7<<18)|(7<<21); //239.5 cycle 
  ADC1->SQR1   |= 0x00700000; // // sekiz kanal tarayacağız.
  ADC1->SQR2 |= 0x000000E6; //6 VE7 KANAL
  ADC1->SQR3 |= 0x0A418820;	//tarama, 0, 1, 2, 3, 4, 5, 			
  ADC1->CR1   = 0x00000100;                       // use independant mode, SCAN mode
  ADC1->CR2   = 0x000E0103;                       // use data align right,continuous conversion
                                                  // EXTSEL = SWSTART
                                              // enable ADC, DMA mode, no external Trigger
  ADC1->CR2    |=  1 <<  3;             // Initialize calibration registers
  while (ADC1->CR2 & (1 << 3));         // Wait for initialization to finish
  ADC1->CR2    |=  1 <<  2;             // Start calibration
  while (ADC1->CR2 & (1 << 2));         // Wait for calibration to finish

  ADC1->CR2  |= 0x00500000;          // start SW conversion
	
}



int main()
{

  ADC1_GPIO_Config();
  ADC1_Mode_Config();
	
	while(1)
	{
	   if(ADC_ConvertedValue[0]<1500)
		 {
			 GPIOC->ODR |= (1<<13);
		 
		 }
		 
		 else
			 
		 {
		   GPIOC->ODR &= ~(1<<13);
		 
		 }
		
	}
}




Klein hocanın std_lib ADC + DMA (Çok kanal)örneğini  register seviyesinde çalıştırdım, ADC1->SQR2 |= 0x000000E6; //6 VE7 KANAL
  ADC1->SQR3 |= 0x0A418820;   //tarama, 0, 1, 2, 3, 4, 5,    hangi kanalların çevirime gireceğini ve hangi sırada çevirime gireceklerini register seviyesinde nasıl ayarlıyoruz, acaba mantığı nedir ?

pax

Bir noktayı tam anlayamadım : kanal çevrimi bittiği zaman DMA bu değeri alır deniyor. DMA Kanal çevriminin bittiğini nasıl anlıyor.

bymrz

Alıntı yapılan: Klein - 06 Eylül 2013, 15:14:38DMA ayarla,
ADC çalıştır
çevirimi başlat. bu sıralamaya dikkat ettin mi?.



Hocam gözünün yağını yeyim senin :)
2 gündür kafayı yiyordum...

Hızlı olsun diye kodları STM32 Cube MX üzerinden ürettim. Yeni versiyonunda da salak gibi DMA init rutinini ADCInit rutininden sonrasına koyuyordu. Dediğiniz gibi yaptım düzeldi...

Koskoca ST bir de,  :P
Cube MX e güvenilemeyeceği bir kez daha ispatlandı...