I am working with STM32F407VGT6 MCU and I am having problem with external interrupts (EXTI). I have configured two pins as EXTI and they are PE7 and PE15. They are connected to HALL sensors driver and are detecting toot edges of trigger wheel. Ones is primary source with multiple tooth and other wheel is single tooth wheel which confirms position. The thing is they can work individually on their own without issues, but if I connect both of them, they start to interfere with each other and I am loosing position sync, because MCU is detecting false edges. I can recreate same behavior by connecting either pin to low signal and other to HALL driver. But if I disable EXTI and leave pin as a input the problem goes away. I have no idea what's going on here.
Also, I had issues with PE15 and EXTI before and it might be related to this. The EXTI was working only in EXTI_Trigger_Rising and EXTI_Trigger_Rising_Falling mode, but EXTI_Trigger_Falling was giving random edge detection, and only solution to this was to listen for both edges and debounce the one that I don't need. I couldn't find anything about this in datasheet.
This STM32F4 is giving me head aches and I running out of options. Well, the last option is to reroute the hall drivers to other pins and use input capture/timer.
Primary wheel config:
void Trigger_Configure_Primary(void) {
// GPIO
GPIOE->OSPEEDR |= (0x03 << (2 * 15)); // high speed
// EXTI
SYSCFG->EXTICR[3] = SYSCFG_EXTICR4_EXTI15_PE; // Tell system that you will use PE15 for EXTI15
EXTI->RTSR |= (1 << 15); // rising edge
EXTI->FTSR |= (1 << 15); // falling edge
EXTI->IMR |= (1 << 15); // Unmask EXTI15 interrupt
EXTI->PR |= (1 << 15); // Clear pending bit
/* Add IRQ vector to NVIC */
NVIC_SetPriority(EXTI15_10_IRQn, 0);
NVIC_EnableIRQ(EXTI15_10_IRQn);
}
Secondary wheel config:
void Trigger_Configure_Secondary(void) {
// GPIO
GPIOE->OSPEEDR |= (0x03 << (2 * 7)); // high speed
// EXTI
SYSCFG->EXTICR[1] = SYSCFG_EXTICR2_EXTI7_PE; // Tell system that you will use PE7 for EXTI7
EXTI->RTSR |= (1 << 7); // rising edge
EXTI->FTSR |= (1 << 7); // falling edge
EXTI->IMR |= (1 << 7); // Unmask EXTI7 interrupt
EXTI->PR |= (1 << 7); // Clear pending bit
/* Add IRQ vector to NVIC */
NVIC_SetPriority(EXTI9_5_IRQn, 0);
NVIC_EnableIRQ(EXTI9_5_IRQn);
}
IRQ handlers:
void EXTI9_5_IRQHandler(void) {
__disable_irq();
/* Make sure that interrupt flag is set */
if ((EXTI->PR & EXTI_Line7) != 0) {
// Secondary trigger IRQ
uint32_t now_nt = GET_TIMESTAMP();
uint8_t edge = ((GPIOE->IDR & SECONDARY_PIN) == 0 ? 0 : 1);
TD_Decode_Secondary_Trigger_Event(
now_nt,
edge
);
#ifdef DEBUG
// Stats
secondary_ticks = (GET_TIMESTAMP() - now_nt);
#endif
/* Clear interrupt flag */
EXTI->PR |= EXTI_Line7;
++s_cnt;
}
__enable_irq();
}
void EXTI15_10_IRQHandler(void) {
__disable_irq();
/* Make sure that interrupt flag is set */
if ((EXTI->PR & EXTI_Line15) != 0) {
// Primary trigger IRQ
uint32_t now_nt = GET_TIMESTAMP();
uint8_t edge = ((GPIOE->IDR & PRIMARY_PIN) == 0 ? 0 : 1);
if (primary_edge == edge) {
TD_Decode_Primary_Trigger(now_nt);
}
#ifdef DEBUG
// Stats
primary_ticks = (GET_TIMESTAMP() - now_nt);
#endif
/* Clear interrupt flag */
EXTI->PR |= EXTI_Line15;
++p_cnt;
}
__enable_irq();
}
RM0090, 12.3.6 Pending register (EXTI_PR):
This bit is cleared by programming it to ‘1’.
Thus, this code
/* Clear interrupt flag */
EXTI->PR |= EXTI_Line7;
Clears not only EXTI_Line7 but all pending interrupts because it reads EXTI-PR with 1 for all triggered interrupts, then OR bit EXTI_Line7 and writes all the 1-es back.
Use
/* Clear interrupt flag */
EXTI->PR = EXTI_Line7;
Related
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.
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'm having trouble getting my msp430 to communicate with a 16x4 LCD screen. Data sheet for the LCD screen: https://www.beta-estore.com/download/rk/RK-10290_410.pdf
Here is my code:
#define READMODE P2OUT = (P2OUT | BIT1) //set R/W pin high
#define WRITEMODE P2OUT=(P2OUT & ~BIT1) //set R/W pin low
#define ENABLEON P2OUT=P2OUT | 0x04 //set enable pin high
#define ENABLEOFF P2OUT=P2OUT & 0xFB //set enable pin low
#define RSDATA P2OUT = (P2OUT | BIT0) //set register select bit high so that the databus is sent to display
#define RSINSTRUCTION P2OUT = (P2OUT & ~BIT0) //set register select low so databus is sent to command register for initialization
int main(void){
WDTCTL = WDTPW + WDTHOLD; //stop watchdog timer
P1DIR = 0xFF; //The entire P1 register is output
P2DIR = 0xF7; //The entire P2 register is output except for p2.3
P1OUT = 0x00;
P2OUT = 0x01;
ENABLEOFF;
WRITEMODE;
RSINSTRUCTION;
sendCommand(0x30); //function set for 8 bit mode and display type
sendCommand(0x01); //clear screen
sendCommand(0x02); //Return Home
sendCommand(0x07); //Increment cursor and screen right
sendCommand(0x0B); //Screen display on and blinking cursor
sendChar('a');
}
/**This function checks if the LCD is busy
*/
void isBusy(void){
P1DIR &= ~BIT7; //Set bit 7 of P1 register as input
READMODE;
RSINSTRUCTION;
while((P1IN & BIT7) == BIT7){
dataRead();
}
P1DIR |= BIT7; //Set bit 7 of P1 register back to output
WRITEMODE;
}
/**Allows databus to be sent to LCD
*/
void dataWrite(void){
ENABLEOFF;
__delay_cycles(1000000);
ENABLEON;
__delay_cycles(1000000);
ENABLEOFF;
__delay_cycles(1000000);
}
/**This function is only for checking if the LCD is busy. If it is busy
* it will blink the enable light on and off
*/
void dataRead(void){
ENABLEOFF;
__delay_cycles(1000000);
ENABLEON;
__delay_cycles(1000000);
ENABLEOFF;
__delay_cycles(1000000);
}
/**This function sends a command to the LCD screen
*/
void sendCommand(unsigned char command){
isBusy();
WRITEMODE;
RSINSTRUCTION;
P1OUT = command;
dataWrite();
}
void sendChar(char letter){
RSDATA;
WRITEMODE;
P1OUT = letter;
dataWrite();
}
I'm pretty positive all my pins are connected correctly. The LCD is projecting light but that's it. I can't even get the cursor to show up and blink on the screen. I have my bus connected to 8 LED lights so I can be sure the correct commands are being sent. That is also why I have the long delay in between each write operation. Not sure what to do from here to troubleshoot. Any help would be greatly appreciated.
Sorry for the delay. The solution to the issue was adding a potentiometer to the circuit so I could change the contrast of the LCD. I also had to change the line
sendCommand(0x0B); //Screen display on and blinking cursor
to
sendCommand(0x0F); //Screen display on and blinking cursor
Thank you all for the contributions.
I'm trying to play a tone while changing something on an LCD display. I've searched around and tried protothreads, but it seems that the delay still blocks the program. I've also tried removing the delay altogether, but it skipped everything except the last note. Is there a way to play a tone without using delay? (millis perhaps?)
Sample tone sequence:
//Beats per Minute
#define BPM 250
//Constants, try not to touch, touch anyways.
#define Q 60000/BPM //Quarter note
#define W 4*Q //Whole note
#define H 2*Q //Half note
#define E Q/2 //Eigth note
#define S Q/4 //Sixteenth note
void toneFunction()
{
tone(tonePin,C5,Q);
delay(1+W);
tone(tonePin,C5,Q);
delay(1+W);
tone(tonePin,C5,Q);
delay(1+W);
tone(tonePin,C6,W);
}
You can set up a timer and put note changing logic into an interrupt service routine (ISR).
Each X milliseconds, the timer will reset and interrupt your main loop. The ISR will run and pick the next note and call the tone function. After exiting the ISR, the program continues from the point it was interrupted.
I have attached a code I used in one of my projects. The timer will interrupt the main loop every 50ms (20 Hz), therfore you will have to put your own numbers in OCR1A and the pre-scaler. Please read more about timer interrupt in arduino so that you will understand how to do it (for example here: http://www.instructables.com/id/Arduino-Timer-Interrupts/step2/Structuring-Timer-Interrupts/). You can also see the example at the end of this page (http://playground.arduino.cc/Code/Timer1) for a more user friendly way of doing this.
setup() {
....
/* Set timer1 interrupt to 20Hz */
cli();//stop interrupts
TCCR1A = 0;// set entire TCCR1A register to 0
TCCR1B = 0;// same for TCCR1B
TCNT1 = 0;//initialize counter value to 0
OCR1A = 781; // approximately 20Hz
TCCR1B |= (1 << WGM12);// turn on CTC mode
TCCR1B |= (1 << CS12) | (1 << CS10); // 1024 presxaler
TIMSK1 |= (1 << OCIE1A); // enable timer compare interrupt
sei();//allow interrupts
}
...
ISR(TIMER1_COMPA_vect){
// pick next note
}
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.