#include <msp430.h>
int forward_cnt = 0;
int reverse_cnt = 0;
void main(void)
{
WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
P1DIR |= BIT0;
P1OUT &= ~BIT0; //LED 1.0
P1REN |= BIT1;
P1OUT |= BIT1;
P2REN |= BIT1;
P2OUT |= BIT1; //switches
P1IE |= BIT1;
P1IES |= BIT1; //falling edge(when button is pushed)
P1IFG &= ~BIT1;
P2IE |= BIT1;
P2IES |= BIT1;
P2IFG &= ~BIT1;
//PWM Settings
P2DIR |= (BIT5 | BIT4);
P2SEL |= (BIT5 | BIT4); //SET TO 1
TA2CTL = TASSEL_2 + MC_1 + TACLR;
TA2CCR0 = 1000;
TA2CCTL2 = OUTMOD_6;
TA2CCR2 = 0;
TA2CCTL1 = OUTMOD_6;
TA2CCR1 = 0;
__bis_SR_register(GIE);
while(1)
{
}
}
#pragma vector = PORT2_VECTOR
__interrupt void Port_2_1(void)
{
forward_cnt++;
switch(forward_cnt % 3){
case 0 :
TA2CCR2 = 400;
TA2CCR1 = 0;
P1OUT |= BIT0;
break;
case 1 :
TA2CCR2 = 800;
TA2CCR1 = 0;
P1OUT |= BIT0;
break;
case 2 :
TA2CCR2 = 0;
TA2CCR1 = 0;
P1OUT &= ~BIT0;
break;
}
P2IFG &= ~BIT1; //interrupt end, so go back with IFG clear
}
#pragma vector = PORT1_VECTOR
__interrupt void Port_1_1(void)
{
TA2CCR2 = 0;
TA2CCR1 = 0;
forward_cnt = 0;
P1IFG &= ~BIT1;
}
I want to control motor speed by slow, fast, stop with clicking switch P2.1(external interrupt) and keep turned on LED when motor is working.
But some problem is coming out. It seems like forward_cnt value is changing while I release button sometimes. It works different while I keep hold on button sometimes.
For example, It should change its speed once when I click button once with my expectation but sometimes it changes its speed when I push, and also changes when I release button. I think interrupt is not working well. How can I fix this clearly?
The interrupt may be working as intended the issue is more likely with the button itself. It seems you are running into the classic switch bouncing problem. When a button is clicked it is not a clean transition from one logic state to the next. Referencing the picture posted below a single press of the button can cause multiple rising and falling edges. Which in the case of your code will cause multiple interrupts to occur changing your forward_cnt variable almost unpredictably.
There are many different solutions to this problem but the most straightforward is to add in a delay after the button is pressed to allow for the bouncing of the switch to settle. After the bouncing has settled you can then increment you forward_cnt variable.
scope capture of button when pressed
Push buttons are noisy by nature, so you need to deal with that. For example, if you were to sample the input pin from the push button, you would find it will toggle many times before settling down on the pressed value. To deal with the noise, you should add a debouce function to the button press event.
The easiest way to do this is as mmeadwell mentioned, which is to add some delay. Normally, a good design would use hardware timer for this, but in a pinch, you can just implement a software delay. For example, in your Port_2_1(void) interrupt routine, add this code:
#pragma vector = PORT2_VECTOR
__interrupt void Port_2_1(void)
{
// Variable for button state
int button_now = 0;
int button_last = 0;
// Implement a debounce delay for 150 ms
for (int delay = 0; delay < 150; delay++)
{
// Sample the current button state
button_now = (P2IN & BIT1) ? 1 : 0;
// Reset delay when state of button changes
if (button_now != button_last) delay = 0;
// delay 1 ms (must know your CPU clock rate)
// using MSP430 compiler intrinsic function
__delay_cycles(CPU_CLOCKS_PER_US * 1000);
button_last = button_now;
}
// Only continue if button is currently pressed
if (button_now == 0) return;
// now process the button press event
// remaining code is unchanged
forward_cnt++;
switch(forward_cnt % 3){
Since the button P1.1 is only used to stop the PWM, then no debounce is necessary. This noise will not change the outcome once stopped.
Related
I'm trying to accomplish serial communication between two arduinos, one is transmitter and one reciever ofc. It is a guess game, when you generate a random number and print it on a lcd 16x2 and you have to guess a correct number. I'm using registers and interrupts. Im testing it in ISIS Proteus. The reciever behaves weirdly because whenever I press the button for a number 1, it shows this number only till Im holding this button otherwise it will change. It's weird because when I start to bruteforcing this buttons, after a while it gets fixed and after a restart it's broken again. Im trying to display also correct points, wrong points and round number but it also doesn't work. I will get only 1 good point or 1 wrong point.
RECIEVER:
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 10, 11, 12, 13);
short generatedNumber;
short userGuess;
short correctPoints = 0;
short wrongPoints = 0;
short numberOfRounds = 0;
void setup()
{
Serial.begin(9600);
randomSeed((PIND&(1<<0)));
lcd.begin(16, 2);
lcd.setCursor(0, 0);
}
void loop()
{
if(Serial.available() > 0 && numberOfRounds != 10)
{
userGuess = Serial.read();
generatedNumber = random(1, 6);
lcd.print(userGuess, DEC);
lcd.print(generatedNumber);
if(userGuess == generatedNumber)
correctPoints += 1;
else
wrongPoints += 1;
lcd.setCursor(0, 1);
lcd.print(correctPoints);
lcd.print(":");
lcd.print(wrongPoints);
lcd.print(" ");
lcd.print(numberOfRounds);
lcd.print("/10");
++numberOfRounds;
lcd.setCursor(0, 0);
}
else
{
userGuess = 0;
correctPoints = 0;
wrongPoints = 0;
numberOfRounds = 0;
}
}
TRANSMITTER:
ISR( PCINT0_vect)
{
if( (PINB&(1<<0)) == 0)
{
Serial.write(1);
}
else if( (PINB&(1<<1)) == 0)
{
Serial.write(2);
}
else if( (PINB&(1<<2)) == 0)
{
Serial.write(3);
}
else if( (PINB&(1<<3)) == 0)
{
Serial.write(4);
}
else if( (PINB&(1<<4)) == 0)
{
Serial.write(5);
}
}
void setup()
{
Serial.begin(9600);
DDRB &= ~(1 << 0);
DDRB &= ~(1 << 1);
DDRB &= ~(1 << 2);
DDRB &= ~(1 << 3);
DDRB &= ~(1 << 4);
SREG |= 1<<7;
PCICR |= 1<<PCIE0;
PCMSK0 |= 1<<PCINT0;
PCMSK0 |= 1<<PCINT1;
PCMSK0 |= 1<<PCINT2;
PCMSK0 |= 1<<PCINT3;
PCMSK0 |= 1<<PCINT4;
}
void loop()
{}
5 minutes after post I just realized, if there is no serial communication available, like when im not holding button, condition else will execute and set my variables to 0.
You have a problem in your logic. As written right now, if there is no serial available, which will be most of the time because the loop function can repeat thousands of times in the time that one character takes to come in, then you reset everything back to zero. So the user makes a guess and then a few microseconds later when nothing is available for serial to read it goes to that else and resets all your variables. I don't think that's what you want.
The transmitter is a mess. Why are you using interrupts to read these buttons? An interrupt is for really fast things that might get missed. A human pressing a button is really slow. Unless superman or The Flash are the user, you don't need an interrupt to read a button press. Just read the button from loop and react accordingly.
i've written a small program for the MSP430FR6989 to toggle the LED as long as the Button is pressed.
#include <msp430.h>
/**
* main.c
*/
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // stop watchdog timer
PM5CTL0 &= ~LOCKLPM5;
// reset port 1
P1DIR = 0x00;
P1OUT = 0x00;
// turn off led on startup
P1DIR |= BIT0;
P1OUT &= ~BIT0;
P1DIR &= ~BIT1; // P1.1 -> input
P1REN |= BIT1; // P1.1 -> enable resistor
P1OUT |= BIT1; // P1.1 -> pull-up resistor
// enable on P1.1
P1IES |= BIT1;
P1IE |= BIT1;
P1IFG = 0x00;
__enable_interrupt();
while(1)
{
__delay_cycles(1000);
}
return 0;
}
#pragma vector=PORT1_VECTOR
__interrupt void PORT1_ISR(void)
{
switch (__even_in_range(P1IV, P1IV_P1IFG1))
{
case P1IV_P1IFG1:
P1OUT = (P1IN & BIT1)
? P1OUT & ~BIT0
: P1OUT | BIT0;
P1IES ^= BIT1;
break;
default:
break;
}
}
Everything works as expected.
BUT: When I debug the program I see that BIT0 in P1IFG is set as soon as I pressed the button for the first time.
Why is this happening? I thought it would only be set if I enable the corresponding IE-Bit?
Thanks in advance
Section 12.2.6 of the MSP430FR58xx, MSP430FR59xx, and MSP430FR6xx Family User's Guide says:
Each PxIFG bit is the interrupt flag for its corresponding I/O pin, and the flag is set when the selected input signal edge occurs at the pin. All PxIFG interrupt flags request an interrupt when their corresponding PxIE bit and the GIE bit are set.
So you can always use the P1IFG bit to check if a transition on the input pin has happened. This is useful if you want to detect short pulses that might already be over when your code gets around to reading the current state of the pin.
This is why you should clear the P1IFG register before enabling interrupts (unless you are interested in old events).
I have this code that I am using to play a sound effect where I used a program called wav2c to convert a .wav file to number values that I put into a header file that I use in the code to generate the sound. I currently have it programmed to play the audio upon uploading it to the Arduino with an LED being activated along with it and staying lit for just the duration of the sound effect. I am trying to program it so that the sound and LED only activate when I am pressing a button. I have the pin the button is plugged into already programmed in but I'm not sure how to have it control the audio and LED as stated above. I don't have much experience with programming or Arduino so any help is much appreciated! I am using an Arduino Mega 2560.
The code
#include <stdint.h>
#include <avr/interrupt.h>
#include <avr/io.h>
#include <avr/pgmspace.h>
#define SAMPLE_RATE 20000
#include "Test.h"
int ledPin = 2;
int speakerPin = 9; // Can be either 3 or 11, two PWM outputs connected to Timer 2
const byte pinSwitch1 = 3;
volatile uint16_t sample;
byte lastSample;
void stopPlayback()
{
digitalWrite(ledPin, LOW);
// Disable playback per-sample interrupt.
TIMSK1 &= ~_BV(OCIE1A);
// Disable the per-sample timer completely.
TCCR1B &= ~_BV(CS10);
// Disable the PWM timer.
TCCR2B &= ~_BV(CS10);
digitalWrite(speakerPin, LOW);
}
// This is called at 8000 Hz to load the next sample.
ISR(TIMER1_COMPA_vect) {
if (sample >= sounddata_length) {
if (sample == sounddata_length + lastSample) {
stopPlayback();
}
else {
if(speakerPin==11){
// Ramp down to zero to reduce the click at the end of playback.
OCR2A = sounddata_length + lastSample - sample;
} else {
OCR2B = sounddata_length + lastSample - sample;
}
}
}
else {
if(speakerPin==11){
OCR2A = pgm_read_byte(&sounddata_data[sample]);
} else {
OCR2B = pgm_read_byte(&sounddata_data[sample]);
}
}
++sample;
}
void startPlayback()
{
pinMode(speakerPin, OUTPUT);
// Set up Timer 2 to do pulse width modulation on the speaker
// pin.
// Use internal clock (datasheet p.160)
ASSR &= ~(_BV(EXCLK) | _BV(AS2));
// Set fast PWM mode (p.157)
TCCR2A |= _BV(WGM21) | _BV(WGM20);
TCCR2B &= ~_BV(WGM22);
if(speakerPin==11){
// Do non-inverting PWM on pin OC2A (p.155)
// On the Arduino this is pin 11.
TCCR2A = (TCCR2A | _BV(COM2A1)) & ~_BV(COM2A0);
TCCR2A &= ~(_BV(COM2B1) | _BV(COM2B0));
// No prescaler (p.158)
TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
// Set initial pulse width to the first sample.
OCR2A = pgm_read_byte(&sounddata_data[0]);
} else {
// Do non-inverting PWM on pin OC2B (p.155)
// On the Arduino this is pin 3.
TCCR2A = (TCCR2A | _BV(COM2B1)) & ~_BV(COM2B0);
TCCR2A &= ~(_BV(COM2A1) | _BV(COM2A0));
// No prescaler (p.158)
TCCR2B = (TCCR2B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
// Set initial pulse width to the first sample.
OCR2B = pgm_read_byte(&sounddata_data[0]);
}
// Set up Timer 1 to send a sample every interrupt.
cli();
// Set CTC mode (Clear Timer on Compare Match) (p.133)
// Have to set OCR1A *after*, otherwise it gets reset to 0!
TCCR1B = (TCCR1B & ~_BV(WGM13)) | _BV(WGM12);
TCCR1A = TCCR1A & ~(_BV(WGM11) | _BV(WGM10));
// No prescaler (p.134)
TCCR1B = (TCCR1B & ~(_BV(CS12) | _BV(CS11))) | _BV(CS10);
// Set the compare register (OCR1A).
// OCR1A is a 16-bit register, so we have to do this with
// interrupts disabled to be safe.
OCR1A = F_CPU / SAMPLE_RATE; // 16e6 / 8000 = 2000
// Enable interrupt when TCNT1 == OCR1A (p.136)
TIMSK1 |= _BV(OCIE1A);
lastSample = pgm_read_byte(&sounddata_data[sounddata_length-1]);
sample = 0;
sei();
}
void setup()
{
pinMode( pinSwitch1, INPUT );
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
startPlayback();
}
void loop()
{
while (true);
}
The header file referenced in the code with the numeric values to create the audio.
#ifndef _HEADERFILE_H // Put these two lines at the top of your file.
#define _HEADERFILE_H // (Use a suitable name, usually based on the file name.)
const int sounddata_length=32000;
//const int sounddata_sampleRate=20000;
const unsigned char sounddata_data[] PROGMEM = {
15,1,49,0,150,0,138,0,219,255,133,0,176,0,15,1,210,
//There are many lines of more numbers in between that I cut out to save space
};
#endif // _HEADERFILE_H // Put this line at the end of your file.
The following changes will allow you to start playback whenever there is a falling edge on your switch pin. You may need to tweak to avoid switch 'bouncing'.
Firstly, add a global variable to record the last switch state:
int lastSwitchState;
Change your setup() to
void setup() {
pinMode(pinSwitch1, INPUT);
pinMode(ledPin, OUTPUT);
digitalWrite(ledPin, HIGH);
lastSwitchState = digitalRead(pinSwitch1);
}
and your loop() function to
void loop() {
delay(50);
int switchState = digitalRead(pinSwitch1);
if (switchState != lastSwitchState) {
lastSwitchState = switchState;
if (switchState == LOW) {
startPlayback();
}
}
}
Interrupts vs polling
Instead of polling the switch pin inside the main loop(), you could use interrupts. You would use attachInterrupt() to do this. Interrupts are only available on certain pins, however, and the above approach is conceptually simpler I think.
Purpose
I'm doing some timer experiment on atmega328.
I'm attempting to use every interrupt (except OVF).
Trying to make timer1 act likes 3 individual timers.
This might comes in handy when ran out of timer.
According to manual,there are 4 interrupt vectors related to timer1.
Timer1 Interrupt Vectors
My idea is making TCNT1 keep counting from 0 to 65535.
Then set the desired compare value in OCR1A,OCR1B and ICR1 registers.
When interrupts, manually add value to registers, so next interrupt will be trigger in same duration.
I wrote some codes to verify and the result seems to be ideal.
I'm considering that is this a proper way to use a timer?
or this may cause some bugs,error or efficiency issue?
Thanks!
Codes
int A_Ticks = 15624; //64us*(15624+1) = 1000ms
int B_Ticks = 23436; //1500ms
int ICR_Ticks = 31248; //2000ms
void setup()
{
Serial.begin(115200);
TCCR1A = 0x00;
TCCR1B = 0x00;
TCCR1B|= _BV(WGM13); //enable TIMER1_CAPT_vect
TCCR1B|= _BV(WGM12); //enable CTC
TCCR1B |= _BV(CS12); //prescaler = CPU clock/1024
TCCR1B &= ~_BV(CS11); //per tick = 64us
TCCR1B |= _BV(CS10);
OCR1A = A_Ticks;
OCR1B = B_Ticks;
ICR1 = ICR_Ticks;
char oldSREG = SREG;
noInterrupts();
TIMSK1 |= _BV(OCIE1A); // enable timer interrupt
TIMSK1 |= _BV(OCIE1B);
TIMSK1 |= _BV(ICIE1);
TIMSK1 |= _BV(TOIE1);
TCNT1=0;
SREG = oldSREG;
}
void loop()
{
delay(100);
}
ISR (TIMER1_COMPA_vect)
{
Serial.print(millis());
Serial.println(":COMPA");
OCR1A+=A_Ticks; //add value manually
}
ISR (TIMER1_COMPB_vect)
{
Serial.print(millis());
Serial.println(":COMPB");
OCR1B+=B_Ticks; //add value manually
}
ISR (TIMER1_OVF_vect)
{
Serial.print(millis());
Serial.println(":OVF_vect");
}
ISR (TIMER1_CAPT_vect)
{
//When interrupt into TIMER1_CAPT_vect
//TCNT will be clear to 0 due to CTC is on
//manually set it to value of ICR1 to make TCNT keep counting
TCNT1=ICR1;
ICR1+=ICR_Ticks; //add value manually
Serial.print(millis());
Serial.println(":CAPT_vect");
}
Result
999:COMPA
1499:COMPB
1999:CAPT_vect
1999:COMPA
2999:COMPA
2999:COMPB
3999:CAPT_vect
3999:COMPA
4194:OVF_vect
4499:COMPB
4999:COMPA
5999:CAPT_vect
5999:COMPA
5999:COMPB
6999:COMPA
7498:COMPB
7999:CAPT_vect
7999:COMPA
8388:OVF_vect
Why doesn't this code work? All that I want is portb to toggle when I press a button.
main
trisb=0
trisa=0xff
while true
if ra0<>0 then
portb = not portb
end if
wend .end
I'm not sure what that is; is it pseudocode?
Anyway, you need to trigger on the CHANGE from RA0 == 0 to RA0 == 1. As written, as long as RA0 == 1 then your PORTB will toggle every time through the loop.
Here's an example in C:
int main(void)
{
unsigned char bButtonValue = 0;
TRISA = 0xFF;
TRISB = 0x00;
while (1)
{
if (RA0 != bButtonValue)
{
bButtonValue = RA0;
if (bButtonValue == 1)
{
PORTB= ~PORTB;
}
}
}
}
Keep in mind that for a real app, you would want to debounce the switch input (make sure it's stable for a few samples before triggering the change event.