Page 1 of 1

ADC interrupt handlers and vector table jumps

Posted: Sat Jan 07, 2023 3:36 am
by Ray M.
Hello I'm a new member. I'm using F303 nucleo and have got myself cornered in a large and complex program and now I must use ADC interrupts, mostly the watch dog.

The core doesn't seem to support it with the usual attachInterrupt method. I can make an example work using CubeMX, but porting my program over is going to be a big learning curve as i have never used HAL/cube before. I have tried low level register approach setting up ADC and NVIC.

The problem is I can't figure out how to get address (or offset) of my IRQHandler hooked into the vector table so that it invokes my function when triggered. I know I will have to manage the context saving and clean up myself. It looks like I'm about to go down the hole of compiler directives and linker scripts, which I know very little about.

Code: Select all

//*********** ISR handlers start here *************************
  //void ADC1_2_IRQnHandler(void){
    //void __irq_adc(void) {
  extern "C" void ADC1_2_IRQHandler(void){
    ADC_Flag++;
  }
Oddly, the extern "C" handler causes a duplicate function name error. I guess Arduino is using it.

My other method is using the ADC watchdog signal(s) which are internally connected to the ETR function of Timer 8.
I set the timer/counter to ( 2^16) -1 then the ADC watchdog signal increments timer 8 and causes an overflow.
and I catch the timer interrupt

Code: Select all

  //****** timer8 interrupts enable *********
  MainTim8->attachInterrupt(pseudoADC_IT_Handler);  //Phase A zero crossing trigger. 

  //*******start timer 8 **********
  TIM8->CR1 |= 0X0001; //start - enable 
  
My questions are:
1. Is there an elegant way?
2. Are there any examples of how to modify the linker? That I can understand hopefully :oops:
3. Am I missing something simple?

Edit;
I see this https://www.stm32duino.com/viewtopic.php?f=41&t=110 now but it's a litle over my head.
can I mix this into an Arduino sketch?

Re: ADC interrupt handlers and vector table jumps

Posted: Sun Jan 08, 2023 12:09 am
by Ray M.
Update:

So I have it working now. The interrupt wasn't triggering after all I had a register set wrong.
I have some working code now. It is low level stuff and a couple magic numbers but works.

This example uses a Nucleo F303 with a 10 KΩ pot connected to 3.3V, GND, and wiper to PC2.
connect a scope to the LED pin PA5 if you want to check the conversion time and latency.

Code: Select all


/***************** ADC2 watchdog 1 example. polling and Interrupt working ************
  by Ray
 STM32F303RE Nucleo board 
 regular single conversion of chan 8 
 Pot connected to PC2 <-> ADC2 ch 8 
 watchdog 1 monitors ch 8. Sets if PC2 is > 1/2 Vcc. 
 */ 
   
   volatile int ADC2wd_flag;
   int AWD2_1_flag;
   
void setup() {
  // put your setup code here, to run once:
  // Arduino core may set these registers as needed, But also added here for some possible low level uses.
  RCC->AHBENR |= 0x300E0003;    //Clock enable ADC 1,2,3,4 GPIO ports A,B,C and DMA1,2
  RCC->APB1ENR |= 0x24000007;   //Turn on some clocks, DAC1,2 Timer 2,3,4 enabled
  RCC->APB2ENR |= 0x00027001;   //Clock enable, Tim8, Tim16, SPI1, USART1, SYSCFGEN (required by COMP)

  Serial.begin(115200); //start serial PA2 TX. PA3 RX. to st-Link which is USB converter
  delay(20);            //let USB get going

  pinMode(PC2, INPUT);          // Signal to sample input
  //pinMode(PA11, INPUT);       //idle validation in
  //pinMode(PB3, INPUT);        //phase A in
  //pinMode(PB7, OUTPUT);       //Tachometer out
  pinMode(PB15, OUTPUT);        //O'scope debug out and latency check
  pinMode(PC13, INPUT);         //user push button sw on nucleo board
  pinMode(PA5, OUTPUT);         //LED on nucleo
  
  //********** set up ADC2 *******

  RCC->CFGR2 |= 0x00000100;     //ADC1,2 clock source 72 MHZ .
  GPIOC->MODER |= 0x00000FFF;    //PC0,1,2,3,4,5 set as analog inputs
  ADC2->CR = 0x00000000;       //turn on Analog voltage regulator sequence
  ADC2->CR |= 0x10000000;
  delay(1);
  ADC2->CR |= 0x90000000;       //calibrate ADC2
  delay(1);
  //              12 bit resolution right     non continous conv               overwrite old              
  ADC2 -> CFGR |= (0b00 << ADC_CFGR_RES_Pos) & (0 << ADC_CFGR_CONT_Pos) | (1 << ADC_CFGR_OVRMOD_Pos);  
  //                watch chan 8                enable watchdog 1           watch 1 chan only
  ADC2 -> CFGR |= (8 << ADC_CFGR_AWD1CH_Pos) | (1 <<ADC_CFGR_AWD1EN_Pos) | (1 << ADC_CFGR_AWD1SGL_Pos); //0x20C00000 
  ADC2 -> TR1 = 0x08000000;     // high thres. 1/2 Vcc, low thres 0.
  ADC2 -> SMPR1 = 0b00110110110110110110110110110000; //  use for higher impedance source eg. 10K pot
  ADC2 -> SMPR2 = 0b00000110110110110110110110110110; //181.5 ADC clock cycle sample time, about 3uS per convertion  
  ADC2->CR |= 0x00000001;       //enable ADC2
  delay(1);

  ADC2 -> SQR1 |= (0b0000 << ADC_SQR1_L_Pos) | (8 << ADC_SQR1_SQ1_Pos);   //number of conversions = 1, chan to use = 8
  // ADC2 -> SQR1 |= 6 << ADC_SQR1_SQ1_Pos;      //ch6 = PC0 = TPS
  // ADC2 -> SQR1 |= 7 << ADC_SQR1_SQ2_Pos;      //ch7 = PC1 = MAP
  // ADC2 -> SQR1 |= 8 << ADC_SQR1_SQ3_Pos;      //ch8 = PC2 = AUX1
  //ADC2 -> SQR1 |= 9 << ADC_SQR1_SQ4_Pos;      //ch9  = PC3 = OIL
  //  ADC2 -> SQR2 |= 5 << ADC_SQR2_SQ5_Pos;      //ch5 = PC4 = spare
  //ADC2 -> SQR1 |= 11 << ADC_SQR1_SQ1_Pos;     //ch11 = PC5 = amps
 
  //***** turn on interrupt ******    
  ADC2-> IER |= (1<< ADC_IER_AWD1IE_Pos); //0x00000080;   //enable watchdog 1 interrupt
  NVIC_SetPriority(ADC1_2_IRQn, 0);
  NVIC_EnableIRQ(ADC1_2_IRQn);

   //Serial.println(ADC2->CFGR, HEX);
   interrupts(); 
  Serial.println("start");

}//setup done

//*********** ISR handler *************************
 /******** watchdog interrupt method *******/  
  //extern "C" void ADC1_2_IRQnHandler(void){  // works but  crashes program need context save restore and cleanup
  extern "C" void ADC1_2_IRQHandler(void){    // WORKS! notice no 'n'   
   if(ADC2->ISR & 0x00000080){  //check ADC2 AWD1 flag 
    GPIOA->BRR = (1<<5);       //connect O'scope to PA5 for response time  
    ADC2->ISR = 0x00000080;    //write '1' resets the flag: important not "|="  rc-w1 type register    
    AWD2_1_flag = 1;           //A/D chan 8 PC2 result out of bounds    
    ADC2wd_flag ++;            //times interrupt was processed
  }
}

void loop() {
  // put your main code here, to run repeatedly:
  /**** watchdog polling method *****/
   if(ADC2->ISR & 0x00000080){   //check ADC2 AWD1 flag 
    AWD2_1_flag = 1;            //A/D chan 8 PC2 result out of bounds
    ADC2->ISR = 0x00000080;    //write '1' resets the flag: important not "|="  rc-w1 type register
  }
  else if((ADC2->ISR & 0x00000080) == 0){
    AWD2_1_flag = 0;
     }

 //start a new conversion     
      ADC2 -> CR |= ADC_CR_ADSTART;
      GPIOA->BSRR = (1<<5);      //o'scope debugging / response time    
      delay(200); //just wait around or check for conversion completed if in a hurry
     
     //printout    
      Serial.print(" ADCVal_PC2 ");
      Serial.print(ADC2->DR);
      Serial.print(" AWD1 ");
      Serial.print(AWD2_1_flag);
      Serial.print(" interrupted ");
      Serial.println(ADC2wd_flag);

}//end of program



Enjoy, Comments welcome.

Cheers Ray.