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).
Related
Why code stucked in the ISR routine in msp430fg6626 microcontroller? here i performing the rs485 communiction with this uc and SN75176A max485 ic.during this i was set the buad rate at 9600 here data was successfully sent from uc and same received at the max485 ic but in receiving time data was not received at uc from max485 ic. why this occured i will share the code of same here please check and answer me.
i will share the samw code below
code was stuck in this line -> __bis_SR_register(LPM3_bits + GIE);
#include <msp430.h>
int ReceiveData;
int main(void)
{
WDTCTL = WDTPW | WDTHOLD; // Stop WDT
CTSD16CCTL0 |= CTSD16SC; // Workaround for CTSD16OFFG errata
do
{
CTSD16CTL &= ~CTSD16OFFG;
}
while (CTSD16CTL&CTSD16OFFG); // End of CTSD16OFFG workaround
while(BAKCTL & LOCKBAK) // Unlock XT1 pins for operation
BAKCTL &= ~(LOCKBAK);
UCSCTL6 &= ~(XT1OFF); // XT1 On
UCSCTL6 |= XCAP_3; // Internal load cap
// Loop until XT1 fault flag is cleared
do
{
UCSCTL7 &= ~(XT2OFFG | XT1LFOFFG | DCOFFG);
// Clear XT2,XT1,DCO fault flags
SFRIFG1 &= ~OFIFG; // Clear fault flags
}while (SFRIFG1&OFIFG); // Test oscillator fault flag
//------------ Configuring MAX485 Control Lines ---------------//
P8SEL |= BIT4; // Assign P8.4 to ~RE DE and...
P8DIR |= BIT4; // P8.4 -> DE,-> ~RE output
P8OUT &= ~(BIT4); // ~RE & DE SET TO ZERO IN RECEIVING MODE
P8SEL |= 0x0C; // Assign P8.2 to UCA0TXD and...
P8DIR |= 0x0C; // P8.3 to UCA0RXD
UCA1CTL1 |= UCSWRST; // **Put state machine in reset**
UCA1CTL1 |= UCSSEL_1; // CLK = ACLK
UCA1BR0 = 0x03; // 32kHz/9600=3.41 (see User's Guide)
UCA1BR1 = 0x00; //
UCA1MCTL = UCBRS_3|UCBRF_0; // Modulation UCBRSx=3, UCBRFx=0
UCA1CTL1 &= ~UCSWRST; // **Initialize USCI state machine**
UCA1IE |= UCRXIE; // Enable USCI_A1 RX interrupt
__bis_SR_register(LPM3_bits + GIE); // Enter LPM3, interrupts enabled***
__no_operation(); // For debugger
while(1)
{
while (!(UCA1IFG&UCTXIFG)); // USCI_A1 TX buffer ready?
// UCA1TXBUF = 0x55; // TX -> RXed character
// __delay_cycles (160000);
}
}
// Echo back RXed character, confirm TX buffer is ready first
#if defined(__TI_COMPILER_VERSION__) || defined(__IAR_SYSTEMS_ICC__)
#pragma vector=USCI_A1_VECTOR
__interrupt void USCI_A1_ISR(void)
#elif defined(__GNUC__)
void __attribute__ ((interrupt(USCI_A1_VECTOR))) USCI_A1_ISR (void)
#else
#error Compiler not supported!
#endif
{
switch(__even_in_range(UCA1IV,4))
{
case 0:break; // Vector 0 - no interrupt
case 2: // Vector 2 - RXIFG
while (!(UCA1IFG&UCTXIFG)); // USCI_A1 TX buffer ready?
ReceiveData=UCA1RXBUF;
P8OUT |= BIT4; // ~RE & DE SET TO HIGH FOR TRANSMITTING MODE
__delay_cycles (16000);
UCA1TXBUF = ReceiveData; // TX -> RXed character
__delay_cycles (16000);
P8OUT |= ~(BIT4); // ~RE & DE SET TO ZERO IN RECEIVING MODE
__delay_cycles (16000);
break;
case 4:break; // Vector 4 - TXIFG
default: break;
}
}
#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.
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.
I have been studying microcontrollers so far and I gone through LCD and understood how it worked in 8 bits mode but 1 thing I don't understand is how to work with it in 4 bits mode and whatever I try in code I cant ever make it and I searched almost all the sites available on the internet to explain this topic but still didn't get it so I would very much appreciate if someone could spend some of his time explaining this to me thanks in advance.
#user3674628: By now you must have had known how to interface in 4-bit modes. Though you didnt specify the compiler you use here is an example I have been using with mplabx with xC8 compiler. It works pretty well. Do not forget to adjust the configuration bits on mplabx. Success!
// PIC18F4550 Configuration Bit Settings
// 'C' source line config statements
#include <xc.h>
// #pragma config statements should precede project file includes.
// Use project enums instead of #define for ON and OFF.
// CONFIG1L
#pragma config PLLDIV = 5
#pragma config CPUDIV = OSC1_PLL2
#pragma config USBDIV = 2
// CONFIG1H
#pragma config FOSC = INTOSCIO_EC //this uses internal oscillator
#pragma config FCMEN = OFF
#pragma config IESO = OFF
// CONFIG2L
#pragma config PWRT = OFF
#pragma config BOR = OFF
#pragma config BORV = 3
#pragma config VREGEN = OFF
// CONFIG2H
#pragma config WDT = OFF
#pragma config WDTPS = 32768
// CONFIG3H
#pragma config CCP2MX = ON
#pragma config PBADEN = OFF
#pragma config LPT1OSC = OFF
#pragma config MCLRE = OFF
// CONFIG4L
#pragma config STVREN = ON
#pragma config LVP = OFF
#include <p18f4550.h>
#include <stdio.h>
#include <stdlib.h>
#include <plib/xlcd.h>
/* DATA_PORT defines the port to which the LCD data lines are connected */
#define DATA_PORT PORTB //for 4-bit mode connect D4-D7 of lcd to B0-B3 of lcd
#define TRIS_DATA_PORT TRISB
/* This defines the port where the control lines are connected.
* These are just samples, change to match your application.
* you may omit this part because its in y the compiler.
*/
#define RW_PIN LATBbits.LATB6
#define TRIS_RW TRISBbits.TRISB6
#define RS_PIN LATBbits.LATB5
#define TRIS_RS TRISBbits.TRISB5
#define E_PIN LATBbits.LATB4
#define TRIS_E TRISBbits.TRISB4
#define _XTAL_FREQ 4000000 // SET THIS TO SUIT YOUR FREQUENCY
void Wait(unsigned int delay)//delay for 100ms
{
for(;delay;delay--)
__delay_us(100);
}
//initialise the lcd
void LCDInit(){
OpenXLCD(FOUR_BIT & LINES_5X7 & CURSOR_ON & BLINK_OFF);
while(BusyXLCD());
WriteCmdXLCD(SHIFT_DISP_LEFT);
while(BusyXLCD());
}
void main(void)
{
ADCON1 = 0xF; // No analog, all digital i/o
TRISB = 0x0;
LCDInit()//initialise lcd
putrsXLCD( " Thanks God" );//display on the lcd
while(BusyXLCD());
WriteCmdXLCD(0xC0);// go to the next line on the lcd
while(BusyXLCD());
putrsXLCD( " I made it" );//display on the lcd
while(BusyXLCD());
while(1);
}
void DelayFor18TCY(void){
Delay10TCYx(20);
}
void DelayPORXLCD(void){
Delay1KTCYx(30);
}
void DelayXLCD(void){
Delay1KTCYx(10);
}
Have you tried these?
http://learningmsp430.wordpress.com/2013/11/16/16x2-lcd-interfacing-in-4-bit-mode/
http://www.spickles.org/projects/4-bit-lcd/
The first is pretty informative, and the second has example source code.
For 4-bit interface data, only four bus lines (DB4 to DB7) are used for transfer. Bus lines DB0 to DB3
are disabled. The data transfer between the HD44780U and the MPU is completed after the 4-bit data
has been transferred twice. As for the order of data transfer, the four high order bits (for 8-bit
operation, DB4 to DB7) are transferred before the four low order bits (for 8-bit operation, DB0 to
DB3).
The busy flag must be checked (one instruction) after the 4-bit data has been transferred twice. Two
more 4-bit operations then transfer the busy flag and address counter data.
For 8-bit interface data, all eight bus lines (DB0 to DB7) are used.
Here is an example on how it works:
#include <msp430g2553.h>
#define DR P1OUT = P1OUT | BIT4 // define RS high
#define CWR P1OUT = P1OUT & (~BIT4) // define RS low
#define READ P1OUT = P1OUT | BIT5 // define Read signal R/W = 1 for reading
#define WRITE P1OUT = P1OUT & (~BIT5) // define Write signal R/W = 0 for writing
#define ENABLE_HIGH P1OUT = P1OUT | BIT6 // define Enable high signal
#define ENABLE_LOW P1OUT = P1OUT & (~BIT6) // define Enable Low signal
void data_write(void)
{
ENABLE_HIGH;
delay(2);
ENABLE_LOW;
}
void data_read(void)
{
ENABLE_LOW;
delay(2);
ENABLE_HIGH;
}
void check_busy(void)
{
P1DIR &= ~(BIT3); // make P1.3 as input
while((P1IN&BIT3)==1)
{
data_read();
}
P1DIR |= BIT3; // make P1.3 as output
}
void send_command(unsigned char cmd)
{
check_busy();
WRITE;
CWR;
P1OUT = (P1OUT & 0xF0)|((cmd>>4) & 0x0F); // send higher nibble
data_write(); // give enable trigger
P1OUT = (P1OUT & 0xF0)|(cmd & 0x0F); // send lower nibble
data_write(); // give enable trigger
}
void send_data(unsigned char data)
{
check_busy();
WRITE;
DR;
P1OUT = (P1OUT & 0xF0)|((data>>4) & 0x0F); // send higher nibble
data_write(); // give enable trigger
P1OUT = (P1OUT & 0xF0)|(data & 0x0F); // send lower nibble
data_write(); // give enable trigger
}
void send_string(char s)
{
while(s)
{
send_data(*s);
s++;
}
}
void lcd_init(void)
{
P1DIR |= 0xFF;
P1OUT &= 0x00;
send_command(0x33);
send_command(0x32);
send_command(0x28); // 4 bit mode
send_command(0x0E); // clear the screen
send_command(0x01); // display on cursor on
send_command(0x06); // increment cursor
send_command(0x80); // row 1 column 1
}
#include “lcd.h”
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // stop watchdog timer
lcd_init();
send_string(“Hello”);
send_command(0xC0);
send_string(“World”);
while(1){}
}
I have written a code which takes two digit number from laptop and changes the PWM dutycycle to that number. It is part of a bigger requirement where I need to control motor speed over UART.
#include "io430g2553.h"
#include <stdint.h>
void PWM(uint8_t duty_cycle);
void PWM_Config();
int main( void )
{
// Stop watchdog timer to prevent time out reset
WDTCTL = WDTPW + WDTHOLD;
WDTCTL = WDTPW + WDTHOLD;
BCSCTL1 = CALBC1_1MHZ; // Run at 1 MHz
DCOCTL = CALDCO_1MHZ; // Run at 1 MHz
PWM_Config();
PWM(5);
__delay_cycles(5000000);
PWM(15);
__delay_cycles(5000000);
PWM(25);
__delay_cycles(5000000);
PWM(50);
__delay_cycles(5000000);
PWM(25);
__delay_cycles(5000000);
PWM(15);
__delay_cycles(5000000);
PWM(5);
while(1)
{}
}
void PWM_Config()
{
P1OUT &= 0x00; // Clearing P1OUT
P1SEL |= BIT6 ;
P1SEL2 &= ~BIT6 ;
P1DIR |= BIT6; // Configuring P1.6 as Output
}
void PWM(uint8_t duty_cycle)
{
TA0CTL =0;
TA0CTL |= TACLR;
TA0CCR1 |= (duty_cycle*100);
TA0CCR0 |= 10000;
TA0CTL |= TASSEL_2 + MC_1 + ID_0;// Register TA0CTL -> SMCLK/8, Up mode
TA0CCTL1 |= OUTMOD_7 ;//Set/Reset Mode
TA0CCTL0 &= ~CCIE; // Interrupt Disabled}
The problem with the void PWM(uint8_t duty_cycle) function is that first time it generates the correct PWM at P1.6, next if it is given a value it changes PWM to that DC, but I can not go back to lower DC.
the fisrt 2 PWM functions in the code changes to correct duty cycle PWM(5),PWM(15) then the rest of PWM values do not produce desired dutycycle.
I am not able to troubleshoot where am I wrong, can any1 help?
Thanks
Seems like a stupid mistake on my part..
TA0CCR1 |= (duty_cycle*100);
instead of
TA0CCR1 = (duty_cycle*100);