I'm trying to write a simple program in C that turns a row of LED lights OFF when I press a button. I'm very new to both embedded devices and using bitwise operators and I can't figure out what I'm doing wrong.
I'm using a Romeo board with an Amega328p which is hooked up to an I/O kit board where the pins connect to the LEDs and push buttons seperately.
Romeo Schematic
I/O Tainer Board Schematic:
The pins have DDRx PORTx and PINx settings.
I've connected pins D2-D7 to JP3_1-6 for the LEDs (I can see this part works)
I've connected pin D10 (PB2) to JP2_5 for the button (problem part)
I've connect the GNDs & VCCs on both boards to each other.
Here is my code:
void main()
{
DDRD = 0b11111100; // open all registries to be used
PORTD = 0b11111100; // set all LEDs ON to start
DDRB = 0b00000000; // set as input
PORTB = 0b00000100; // set PB2 pull up resistors
if((PINB & 0b00000100)==0) // if buttons pressed
{
PORTD = 0b00000000; // turn all lights off
}
}
When I push the button nothing happens. I don't think it's the board because it doesn't matter what switch I use.
Any help or direction in solving this would be appreciated. I think it's code and not how I'm hooking things up but I'm a newbie so I could be wrong.
What is happening is that the if statement is ran once(probably before you even press the button), and then main returns. To fix this you need to run the if statement inside of a while loop. Something like:
void main()
{
DDRD = 0b11111100; // open all registries to be used
PORTD = 0b11111100; // set all LEDs ON to start
DDRB = 0b00000000; // set as input
PORTB = 0b00000100; // set PB2 pull up resistors
while(1)
{
if((PINB & 0b00000100)==0) // if buttons pressed
{
PORTD = 0b00000000; // turn all lights off
}
//TODO: set LEDs on button release?
}
}
I do not know what happens when main returns on your micro-controller. A common thing is for main to be re-ran.
Related
I am currently working on a low-power project using the Adafruit Feather M0 microprocessor. A requirement of my project is to be able to sleep the CPU and wake it again using an external interrupt triggered from the MPU6050 accelerometer.
I have tested the following code sample from GitHub - it works successfully! The question that I need answering is how to I alter this sample code to work on Pin 13 of the feather, rather than pin 6.
#define interruptPin 6
volatile bool SLEEP_FLAG;
void EIC_ISR(void) {
SLEEP_FLAG ^= true; // toggle SLEEP_FLAG by XORing it against true
//Serial.print("EIC_ISR SLEEP_FLAG = ");
//Serial.println(SLEEP_FLAG);
}
void setup() {
// put your setup code here, to run once:
Serial.begin(9600);
delay(3000); // wait for console opening
attachInterrupt(digitalPinToInterrupt(interruptPin), EIC_ISR, CHANGE); // Attach interrupt to pin 6 with an ISR and when the pin state CHANGEs
SYSCTRL->XOSC32K.reg |= (SYSCTRL_XOSC32K_RUNSTDBY | SYSCTRL_XOSC32K_ONDEMAND); // set external 32k oscillator to run when idle or sleep mode is chosen
REG_GCLK_CLKCTRL |= GCLK_CLKCTRL_ID(GCM_EIC) | // generic clock multiplexer id for the external interrupt controller
GCLK_CLKCTRL_GEN_GCLK1 | // generic clock 1 which is xosc32k
GCLK_CLKCTRL_CLKEN; // enable it
while (GCLK->STATUS.bit.SYNCBUSY); // write protected, wait for sync
EIC->WAKEUP.reg |= EIC_WAKEUP_WAKEUPEN4; // Set External Interrupt Controller to use channel 4 (pin 6)
PM->SLEEP.reg |= PM_SLEEP_IDLE_CPU; // Enable Idle0 mode - sleep CPU clock only
//PM->SLEEP.reg |= PM_SLEEP_IDLE_AHB; // Idle1 - sleep CPU and AHB clocks
//PM->SLEEP.reg |= PM_SLEEP_IDLE_APB; // Idle2 - sleep CPU, AHB, and APB clocks
// It is either Idle mode or Standby mode, not both.
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk; // Enable Standby or "deep sleep" mode
SLEEP_FLAG = false; // begin awake
// Built-in LED set to output and high
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].DIRSET.reg = (uint32_t)(1<<g_APinDescription[LED_BUILTIN].ulPin); // set pin direction to output
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].OUTSET.reg = (uint32_t)(1<<g_APinDescription[LED_BUILTIN].ulPin); // set pin mode to high
Serial.println("Setup() Run!");
}
void loop() {
// put your main code here, to run repeatedly:
if (SLEEP_FLAG == true) {
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].OUTCLR.reg = (uint32_t)(1<<g_APinDescription[LED_BUILTIN].ulPin); // set pin mode to low
Serial.println("I'm going to sleep now.");
__WFI(); // wake from interrupt
SLEEP_FLAG = false;
Serial.println("Ok, I'm awake");
Serial.println();
}
//Serial.print("SLEEP_FLAG = ");
//Serial.println(SLEEP_FLAG);
PORT->Group[g_APinDescription[LED_BUILTIN].ulPort].OUTTGL.reg = (uint32_t)(1<<g_APinDescription[LED_BUILTIN].ulPin); // toggle output of built-in LED pin
delay(1000);
}
As per the pinout diagram and Atmel datasheet, I am struggling to work out which changes to make to allow pin 13 to operate in the same way as pin 6.
Atmel Datasheet
The obvious solution is to change the following lines...
#define interruptPin 13
EIC->WAKEUP.reg |= EIC_WAKEUP_WAKEUPEN1; // Set External Interrupt Controller to use channel 4 (pin 6)
I suspected channel 1 (WAKEUPEN1) due to the ENINT^1 next to pin 13 on the pinout diagram. But this didn't work, the code pin operation did not exhibit the same behaviour as the pin 6 setup.
I would be very grateful for any suggestion of how to implement this code working on Pin 13. Many thanks for your support.
I'm not an authority here, and your code looks correct to me.
Except, the pin out shows Pin 13 is the built-in LED line, and you manipulate LED_BUILTIN several places in your code. That's almost certainly conflicting with your attempts to use 13 as an interrupt line.
I would like to configure tim3 ch1 ch2 as encoder mode, I have the same code on tim2( it's also general purpose timer) and it's working good.
Maybe there's another bits should I set but I cant find them.
I was trying to configure this timer to work without any outputs, just generate interrupt after set period of time but it's not working as well.
//TIM2 CH1 PA0 CH2 PA1 AF1
//TIM3 CH1 PE2 CH2 PE3 AF2
RCC->APB1ENR |= RCC_APB1ENR_TIM2EN | RCC_APB1ENR_TIM3EN ;
RCC->AHBENR |= RCC_AHBENR_GPIOAEN | RCC_AHBENR_GPIOEEN;
GPIOA->MODER |= GPIO_MODER_MODER0_1 | GPIO_MODER_MODER1_1;
GPIOE->MODER |= GPIO_MODER_MODER2_1 | GPIO_MODER_MODER3_1;
GPIOA->AFR[0] |= 0X00000011;
GPIOE->AFR[0] |= 0X00002200;
TIM2->SMCR = TIM_SMCR_SMS_0;
TIM2->CCMR1 = TIM_CCMR1_CC1S_0|TIM_CCMR1_CC2S_0;
TIM2->ARR = 24;
TIM2->DIER = TIM_DIER_UIE;
TIM2->CR1= TIM_CR1_CEN;
TIM3->SMCR = TIM_SMCR_SMS_0 ;
TIM3->CCMR1 = TIM_CCMR1_CC1S_0 | TIM_CCMR1_CC2S_0;
TIM3->ARR = 24;
TIM3->DIER = TIM_DIER_UIE;
TIM3->CR1= TIM_CR1_CEN ;
enter image description here
Set SMCR to 0
Your code sets both timers to encoder mode 1, see the description of the SMCR register in the reference manual.
0001: Encoder mode 1 - Counter counts up/down on TI1FP1 edge depending on TI2FP2 level.
In this mode, the timer counter is incremented or decremented by the signals on then CH1 and CH2 input, instead of the internal clock. There must be some other component on the board, or line noise when they are unconnected, that managed to trigger TIM2 a few times.
PE2 is connected to an output of another IC
Check the schematics in the board user manual. PE2 is connected to the DRDY output of the onboard accelerometer.
You can use the CubeMX tool to find available pins for TIM3. Select your board in the Board Selector screen, it will show that PE2 and PE3 are already connected to something.
Set TIM3 combined channels to encoder mode, it will assign some free pins to the timer. You can then hold down CTRL and click on the pin to see alternatives (they will blink in blue), and you can drag the pin assignments with the mouse.
Ok, I find a solution :)
If I assign TIM3 CH1 to PB4 and CH2 to PB5 it's work good, but I don't understand why, can someone explain it ?
I was recently trying to make an interrupt on my atmega328p using atmelstudio to make a LED that is connected to digitalpin 13/PB5/PCINT5 blink four times as slow as normal when the button that is connected to a 5V output and digitalpin 2/PD0/PCINT18 is pressed down.
But whenever I run the code and press the button it will never(as far as i can tell) go true the interrupt code.
#include <avr/io.h>
#include <avr/interrupt.h>
volatile int t = 1;
int main(void)
{
init();
Serial.begin(9600);
DDRB = (1 << 5);
DDRD &= ~(1 << DDD2); // Clear the PD2 pin
// PD2 (PCINT0 pin) is now an input
PORTD |= (1 << PORTD2); // turn On the Pull-up
// PD2 is now an input with pull-up enabled
EICRA |= (1 << ISC00); // set INT0 to trigger on ANY logic change
EIMSK |= (1 << INT0); // Turns on INT0
sei(); // turn on interrupts
Serial.println("loop started");
while(1)
{
PORTB ^= (1 << 5);
delay(500*t);
}
}
ISR (INT0_vect)
{
Serial.println("interrupt");
if(t=1){
t=4;
}
else{
t=1;
}
}
I went through the datasheet multiple times and eventually stumbled onto this code online (yea yea i know i'm a real piece of work) and added my own pieces to it.
but it even this does not work, does anybody know why?
There are a few possible problems in your code:
The most important is the assignment in the if condition that was already mentioned in the comments.
clearly another one is the also mentioned serial.print stuff in the ISR.
ISRs should be as short and simple as possible.
Another is the hardware. If you press a button, they bounces and usually give multiple interrupts. so look for some de-bouncing code or have a look in the arduino library if there is something there. you may have to change the code, because usually the hardware logic itself is handled in interrupts, but the actual testing of the button states should belong to main code.
advanced stuff - if you currently reading tutorials and teach your self - ignore this, but may be keep in mind for actual projects
Another issue is the program design: your processor now cannot do anything else then toggling LEDs because his main program flow waits.
Normally you would want to use a hardware timer for this kind of tasks.
Either use it as a time base, that signals passed intervals to the main via a volatile flag variable. or directly use the PWM-Feature to directly interface the LED via one of the Output Compare Pins (OC[012][AB]).
Essentially, the aim of the piece of test code is to:
*Start a timer (hardware timer) -
turn on two LEDs -
wait until the timer finishes -
turn off both LEDs -
delay for 0.9s -
start again.
for (;;) //forever
{
PORTB &= ~_BV(PINB7); //Turn OFF GREEN LED
_delay_ms(900); //RED LED stay off for 0.9s
timer_two(); // start 5 second timer
while(!(TIFR1 & _BV(OCF1A))) //WHILE 5 second timer flag is not set
{
PORTB |= _BV(PINB7); //Turn on GREEN LED
PORTA |= _BV(PINA6); //Turn on RED LED
} //leave while loop when five second timer flag is set
PORTA &= ~_BV(PINA6); //Turn off RED LED #THIS DOESN'T HAPPEN#
}
However, with the code written as it is, both LEDs turn on, but only the Green LED turns off. The Green LED continues to turn on and off as I expected.
Although it clearly exits the while loop (as it turns the Green LED off), it doesn't seem to execute anything after it.
Am I missing a basic trick here?
(Code is compiled for an AVR, atmega644p via avr-gcc, on Windows)
Resolved it now.
The LED appeared to flash according to the timer due to the timer creating an Interrupt. The program would then restart, as no interrupt handler was given. This would reset the LED that was set before the timer was started.
Disabling all interrupts allows the code to function correctly.cli(); //disable interrupts
for (;;) //forever
{
PORTB &= ~_BV(PINB7); //Turn OFF GREEN LED
_delay_ms(900); //RED LED stay off for 0.9s
timer_two(); // start 5 second timer and executes all lines till the end
while(!(TIFR1 & _BV(OCF1A))) //WHILE 5 second timer flag is not set
{
PORTB |= _BV(PINB7); //Turn on GREEN LED
PORTA |= _BV(PINA6); //Turn on RED LED
} //leave while loop when five second timer flag is set
_delay_ms(4000); // delays the while
//OR while will be executed for 4sec
PORTA &= ~_BV(PINA6); //Turn off RED LED for 1sec
}
If You Didn't got anything what I Said then coming to the Execution..
Your while is being executed only once..
Or you can say that You don't have a while loop running..
Your runnung code is:
for (;;)
{
PORTB &= ~_BV(PINB7);
_delay_ms(900);
timer_two();
PORTB |= _BV(PINB7);
PORTA |= _BV(PINA6);
PORTA &= ~_BV(PINA6);
}
Your RED would be always OFF at the execution..?
You set hardware timer to execute
While
RED OFF
Now, it executes the while at it's crystal speed (i.e., must be in less than 5ms) and executes the last line for rest of the 4995ms..
Got the Execution..?
This question already has an answer here:
C8051f312 microcontroller [closed]
(1 answer)
Closed 9 years ago.
I have the 8051F312 microcontroller, and I have to turn on the led (on the 7.bit of the P2 port). My code is not working, maybe you have some ideas.
#include < C8051F310.H >
#include < stdio.h >
sbit LED_16 = P2^7; // P2^7-->green LED: 1 = ON; 0 = OFF
void init(void)
{
// XBRN registers_init
XBR1 = 0x40; // Enable the crossbar
PCA0MD &= 0X40; // Disable Watchdog
P2MDOUT |= 0xF0;
ADC0CN = 0x80;
ADC0CF = 0xFC;
REF0CN = 0x08;
}
void main(void)
{
init();
while (1)
{
LED_16 = 1; // LED continuously illuminated
}
}
(sorry for the format, but I had problem with the text editor)
First you need to set input/output to the GPIO. For 8051 micro controller family(According to my knowledge)(I don't know about 8051F312), assigning 1 to a pin sets that gpio as input and assigning 0 sets that gpio as output. So in your case first you need to set P2.7 as output. For that you need to do LED_16 = 0; in your init function. After that you need to consider how your LED is connected to your micro controller pin. If anode of LED connected to micro controller pin you need to make it HIGH to glow the LED. If cathode of LED connected to micro controller pin you need to make it LOW to glow the LED.
If anode of led connected to micro controller your code should be
void main(void)
{
init();
while (1)
{
LED_16 = 1; // LED continuously illuminated
}
}
If cathode of led connected to micro controller, then your code should be
void main(void)
{
init();
while (1)
{
LED_16 = 0; // LED continuously illuminated
}
}
Please find AN101 application note from Silicon Labs. I have compared example source code with your code and I have noticed that:
They use XBR2 = 0x40 for crossbar initialization.
They enable /SYSCLK by XBR1 = 0x80.
They configure output pin to push-pull mode by PRT1CF |= 0x40 (I think it should be PRT1CF |= 0x80 in your case).