STM32F4 - Button GPIO input doesn't work - interrupt

I'm using a STM32F469 Discovery board, and I'm trying to get an external momentary push button to work.
It is currently connected to PG11, and it is set up so than when pressed it connects the pin to +5V supplied by the MCU. Before I continue, I just want to state that I use the following code in stmf4xx_it.c to perform debounce:
#define REFdebounce 200
int In1 = 3;
int In1_0 = 0;
int In1_1 = 0;
int StatoIn1 = 3;
void SysTick_Handler(void)
{
In1 = HAL_GPIO_ReadPin( GPIOG, GPIO_PIN_11 );
if ( In1 == 0 )
{
In1_0++;
In1_1 = 0;
if ( In1_0 >= REFdebounce )
{
In1_0 = REFdebounce + 1;
StatoIn1 = 0;
}
}
else
{
In1_0 = 0;
In1_1++;
if ( In1_1 >= REFdebounce )
{
In1_1 = REFdebounce + 1;
StatoIn1 = 1;
}
}
}
I have the following code in a header file inout.h:
typedef void ( * TSelectCallback ) ( int aSelectSignal );
void ConfigSelectPin
(
TSelectCallback aSelectCallback
);
And then in inout.c I have the following code for setup of the button GPIO pin:
#define SELECT_SIGNAL_PIN GPIO_PIN_11
#define SELECT_SIGNAL_GPIO_PORT GPIOG
#define SELECT_SIGNAL_GPIO_CLK_ENABLE() __HAL_RCC_GPIOG_CLK_ENABLE()
#define SELECT_SIGNAL_GPIO_CLK_DISABLE() __HAL_RCC_GPIOG_CLK_DISABLE()
#define SELECT_SIGNAL_EXTI_IRQn EXTI15_10_IRQn
void ConfigSelectPin( TSelectCallback aSelectCallback )
{
GPIO_InitTypeDef GPIO_InitStructure;
/* Enable GPIOC clock */
SELECT_SIGNAL_GPIO_CLK_ENABLE();
/* Configure washer signal pin as input floating */
GPIO_InitStructure.Mode = GPIO_MODE_IT_RISING;
GPIO_InitStructure.Pull = GPIO_PULLDOWN;
GPIO_InitStructure.Pin = SELECT_SIGNAL_PIN;
HAL_GPIO_Init( SELECT_SIGNAL_GPIO_PORT, &GPIO_InitStructure );
/* Enable and set EXTI lines 0 Interrupt to the lowest priority */
HAL_NVIC_SetPriority( SELECT_SIGNAL_EXTI_IRQn, 8, 0 );
HAL_NVIC_EnableIRQ( SELECT_SIGNAL_EXTI_IRQn );
SelectCallback = aSelectCallback;
}
void EXTI15_10_IRQHandler( void )
{
if (__HAL_GPIO_EXTI_GET_IT( SELECT_SIGNAL_PIN ) != RESET)
{
RedLedOn();
__HAL_GPIO_EXTI_CLEAR_IT( SELECT_SIGNAL_PIN );
HAL_GPIO_EXTI_IRQHandler( SELECT_SIGNAL_PIN );
}
}
void HAL_GPIO_EXTI_Callback( uint16_t GPIO_Pin )
{
if ( GPIO_Pin == SELECT_SIGNAL_PIN )
{
YellowLedOn();
GPIO_PinState pinState;
//pinState = HAL_GPIO_ReadPin( SELECT_SIGNAL_GPIO_PORT, GPIO_Pin );
pinState = 1;
if ( SelectCallback )
SelectCallback ( pinState );
}
}
Then in my main.c file I have the following:
/* variable to detect that hardware button is pressed */
static int Select = 0;
extern int StatoIn1;
void SelectIsrCallback( int aSelectSignal )
{
if ( StatoIn1 == 1 )
{
OrangeLedOn();
Select = 1;
}
}
I then use the following code to detect if the button has been pressed and perform my required action
if ( Select )
{
BlueLedOn();
Select = 0;
}
Now EVERY time I press the button, the EXTI15_10_IRQHandler is called as acknowledged by the red led turning on.
If I keep pressing the button, many many many times, the HAL_GPIO_EXTI_Callback will eventually be called as acknowledged by the yellow led turning on.
If I then keep pressing the button even more times, then eventually the SelectIsrCallback is called and my desired action is perfomed, as acknowledged by the orange and blue led turning on.
Why do the HAL_GPIO_EXTI_Callback and SelectIsrCallback not get called on the first button press? And why does the SelectIsrCallback not get called once the HAL_GPIO_EXTI_Callback is called?
NOTE: I've just moved the YellowLedOn() call, to before the if statement in HAL_GPIO_EXTI_Callback to see if it was this function of the if statement that takes loads of button presses before it is called. It made no difference, so the issue is with the calling of the HAL_GPIO_EXTI_Callback function.

Okay, so despite spending days to figure this out, it turns out the answer is a simple one. The order of the functions called in EXTI15_10_IRQHandler need to be switched. I.e. the HAL_GPIO_EXTI_IRQHandler needs to b called first and then the flag needs to be cleared. So this:
void EXTI15_10_IRQHandler( void )
{
if (__HAL_GPIO_EXTI_GET_IT( SELECT_SIGNAL_PIN ) != RESET)
{
RedLedOn();
**__HAL_GPIO_EXTI_CLEAR_IT( SELECT_SIGNAL_PIN );
HAL_GPIO_EXTI_IRQHandler( SELECT_SIGNAL_PIN );**
}
}
Needs to be switched to this:
void EXTI15_10_IRQHandler( void )
{
if (__HAL_GPIO_EXTI_GET_IT( SELECT_SIGNAL_PIN ) != RESET)
{
RedLedOn();
HAL_GPIO_EXTI_IRQHandler( SELECT_SIGNAL_PIN );
__HAL_GPIO_EXTI_CLEAR_IT( SELECT_SIGNAL_PIN );
}
}
This seems obvious now, because you can't clear the interrupt flag before calling the interrupt function otherwise it will not execute. However, I'm sure I saw a lot of examples where it was in the order I had the functions originally.

Related

How do you make a button only send one input to a source while being pushed down and not send another one until the button is let go and repressed?

In a basic variable and Button setup on an Arduino software, the idea is that if you press a button, you gain one on a variable. Theoretically, holding the button shouldn't cause the variable to increase more than one until the release and repressing of the button. However, I could not make this happen. How is this supposed to be done?
I tried using an else around a delay (because the adding one to a variable code was inside an "if" statement) so it would not delay unless the button and released and would not count multiple button inputs, but this resulted in a larger increase when a button was clicked rather than just a one increase.
const int buttonPin = 8;
int number = 0;
int numbertwo = 0;
int buttonState = 0;
void setup()
{
pinMode(buttonPin, INPUT);
}
void loop()
{
buttonState = digitalRead(buttonPin);
if (buttonState == LOW){
numbertwo = ++number;
delay(100);
}
(Can use any method to determine variable, like on an LCD)
As mentioned above, I expected the variable to only increase by one when a button is pressed and not to continue increasing until the button is released and re-pressed, but what actually happened was the variable kept increasing in value as the button was held.
I'm assuming you want the variable "number" increase by 1 per press. I think you are looking for something like this:
const int buttonPin = 8;
int number = 0;
int buttonState = 0;
void setup() {
pinMode(buttonPin, INPUT);
}
void loop()
{
buttonState = digitalRead(buttonPin);
if (buttonState == LOW){
++number;
// This loop is to make sure not to count up until button is released
do {
delay(5);
buttonState = digitalRead(buttonPin);
} while (buttonState == LOW);
}
}
If you don't like the loop based implementation, here's a state based implementation for you:
const int buttonPin = 8;
int number = 0;
int buttonState = 0;
int prevState = -1;
void setup() {
pinMode(buttonPin, INPUT);
prevState = -1;
}
void loop()
{
buttonState = digitalRead(buttonPin);
if (buttonState == LOW && prevState != buttonState){
++number;
prevState = buttonState;
}
delay(5);
}

Breaking a while loop in Arduino

I have a program that moves a stepper motor to the right, left and have a stop button that stops the motor. In one part of my program, a motor gradually lowers a speed and stops after a certain period of time iv.
The problem is that in this part of a program (when a motor gradually lowers a speed and then stops) I can’t stop the motor upon pressing a stop button. I understand that I need to break a while loop somehow, but using a break statement doesn't wort for me.
Do you have some ideas?
Here is my function:
/* --- STEPPER MOTOR ---*/
const int motor_step = 3;
const int motor_dir = 4;
int stepSpeed = 0;
int stepMaxSpeed = 1000;
int fadeAmount = 100;
int fadeDelay = 10;
/* ---- STOP BUTTON ---- */
int buttonStop = 5;
int stateStop=0;
void setup() {
.
.
.
stateStop = digitalRead(buttonStop);
}
void loop () {
.
.
.
myfunc();
}
void myfunc() {
if(stateStop == HIGH) {noTone(motor_step); stepSpeed = 0;}
elapsedMillis te;
unsigned int iv = 1500;
while (te < iv) {
if(stepSpeed == stepMaxSpeed) {
stepSpeed = stepSpeed+0;
tone(motor_step,stepSpeed);
digitalWrite(motor_dir,HIGH);
}
else {
stepSpeed = stepSpeed + fadeAmount;
tone(motor_step,stepSpeed);
digitalWrite(motor_dir,HIGH);
delay(fadeDelay);
}
if(stateStop == HIGH) { stepSpeed = 0; break;}
}
if(stepSpeed == stepMaxSpeed) {
while(stepSpeed>0){
stepSpeed = stepSpeed-fadeAmount;
tone(motor_step,stepSpeed);
digitalWrite(motor_dir,HIGH);
delay(fadeDelay);
if(stateStop == HIGH) { stepSpeed = 0; break;}
}
}
stepSpeed = 0;
noTone(motor_step);
digitalWrite(enable,LOW); // enable changed from HIGH
}
Your break condition does never trigger as stateStop is never being updated inside your while loop. How is your program supposed to know? It's busy running the loop and does not care about anything outside it's scope.
Check the button state inside the while loops or use interrupts

How to toggle LED

How to toggle LED with push button. I wrote program. There is problem in Program. This program fail if i held the button down continuously then LED turn ON/OFF continuously. How to solve problem ?
#include<REGX51.h>
#define led_off 0
#define switch_pressed 0
sbit Switch = P1^2; /*set bit P1^2 to Switch*/
sbit led = P1^0; /*set bit P1^0 to LED*/
void debounce (unsigned long wait) /* Debounce function */
{
unsigned int i;
for (i = 0; i < wait; i++);
}
void main (void)
{
led = led_off;
while (1)
{
if (Switch == switch_pressed)
{
led = ~led;
debounce (40000);
}
}
}
You are toggling the button so long as the button is pressed. when you need to toggle it only when the button changes from not pressed to pressed:
#include<REGX51.h>
#define BTN_UP 1
#define BTN_DN 0
#define DEBOUNCE_DELAY 40000
sbit Switch = P1^2 ;
sbit led = P1^0 ;
void delay( unsigned long wait )
{
for( volatile unsigned long i = 0 ;
i < wait;
i++ )
{ /* do nothing */ }
}
int main( void )
{
int prev_button_state = Switch ;
led = 0 ;
for(;;)
{
// Read current Switch state
int button_state = Switch ;
// If BTN_DN event...
if( button_state != prev_button_state &&
button_state == BTN_DN )
{
led = ~led;
delay( DEBOUNCE_DELAY ) ;
}
prev_button_state = button_state ;
}
return 0 ;
}

Serial.Event() only runs once (using single char input)

On my Arduino Mega 2560, I'm trying to run a motor that turns a 20-vial container (accepting int input 1-20) while regulating temperature via PID of a separate cooler. I am generally new to this field of technology so bear with me. I also have an interrupt set up for an encoder to keep track of vial position.
The void serialEvent() and void loop() are the most important portions to look at, but I decided to put the rest of the code in there just in case you needed to see it.
#include <PID_v1.h>
#include <SPI.h>
#include <TMC26XStepper.h>
#define COOL_INPUT 0
#define PIN_OUTPUT 9
TMC26XStepper tmc26XStepper = TMC26XStepper(200,5,7,6,500);
int step = 6;
int value;
int i;
char junk = ' ';
volatile long enc_count = 0;
const byte interruptPinA = 2;
const byte interruptPinB = 3;
//Define Variables we'll be connecting to
int outMax = 255;
int outMin = -145;
double Setpoint, Input, Output;
double heatInput, heatOutput, originalInput;
//Specify the links and initial tuning parameters
// AGGRESSIVE VALUES (to get to 4 deg C)
double aggKp=8.0, aggKi=3.0, aggKd=0.15;
// CONSERVATIVE VALUES (to hover around 4 deg C)
double consKp=2.5, consKi = 0.0, consKd = 1.0;
PID myPID(&Input, &Output, &Setpoint, aggKp, aggKi, aggKd, REVERSE);
void setup()
{
pinMode(step, OUTPUT);
pinMode(interruptPinA, INPUT_PULLUP);
pinMode(interruptPinB, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPinA), encoder_isr, CHANGE);
attachInterrupt(digitalPinToInterrupt(interruptPinB), encoder_isr, CHANGE);
//initialize the variables we're linked to
Input = (5.0*analogRead(COOL_INPUT)*100.0) / 1024;
Setpoint = 10.75;
myPID.SetOutputLimits(outMin, outMax);
//turn the PID on
myPID.SetMode(AUTOMATIC);
Serial.begin(115200);
tmc26XStepper.setSpreadCycleChopper(2,24,8,6,0);
tmc26XStepper.setMicrosteps(32);
tmc26XStepper.setStallGuardThreshold(4,0);
Serial.println("...started...");
tmc26XStepper.start();
Serial.flush();
Serial.println("Enter vial numbers 1-20");
}
void loop() {
Input = (5.0*analogRead(COOL_INPUT)*100.0) / 1024;
// A BUNCH OF CODE FOR TEMP REGULATION
Serial.println(Input);
delay(150);
}
void serialEvent() {
while (Serial.available() == 0) {}
i = Serial.parseInt();
Serial.print("position: ");
Serial.print(i);
Serial.print(" ");
while (Serial.available() > 0) {
junk = Serial.read();
}
if (i == 1) {
value = 0;
} else {
int num = i - 1;
value = num * 72;
}
while (enc_count != value) {
digitalWrite(6, HIGH);
delayMicroseconds(100);
digitalWrite(6, LOW);
delayMicroseconds(100);
if (enc_count == 1440) {
enc_count = 0;
}
}
Serial.println(enc_count);
}
// INFO FOR ENCODER
void encoder_isr() {
static int8_t lookup_table[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
static uint8_t enc_val = 0;
enc_val = enc_val << 2;
enc_val = enc_val | ((PIND & 0b1100) >> 2);
enc_count = enc_count + lookup_table[enc_val & 0b1111];
}
So, originally I had the two processes tested separately (vial position + encoder, then temperature regulation) and everything did exactly as it was supposed to. Now, I fused the code together and stored the vial position entry in the serialEvent() method to keep the temperature reading continuous and the vial position entry available for whenever I decided to provide input. However, when I put in a value, the program stops all together. I am able to see the number I entered (position: 5), but the Serial.println(enc_count) never gets printed. On top of the that, the temperature readings stop displaying readings.
Any thoughts? Need more information?

UART0 to UART2 gateway (sort of) for AtMega2560

I connected a device to the UART0 of the AtMega2560. I want to transfer the UART0 data to the UART2 to view it on the Terminal(PC).
When I connect the device directly to the PC using an UART to serial device (FTDI) It sends the data nicely.
When I put the UART2 in the middle for said purpose, then It only sends the first line, specifically:
Ver V2DAPV142 On-Line: And then forgets. Sometimes it doesn't send the first line too.
Code:
#define UART0_BUFFER_SIZE 40
#define RX_WAIT 65000
volatile unsigned char UART0_rx_ArrUC85[UART0_BUFFER_SIZE];
volatile unsigned char UART0_rx_ArrLength = 0, UART0_rx_ArrIndex = 0;
void uart0_init( unsigned int baudrate )
{
UBRR0H = (unsigned char) (baudrate>>8);
UBRR0L = (unsigned char) baudrate;
UCSR0B = ( 1 << RXEN0 ) | ( 1 << TXEN0 ) | (1<<RXCIE0);
UCSR0C = ( 1 << USBS0 ) | ( 1 << UCSZ01 ) | ( 1 << UCSZ00 ); // 8N1
}
void USART2Init(UINT16 ubrr_value)
{
UBRR2L = ubrr_value;
UBRR2H = (ubrr_value>>8);
UCSR2C|=(3<<UCSZ20);
UCSR2B = (1<<RXEN2) | (1<<TXEN2);
}
ISR(USART0_RX_vect)
{
unsigned char recChar = UDR0;
if (UART0_BUFFER_SIZE > UART0_rx_ArrLength)
{
UART0_rx_ArrUC85[UART0_rx_ArrIndex++] = recChar;
UART0_rx_ArrLength = UART0_rx_ArrIndex;
}
}
void uart2_putchar(UINT8 data)
{
//Local variables
unsigned int i;
for( i = 0; !( UCSR2A & ( 1 << UDRE2 ) ); i++ ) // Wait for empty transmit buffer
{
if( i > RX_WAIT ) // How long one should wait
{
return ; // Give feedback to function caller
}
}
UDR2 = data; // Start transmitting
//return (int)data; // Cast and return int value
}
void uart2_puts(unsigned char *str)
{
UINT8 dat;
for( ;*str != '\0'; )
{
dat= *str++ ;
uart2_putchar(dat);
}
}
int main()
{
USART2Init(8);
uart0_init(103);
sei();
while(1)
{
if(UART0_rx_ArrLength>0)
{
uart2_puts((unsigned char *) UART0_rx_ArrUC85);
UART0_rx_ArrLength = UART0_rx_ArrIndex = 0;
}
}
}
What could be the issue.
I checked it with same and different baud rates too for UART0 and UART2.
The issue was circuitry power level. The power supply was not sufficient for the Pen-Drive ctrlr and the regulator was not able to source for its communication power level. Hence it was not working sometimes. Further we have tested it and drew a conclusion that after giving sufficient power to the Pen-Drive ctrlr using another power regulator, the above said communication takes nicely place. I hope this can help ppl to draw attention towards the possible circuitry issues.