Forum > Embedded - ARM

STM32 getting ADC to work in FPC [solved]

(1/2) > >>

petex:
hello,
I converted the ADC module and parts of the RCC module from the STM electronics library (written in C) to free pascal.

Now that my debugger is working I am trying to get it to work. I have a simple example to read the ADC.

The control block for the ADC is located at $40012400 which agrees with the spec. However it looks like that when I write to the registers, the contents can not be read back.
I issue the convert command and wait forever for the status to change. I don't think this part of the memory map is enabled ??


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---    ADC_DeInit(ADC1); //Power-on default     RCC_APB2PeriphResetCmd(RCC_APB2Periph_ADC1, xENABLE);    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1, xENABLE);     ADC_InitStructure.ADC_Mode := ADC_Mode_Independent; //Independent conversion mode (single)    ADC_InitStructure.ADC_ScanConvMode := xDISABLE; //Convert single channel only    ADC_InitStructure.ADC_ContinuousConvMode := xDISABLE; //Convert 1 time    ADC_InitStructure.ADC_ExternalTrigConv := xDISABLE; //No external triggering    ADC_InitStructure.ADC_DataAlign := ADC_DataAlign_Right; //Right 12-bit data alignment    ADC_InitStructure.ADC_NbrOfChannel := 1; //single channel conversion    ADC_Init(ADC1, ADC_InitStructure);     ADC_TempSensorVrefintCmd(xENABLE); //wake up temperature sensor    ADC_Cmd(ADC1, xENABLE); //Enable ADC1    ADC_ResetCalibration(ADC1); //Enable ADC1 reset calibration register    while (ADC_GetResetCalibrationStatus(ADC1) = xRESET) do; //check the end of ADC1 reset calibration register     ADC_StartCalibration(ADC1); //Start ADC1 calibration    while (ADC_GetCalibrationStatus(ADC1) = xRESET) do; //Check the end of ADC1 calibration     ADC_RegularChannelConfig(ADC1, ADC_Channel_16, 1, ADC_SampleTime_1Cycles5); //Select 1.5 cycles conversion for channel 16    ADC_SoftwareStartConvCmd(ADC1, xENABLE); //Start ADC1 software conversion    while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) = xRESET) do; //Wail for conversion complete     an_val := ADC_GetConversionValue(ADC1); //Read ADC value    ADC_ClearFlag(ADC1, ADC_FLAG_EOC); //Clear EOC flag   

kupferstecher:
Hello petex,

which device are you using? There is a partial translation of the Standard Peripheral Library for the STM32F103.

https://github.com/Turro75/stm32lazarustemplate/blob/master/stm32f103fw.pas

I once wrote a little programm to capture analog signals, see below my ADC and timer unit. The conversion is automaticly triggered by the timer-overflow. The ADC value is collected in the interrupt. There are two ADCs running synchronously. Each with one channel. The clocks for ADC, GPIO and Timer you have to enable seperately.

Hope it helps.


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit uADC; {$mode objfpc} interface Procedure ADCInit(AChannelKanal1,AChannelKanal2: byte);Procedure ADCSingleConversion; var ADCOnConversionsComplete: Procedure; const AnalogKanal1Pin = GPIO_Pin_0;const AnalogKanal2Pin = GPIO_Pin_1;var   AnalogKanal1Port: TPortRegisters absolute PortB;var   AnalogKanal2Port: TPortRegisters absolute PortB; var  ADC1Result: uInt16;  ADC2Result: uInt16;   IMPLEMENTATION uses  stm32f103fw,STM32F10x_BitDefs,cortexm3; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++Procedure ADCInit(AChannelKanal1,AChannelKanal2: byte);var  ADC_InitStructure: TADC_InitTypeDef;  NVIC_InitStructure: TNVIC_InitTypeDef;  GPIO_InitStructure: TGPIO_InitTypeDef;begin   // Analog Eingaenge Kanal 1 und 2  GPIO_InitStructure.GPIO_Pin := AnalogKanal1Pin;  GPIO_InitStructure.GPIO_Mode := GPIO_Mode_AIN;    //Analog In  GPIO_InitStructure.GPIO_Speed := GPIO_Speed_50MHz;  GPIO_Init(AnalogKanal1Port, GPIO_InitStructure);   GPIO_InitStructure.GPIO_Pin := AnalogKanal2Pin;  GPIO_InitStructure.GPIO_Mode := GPIO_Mode_AIN;    //Analog In  GPIO_InitStructure.GPIO_Speed := GPIO_Speed_50MHz;  GPIO_Init(AnalogKanal1Port, GPIO_InitStructure);   // ADCCLK = PCLK2/2  RCC_ADCCLKConfig(RCC_PCLK2_Div4);   ////ADC1  ADC_InitStructure.ADC_Mode:= ADC_Mode_Independent;  //ADC1 unabhängig von ADC2, nicht dual-mode  ADC_InitStructure.ADC_ScanConvMode:= DISABLED;       //nicht Nur ein Kanal  ADC_InitStructure.ADC_ContinuousConvMode:= DISABLED;//Nur einzelne Wandlung  ADC_InitStructure.ADC_ExternalTrigConv:= ADC_ExternalTrigConv_T1_CC1;//Timer1 Kanal1 triggert  ADC_InitStructure.ADC_DataAlign:= ADC_DataAlign_Right;  ADC_InitStructure.ADC_NbrOfChannel:= 1;  ADC_Init(ADC1, ADC_InitStructure);   // ADC1 regular channel14 configuration  ADC_RegularChannelConfig(ADC1, AChannelKanal1, 1, ADC_SampleTime_28Cycles5);  // Enable ADC1 external trigger  ADC_ExternalTrigConvCmd(ADC1, ENABLED);  // Enable EOC interrupt  ADC_ITConfig(ADC1, ADC_IT_EOC, DISABLED);   // Enable ADC1  ADC_Cmd(ADC1, ENABLED);   {////Injected Kanäle  ADC_InjectedSequencerLengthConfig(ADC1,2);  ADC_InjectedChannelConfig(ADC1, AChannelKanal2,1,ADC_SampleTime_13Cycles5);  ADC_InjectedChannelConfig(ADC1, AChannelKanal2,2,ADC_SampleTime_13Cycles5);   // Interrupt für Injected-Gruppe  ADC_ExternalTrigInjectedConvConfig(ADC1, ADC_ExternalTrigInjecConv_T2_CC1);  ADC_ExternalTrigInjectedConvCmd(ADC1,ENABLED);  ADC_ITConfig(ADC1, ADC_IT_JEOC, ENABLED);  }   ////ADC2  ADC_InitStructure.ADC_Mode:= ADC_Mode_Independent;  //ADC1 unabhängig von ADC2, nicht dual-mode  ADC_InitStructure.ADC_ScanConvMode:= DISABLED;       //nicht Nur ein Kanal  ADC_InitStructure.ADC_ContinuousConvMode:= DISABLED;//Nur einzelne Wandlung  ADC_InitStructure.ADC_ExternalTrigConv:= ADC_ExternalTrigConv_T1_CC1;//Timer1 Kanal1 triggert  ADC_InitStructure.ADC_DataAlign:= ADC_DataAlign_Right;  ADC_InitStructure.ADC_NbrOfChannel:= 1;  ADC_Init(ADC2, ADC_InitStructure);   // ADC1 regular channel14 configuration  ADC_RegularChannelConfig(ADC2, AChannelKanal2, 1, ADC_SampleTime_28Cycles5);  // Enable ADC external trigger  ADC_ExternalTrigConvCmd(ADC2, ENABLED);  // Enable EOC interrupt  ADC_ITConfig(ADC2, ADC_IT_EOC, ENABLED);   // Enable ADC1  ADC_Cmd(ADC2, ENABLED);     ////ADC1+2  // ADC-NVIC-Konfiguration  NVIC.IP[ADC1_2_IRQn]:= $10;     //IRQ-Priority, right 4bits always to be zero,low value=high prio  NVIC.ISER[ADC1_2_IRQn shr 5] := 1 shl (ADC1_2_IRQn and $1F); //Enable Interrupt    //ADC_SoftwareStartConvCmd(ADC1, ENABLED);   // Enable ADC1 reset calibration register  //ADC_ResetCalibration(ADC1);  // Check the end of ADC1 reset calibration register  //while(ADC_GetResetCalibrationStatus(ADC1));   // Start ADC1 calibration  //ADC_StartCalibration(ADC1);  // Check the end of ADC1 calibration  //while(ADC_GetCalibrationStatus(ADC1));  end; //---------------------------------------------------------procedure ADCSingleConversion;begin  ADC_SoftwareStartConvCmd(ADC1, ENABLED);end;  //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//++ INTERRUPT SERVICE ROUTINEprocedure ADC_ISR; Alias: 'ADC1_and_ADC2_global_interrupt'; Interrupt; Public;var Timeout: Integer;begin  //comvisu.cmdLn('ADC1ISR');   Timeout:= 20;  while (ADC1.SR and ADC1.SR and ADC_SR_EOC) = 0  do begin Timeout:= Timeout-1; if Timeout = 0 then BREAK; end;   if (ADC1.SR and ADC_SR_EOC) > 0  then ADC1Result:= ADC1.DR else ADC1Result:= $FFFF;  if (ADC2.SR and ADC_SR_EOC) > 0  then ADC2Result:= ADC2.DR else ADC2Result:= $FFFF;   if assigned(ADCOnConversionsComplete) then ADCOnConversionsComplete;  ADC_ClearITPendingBit(ADC1,ADC_IT_EOC);  ADC_ClearITPendingBit(ADC2,ADC_IT_EOC); end; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ end.

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---unit uTimer1;{$mode objfpc}interfaceuses  STM32F10x_BitDefs, cortexm3; var Timer1OnTimerCompare: Procedure;     Procedure Timer1Init;    procedure Timer1SetPrescaler(AValue: uInt16);    procedure Timer1SetAutoReloadRegister(AValue: uInt16);    Procedure Timer1Enable;    Procedure Timer1Disable;  //##############################################################################IMPLEMENTATIONuses  stm32f103fw; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++procedure Timer1Init;var  GPIO_InitStructure: TGPIO_InitTypeDef;begin  Timer1OnTimerCompare:= nil;   //RCC enable for Timer1  RCC.APB2ENR:= RCC.APB2ENR or RCC_APB2ENR_TIM1EN;   ////Timer settings ----------------  //Various settings in default  Timer1.CR1:=   0;         //Control register 1; Wait with timer enable until settings complete  Timer1.CR2:=   0;         //Control register 2  Timer1.CCMR1:=                   //Capture/compare mode register;                 TIM_CCMR1_OC1PE   //oc preload enable              or TIM_CCMR1_OC1M_1  //PWM-Mode 1              or TIM_CCMR1_OC1M_2;  Timer1.CCER:=  TIM_CCER_CC1E; //Capture/compare enable register; CC1-output-enable}   //Capture compare Register um Flanken zu bekommen  Timer1.CCR1:= 2;   //Prescaler  Timer1.PSC:= 2000;  //AutoReload value  Timer1.Arr:= 10000;   Timer1.DIER:= TIM_DIER_CC1IE; //Compare 1 interrupt enable  Timer1.EGR:= Timer1.EGR or TIM_EGR_UG; //Werte übernehmen  Timer1.BDTR:= Timer1.BDTR or TIM_BDTR_MOE; //Output enable   // NVIC configuration  NVIC.IP[TIM1_CC_IRQn]:= $20;     //IRQ-Priority, right 4bits always to be zero,low value=high prio  NVIC.ISER[TIM1_CC_IRQn shr 5] := 1 shl (TIM1_CC_IRQn and $1F); //Enable Interrupt   //Enable Counter  Timer1.CR1:= Timer1.CR1 or TIM_CR1_CEN;   {a pwm mode in CCMR  CC1EN in Tim1_CCER }     // PA8 PWM-output  GPIO_InitStructure.GPIO_Pin := GPIO_Pin_8;  GPIO_InitStructure.GPIO_Speed := GPIO_Speed_50MHz;  GPIO_InitStructure.GPIO_Mode := GPIO_Mode_AF_PP;  GPIO_Init(PortA, GPIO_InitStructure);  end; //---------------------------------------------------------procedure Timer1SetPrescaler(AValue: uInt16);begin  Timer1Disable;  Timer1.PSC:= AValue;  Timer1Enable;end; //---------------------------------------------------------procedure Timer1SetAutoReloadRegister(AValue: uInt16);begin  Timer1Disable;  Timer1.Arr:= AValue;  Timer1Enable;end; //---------------------------------------------------------procedure Timer1Enable;begin Timer1.CR1:= Timer1.CR1 or TIM_CR1_CEN; end; //---------------------------------------------------------procedure Timer1Disable;begin Timer1.CR1:= Timer1.CR1 and not TIM_CR1_CEN; end; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++//++ INTERRUPT SERVICE ROUTINEprocedure TIM1_ISR; Alias: 'TIM1_Capture_Compare_interrupt'; Interrupt; Public;begin  Timer1.SR:= Timer1.SR and not TIM_SR_CC1IF;   if assigned(Timer1OnTimerCompare) then Timer1OnTimerCompare; end; //+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++end.

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---   //Timer1   Timer1Init;   Timer1OnTimerCompare:= @TimerCompareHandler;    //ADC   ADCInit(AnalogChannelKanal1,AnalogChannelKanal2);   ADCOnConversionsComplete:= @ADCConversionCompleteHandler;

petex:
that's great thanks.
I shall have a play about with this. The device is STM32F103C8T6.
I didn't realise there was a translation of the library around (I did ask!).
I am sure I am missing a vital piece of initialisation.

petex:
The previous code is not going to compile with my version of compiler, so i am doing some simple debugging on the existing code at the moment.

I am not convinced that the peripheral registers are getting updated at all. It works fine for variable memory @ 2000000. (I can do pointer manipulations and every thing is hunky dory.)

A simple example to illustrate the problem :

I have declared a record to map the peripheral registers for ADC 1:
ADC1_BASE seems to be an internal compiler definition (since I cannot find a definition of it anywhere in the code) that has a value of $4012400 as expected.
ie

#define   PERIPH_BASE   ((uint32_t)0x40000000)
#define   APB2PERIPH_BASE   (PERIPH_BASE + 0x10000)
#define   ADC1_BASE   (APB2PERIPH_BASE + 0x2400)

from the C code.


--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---const  ADC1 = ( ADC1_BASE );   type  ADC_TypeDef = record    SR: uint32_t;    CR1: uint32_t;    CR2: uint32_t;    SMPR1: uint32_t;    SMPR2: uint32_t;    JOFR1: uint32_t;    JOFR2: uint32_t;    JOFR3: uint32_t;    JOFR4: uint32_t;    HTR: uint32_t;    LTR: uint32_t;    SQR1: uint32_t;    SQR2: uint32_t;    SQR3: uint32_t;    JSQR: uint32_t;    JDR1: uint32_t;    JDR2: uint32_t;    JDR3: uint32_t;    JDR4: uint32_t;    DR: uint32_t;  end;  var  vADC1 : ADC_TypeDef absolute ADC1;   
Now in the code to set up the internal temperature measurement there is a code line :=

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} --- const  CR2_TSVREFE_Set = uint32_t($00800000);   {''' ''' ''' etc etc}     vADC1.CR2 := vADC1.CR2 or CR2_TSVREFE_Set;   
I put a break point here. I inspected vADC1.CR2 and it was zero. After oring it with $00800000 it is still zero. I would have thought that the register would be persistent and the value would change.

I tried the following and it does not work either, although "tmp" is updated correctly.

--- Code: Pascal  [+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i < x.length; i++) { x[i].style.maxHeight='none'; x[i].style.height = Math.min(x[i].clientHeight+15,306)+'px'; x[i].style.resize = "vertical";}};} ---     tmp :=  vADC1.CR2;    tmp :=  tmp or CR2_TSVREFE_Set;    vADC1.CR2 := tmp;  



kupferstecher:

--- Quote from: petex on January 30, 2021, 04:45:03 pm ---The previous code is not going to compile with my version of compiler, so i am doing some simple debugging on the existing code at the moment.
--- End quote ---
I just checked, my code compiles with the current stable version FPC3.2.0 (Revision 48091). The code I posted is not complete, though, just part of a project.


--- Quote ---I inspected vADC1.CR2 and it was zero.
--- End quote ---
The addresses look correct.
Did you try to read out CR2 and print it to the UART? Not that the debugger is fetching a wrong address.

Navigation

[0] Message Index

[#] Next page

Go to full version