Good day!
I write code for an STM32f401xB/C board using the arduino IDE. I compile my code with stm32duino.
I am trying to write an EXTI interrupt handler but compiler flags me that I am trying to overwrite the handler function.
How should I go about with this problem?
Here is my code:
#define echo_pin PB9
void setup()
{
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN;
GPIOB->MODER &= ~(0x000C0000);
GPIOB->PUPDR &= ~(0x000C0000);
GPIOB->PUPDR |= 0x00080000;
pinMode(PC13, OUTPUT);
digitalWrite(PC13, LOW);
//enable clock and power on the syscfgen register
RCC->APB2ENR |= RCC_APB2ENR_SYSCFGEN;
//there are 4 bits that have to be set to enable an interrupt
//there are 4 EXTICR register each 16 bits wide
//reset the bits in the register
SYSCFG->EXTICR[2] &= ~(0x00F0);
//set interrupt on pin PB9
SYSCFG->EXTICR[2] |= (0x0010);
//enables interupt on EXTI line
EXTI->IMR |= (1 << 9);
//set to trigger on both rising and falling edge, simulating the CHANGE
//flag in attachInterrupt() function
EXTI->RTSR |= (1 << 9);
EXTI->FTSR |= (1 << 9);
//enable the NVIC interrupt for EXTI9 to EXTI5 to lowest priority
NVIC_SetPriority(EXTI9_5_IRQn, 0x03);
NVIC_EnableIRQ(EXTI9_5_IRQn);
}
extern "C" { void EXTI9_5_IRQHandler(void){
//check the pending bit of the PB9 pin
if(EXTI->PR & (1 << 9)){
//reset the interrupt bit
EXTI->PR |= (1 << 9);
digitalWrite(PC13, HIGH);
}
}
}
void loop()
{
}
This is the error
home/admin1/.arduino15/packages/STMicroelectronics/tools/xpack-arm-none-eabi-gcc/10.3.1-2.3/bin/../lib/gcc/arm-none-eabi/10.3.1/../../../../arm-none-eabi/bin/ld: /tmp/arduino-sketch-E075A13F2405718203FB0E8501028A7D/libraries/SrcWrapper/stm32/interrupt.cpp.o: in function `EXTI9_5_IRQHandler':
interrupt.cpp:(.text.EXTI9_5_IRQHandler+0x0): multiple definition of `EXTI9_5_IRQHandler'; /tmp/arduino-sketch-E075A13F2405718203FB0E8501028A7D/sketch/Test Distance.ino.cpp.o:Test Distance.ino.cpp:(.text.EXTI9_5_IRQHandler+0x0): first defined here
collect2: error: ld returned 1 exit status
The issue you're running into is with your call to Extern "C". Calling Extern "C" prevents name mangling which is what the C++ compiler uses to differentiate between functions with the same name. What you need to do is use Extern "C" to provide a linkage to the original C function and then provide a separate definition. If you want to read a little more about Extern "C" here is a very brief intro. Depending on the version of the STM32F4 library you are using, you may also need to locate the "/stm32/{version}/libraries/SrcWrapper/src/stm32/interrupt.cpp" file and add __weak in front of the first definition of EXTI9_5_IRQHandler. This allows for a different definition of the function to be created but the original definition will remain as a fallback.
// Top of File
Extern "C" { void EXTI9_5_IRQHandler(void); }
// OTHER CODE
void EXTI9_5_IRQHandler(void){
//check the pending bit of the PB9 pin
if(EXTI->PR & (1 << 9)){
//reset the interrupt bit
EXTI->PR |= (1 << 9);
digitalWrite(PC13, HIGH);
}
}
This code cleans up the error but it is generally not common practice overwrite the default IRQHandlers. If you simply want to attach an interrupt service routine to the Arduino library for the STM32F4XX exposes a method called attachInterrupt. You can use it to assign a callback for an individual pin.
attachInterrupt({Pin#}, {callback_function}, RISING). Rising can be replaced with falling or both as the mode for when the interrupt is triggered.
Related
I am working on a project with PIC16F877 (using MPLABX). I use RB0 pin external interrupt and RB4 pin portb interrupt to detect zero cross detection. I did everything correct, in proteus simulation everything was okey. Then I set up the circuit on breadboard, the LCD wasnt displaying the numbers (just the white dots). I thought the problem is the RB0 and PORTB interrupt. I wrote a simple code just includeshe PORTB interrupt and LCD and simulated. Everything is okey until the interrupt occures, when interrupt comes the code stops. I am new to PIC, this is the code I wrote:
/*
* File: lcd_deneme_16f877a.c
* Author: BATUHAN
*
* Created on 28 Aral?k 2022 Çar?amba, 13:52
*/
#include <xc.h>
#include <stdio.h>
#include <stdint.h>
#pragma config FOSC = XT // Oscillator Selection bits (HS oscillator)
#pragma config WDTE = OFF // Watchdog Timer Enable bit (WDT disabled)
#pragma config PWRTE = ON // Power-up Timer Enable bit (PWRT enabled)
#pragma config BOREN = OFF // Brown-out Reset Enable bit (BOR disabled)
#pragma config LVP = OFF // Low-Voltage (Single-Supply) In-Circuit Serial Programming Enable bit (RB3 is digital I/O, HV on MCLR must be used for programming)
#pragma config CPD = ON // Data EEPROM Memory Code Protection bit (Data EEPROM code-protected)
#pragma config WRT = OFF // Flash Program Memory Write Enable bits (Write protection off; all program memory may be written to by EECON control)
#pragma config CP = ON // Flash Program Memory Code Protection bit (All program memory code-protected)
#define _XTAL_FREQ 4000000
void __interrupt() interrpt()
{
if(INTF)
{
uint8_t dummy = PORTB; // Read PORTB to end mismatch condition
INTF=0;
RD0=RD0^1;
}
if(RBIF==1 && RB4==1)
{
uint8_t dummy = PORTB; // Read PORTB to end mismatch condition
RBIF=0;
RD0=RD0^1;
}
}
void main(void)
{
TRISD=0X00;
PORTD=0X00;
TRISB=0b00010001;
PORTB=0X00;
INTCON=0b11011000; // GIE PEIE TMR0IE INTE RBIE TMR0IF INTF RBIF
OPTION_REGbits.nRBPU = 1;
INTEDG=1;
int V=0;
while(1)
{
V++;
__delay_ms(200);
}
return;
}
I tried the PORTB and RB0 interrupts separately and the problem still occurs.
What could be the problem. Thanks in advance
This is because your program stucks in interrupt routine due to the lack of proper handling of interrupts. You don't seem to handle the INT interrupt at all. For RB interrupt-on-change (IOC), you have to handle it sort of a little different and end the mismatch condition before clearing the flag. According to the PIC16F877A Datasheet this how the IOC works and must be handled:
Four of the PORTB pins, RB7:RB4, have an interrupt-on-change feature. Only pins configured as inputs can cause this interrupt to occur (i.e., any RB7:RB4 pin configured as an output is excluded from the interrupt-on-change comparison). The input pins (of RB7:RB4)are compared with the old value latched on the last read of PORTB. The “mismatch” outputs of RB7:RB4
are OR’ed together to generate the RB port change interrupt with flag bit RBIF (INTCON<0>). This interrupt can wake the device from Sleep. The user, in the Interrupt Service Routine, can clear the interrupt in the following manner:
a) Any read or write of PORTB. This will end the mismatch condition.
b) Clear flag bit RBIF.
A mismatch condition will continue to set flag bit RBIF. Reading PORTB will end the mismatch condition and allow flag bit RBIF to be cleared.
So your interrupt service code should look like the following:
void __interrupt() interrpt()
{
if(RBIF && RB4)
{
volatile uint8_t dummy = PORTB; // Read PORTB to end mismatch condition
RBIF=0;
RD1=RD1^1;
}
else if(INTIF) {
INTIF = 0;
RD0 = !RD0; // Toggle D0 for INT interrupt
}
}
A friendly reminder
The proteus simulation is ok for some cases. However the simulation runs in ideal conditions. That's why you may not get the same expected behaviour in the real world conditions compared to proteus' ideal simulation conditions.
void __interrupt() interrpt()
{
if(RBIF)
{
PORTB; // Read PORTB to end the mismatch condition
RBIF=0;
if(RB4)
RD1=RD1^1;
}
else if(INTF) {
INTF = 0;
RD0 = !RD0; // Toggle D0 for INT interrupt
}
}
Here is the problem: I am trying to initialize the 12-bit built-in ADC on MSP430FR2476, but no matter what I do it seems to work at 10 bits. I change the resolution bits in a control register and alas, to no avail. No matter what I have tried nothing helps, always 10 bits. The most significant byte never gets higher than 3. Please help, here is a snippet of the code:
//Configuring ADC
PMMCTL2 |= (INTREFEN); //Internal reference, default 1.5V
ADCCTL0=ADCCTL1 = 0; //Ensuring that the ADC is off
ADCCTL0 |= (ADCSHT_7 + ADCMSC); //sample and hold = 64clk, multiple conversion
ADCCTL1 |= (ADCSHP + ADCSSEL_2 + ADCCONSEQ_1 + ADCDIV_7); //Conversion is triggered
//manually, ADC clock source - SMCLK/8 ~ 2Mhz, sequence of
//channels single conversion,
ADCCTL2 |= (ADCRES_2); //12 bit resolution, no matter what setting I have, no change
ADCMCTL0 |= (ADCSREF_1 + ADCINCH_1); //Employing the internal reference and starting
//conversion from A1 (P1.1)
ADCIE |= ADCIE0; //Activate interrupt
ADCCTL0 |= (ADCON); //Switching ADC on
SYSCFG2 |= (BIT1); //Activate ADC module on the pins (this line
//doesn't do anything for some reason
void adc_convert_begin(){
ADCCTL0 |= ADCENC;
ADCCTL0 |= ADCSC; //Start conversion
//The interrupt simpy send the most significant byte over UART
__attribute__((interrupt(ADC_VECTOR)))
void ADC_ISR(void){
switch(__even_in_range (ADCIV, 0x0C)){
case 0x0C:
adc_data[adc_index] = ADCMEM0;
UCA1TXBUF = (unsigned char)(ADCMEM0>>8);
break;
}
}
The error happens to be here:
ADCCTL2 |= (ADCRES_2);
The idea is the default value is 1, so when I perform an |= operation on the register, the final value turns out to be 3, instead of 2. I need to zero that bit field first!
Trying to understand timing / dimming and interrupts using an Arduino Uno (or any other AVR) is being made very difficult by a serious lack of example code. Having found a sketch that starts from zero and ramps up the brightness, I have tried to adapt the code to prevent the continuous loop which occurs when the 16-bit register overflows.
The attached sketch starts up from zero light output and increases over a period of time - currently using the delay() function.
Attempting to adapt the code to prevent the loop from starting the entire process again and to allow the led to remain at the "top" brightness output for x (variable) number of hours has proved to be most elusive. As one of the contributors have noted this area of coding is one of the most difficult to master.
Any advice or guidance which will put me in the right direction will be most appreciated.
...
//fade over 65535 steps
// 16 bit PWM on any pin
// Example uses built in LED on pin 13 (PORTB bit 5)
// https://forum.arduino.cc/index.php?topic=348170.0
void setup() {
pinMode(13, OUTPUT);
cli(); // Disable all interrupts
TCCR1A = 0; // Clear all flags in control register A
TCCR1B = 0; // Clear all flags in control register B
TCNT1 = 0; // Zero timer 1 count
OCR1A = 32768; // Preload compare match register (50% duty cycle)
// No prescaler
//TCCR1B |= _BV(CS12);
//TCCR1B |= _BV(CS11);
TCCR1B |= _BV(CS10);
TIMSK1 |= _BV(OCIE1A); // Enable timer compare interrupt
TIMSK1 |= _BV(TOIE1); // Enable timer overflow interrupt
sei(); // enable all interrupts
}
void loop() {
for (unsigned int x = 1; x < 65535; x++) {
//cli();
OCR1A = x;
//sei();
delay(20);
}
}
ISR(TIMER1_OVF_vect) { // Timer1 overflow interrupt service routine
PORTB |= _BV(PORTB5); // Turn LED (pin 13) on
}
ISR(TIMER1_COMPA_vect) { // Timer1 compare interrupt service routine
PORTB &= ~_BV(PORTB5); // Turn LED off
}
...
I have writing code to measure ADC values on channel numbers stored in ch1 array(first 2 channels correspond to measurement of capacitor voltage Vc; then next 6 channels correspond to measurement of Grid current Ia,Ib,Ic; and last 6 channels correspond to measurement of Grid voltage Va,Vb,Vc ).
I am writing code for Atmega2560 of arduino-Mega.
Over-all structure of code:
1.In function setup(), i have initialized ADC, Serial-monitor and interrupt
2.In function loop(), i am starting ADC conversion for each channels stored in array named ch1 to get capacitor voltage, grid current and grid voltage in sequence.
I am unable to get desired result,by which i mean that nothing is printed on Serial terminal after 5-6 lines.
#define F_CPU 16000000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
//#include <util/atomic.h>
#define ChannelMax 4
uint8_t ch1[]={0,0,1,1,2,2,3,3,4,4,5,5,6,6};//channels for //Vc,Vc,Ia,Ia,Ib,IB,Ic,Ic,Va,Va,Vb,Vb,Vc,Vc(capacitor voltage,grid current,grid voltage)
volatile unsigned short ci=0;//index for ch1 array
unsigned int ADarray[15];//array to store ADC output data
volatile unsigned short readFlag=0;//it is set from ADC ISR when either Vc
or all I or all V is measured twice
void setupADC();
uint16_t adc_read(uint8_t ch);
void adc_start(uint8_t ch);
//setup function detail for arduino
void setup()
{
cli();
Serial.begin(9600); // connect to the serial port
setupADC();
sei();
}
//looping for infinity
void loop()
{
while(1)
{
//initialize things
ci=0;// start with first channel present in ch1
Serial.print("Vc measuring started with");
Serial.println(ch1[ci]);
adc_start(ch1[ci]);//start adc to measure Vc i.e ch1[0] and ch1[1]
//do something whil Vc is measured
while(1){ //wait till end of ADC measurement of Vc
if(readFlag==0){
Serial.println(readFlag);
Serial.println("Vc beingmeasured");
}
else break;
};
readFlag=0; Serial.println("Vc finally measured");
//use Vc i.e. ADarray[1] for calculation
Serial.print("I measuring started with ");Serial.println(ch1[ci]);
adc_start(ch1[ci]);//start adc to measure grid current Ia,Ib,Ic i.e ch1[2]
//to ch1[7]
//do something while grid current is being measured
while(1){ //wait for end of measurement of grid current
if(readFlag==0){
Serial.println(readFlag);
Serial.println("I beingmeasured");
}
else break;
};
readFlag=0; Serial.println("I finally measured");
//Use i i.e. ADarray[3],5,7 for calculation
Serial.print("V measuring started with ");Serial.println(ch1[ci]);
adc_start(ch1[ci]);//start measurement of grid voltage Va,Vb and Vc
//do something while grid volt is being measured
while(1){//wait till end of measurement of grid voltage
if(readFlag==0){
Serial.println(readFlag);
Serial.println("V beingmeasured");
}
else break;
};
readFlag=0;Serial.println("V finally measured");
//Use V i.e. ADarray[9],11,13 for calculation
}//end of while loop
}//end of loop function
//setup registers for ADC in atmega 2560
void setupADC(){
ADCSRA = 0;
ADCSRB = 0;
ADMUX = ch1[ci]; // Channel 0 only
ADMUX |= (1<<REFS0);//ADMUX|=0b01000000;//use it if u want to use AVCC //as Aref internally ,but dont forget to use external cap at AREF
ADCSRA = _BV(ADEN) ; // Enable ADC,
//ADCSRA |= _BV(ADPS2) | _BV(ADPS1) | _BV(ADPS0) ;//prescale at 128.
ADCSRA|=_BV(ADIE);//to interupt enable for ADC
//ADCSRA |= _BV(ADSC); // Start initial ADC cycle
}
//Interrupt service routine for ADC measurment
ISR(ADC_vect){
ADarray[ci]=(unsigned int)ADC;
Serial.print(ch1[ci]);Serial.println("adc read");
ci++;ci++;Serial.print(ci);
//if(ci>=ChannelMax) ci=0;
if((ci==2) ||( ci==8) ||(ci==14)){
Serial.println("finished");
readFlag=1;
return ;
}
//Serial.println("adc started");
adc_start(ch1[ci]);
//Serial.print(ch1[ci]);Serial.println("adc started");
}
//function to start ADC for particular channel
void adc_start(uint8_t ch){
// Serial.print(ch);Serial.println("adc started subroutine");
// select the corresponding channel 0~7
// ANDing with ’7? will always keep the value
// of ‘ch’ between 0 and 7
ch &= 0b00001111; // AND operation with 7
ADMUX = (ADMUX & 0xF0)|ch; // clears the bottom 3 bits before ORing
// start single convertion
// write ’1? to ADSC
ADCSRA |= (1<<ADSC);
}
Doing things, i understood that One shouldn't use Serial.print inside ISR or any function called from ISR(perhaps because Serial.print uses interrupt)... Then question is that how to debug inside ISR? Can i use Atomic_Block to do something?
Where do I look to find code that signifies that it uses interrupts? I've gone through wiring.c in the subfolder of Arduino, but it only leads to the function.
The issue is that when I enable CTC mode for Timer/Comp0, the LCD prints out complete jibberish, but when I disable CTC mode, it works perfectly fine.
Here is the timer initialization code:
void timerCompare0_ini(void){ // -Initialization of the Timer Compare 0
TCCR0A = 0; // This regulates the menu navigation arrow to show where user is pointing
TCCR0B = 0;
TCNT0 = 0;
OCR0B = 256;
// TCCR0A |= (1 << COM0B0) | (1 << COM0B1);
TCCR0A |= (1 << WGM01); // -CTC mode
TCCR0B |= ((1 << CS02) | (1 << CS00)); // -1024 prescaler
TIMSK0 |= (1 << OCIE0B); // -Enable timer compare interrupt
}
wiring.c is good place. delay() uses micros() which depends on running timer0 and overflow interrupt. delayMicroseconds() does internal pause based on instructions. BTW But both method has bug because when watchdog is set shorter than requested delay it reboots CPU. I.e. you need implement own functions.