I am trying to make a button debounce software by the help of an led toggle function that returns a different boolean each time which i got by asking the question before but that never worked :
#include <avr/io.h>
bool ledToggle();
int main(void)
{
DDRB |= (1 << 0);
DDRB &= ~(1 << 0);
while(1)
{
//TODO:: Please write your application code
if (ledToggle() == true)
{
//led on
PORTB |= (1 << 0);
}else{
//led off
PORTB &= ~(1 << 0);
}
}
}
bool ledToggle()
{
static bool state = false;
if(bit_is_clear(PINB, 1)){
state = !state;
}
return state;
}
EDIT
I get no errors or anything when I try to compile it just doesn't work...
I don't recognize in which way this code would debounce a switch connected to Port B / 1. Debouncing means to
check and store the key logic state
wait a certain amount of time (depending on hardware, 5 - 50 ms)
compare the (now) logic state with what was read before
if equal the (now) logic state is the debounced key state
Provided the program is functioning well, the LED would bounce at the same pace as the switch.
In your bool ledToggle() I suggest you declare static volatile bool state; to ensure the variable is created in RAM (rather than a CPU register)
In the second line of main () you are mistakenly setting the LED port instead of the BUTTON port as an input.
I suggest using defines as a way of making this kind of mistakes less likely:
#define LED 0
#define BUTTON 1
DDRB |= (1 << LED);
DDRB &= ~(1 << BUTTON);
Related
//setup
RCC->AHBENR |= RCC_AHBENR_GPIOAEN; //enable the bus for port IOPA.
GPIOA->MODER |= 0x400; // MODER5 (PA5) LD2,
GPIOA->MODER |= 0x100000; // MODER10 (PA10) LED
GPIOC->PUPDR |= 0x2000000; // (PC13) B1
GPIOA->PUPDR |= 0x20; //(PA2) B2
//main
while (1)
{
if(GPIOA->IDR |= 0x4){
GPIOA->BSRR = 0x400;
HAL_Delay(1000);
GPIOA->BRR = 0x400;
HAL_Delay(1000);
}
if(GPIOC->IDR &= 0x2000){
GPIOA->BRR = 0x20;
}
else{
GPIOA->BSRR = 0x20;
}
}
So the first if statement, the led is toggling on its own and the button is not working.
the second if statement is correct but it needs to work like simultaneously like millis() in Arduino IDE.
My questions are, is there anything I missed with configuring the button and how to make it work simultaneously?
You have made some very basic errors with C operators.
if(GPIOA->IDR |= 0x4)
means the same as:
temporary_value = (GPIOA->IDR | 0x4);
GPIOA->IDR = temporary_value;
if (temporary_value != 0)
This is wrong for two reasons: firstly, you aren't allowed to write to IDR, it is the input data register. Secondly the if control expression will always be true because anything OR 4 is always not equal to zero.
Similarly further down &= is also an assignment operator, you are trying to write to the input register again.
To test if a bit in a register is set, use:
if ((GPIOA->IDR & (1 << n)) != 0)
Where n is the bit number you want to test. Don't calculate the bitmask in your head (eg: 0x400) because you are more likely to make an error.
Im trying to build a program that has a scannerlight with at least 5 leds and one button connected to an external interrupt pin. A single button press (and release) to start the scanner light. Pressing (and releasing) stops the scanner light again (and so on...).
I have a ground side switch on PD2
i have my LEDS on pin PD3,PD4,PD5,PD6 and PD7.
Im useing ATMega328P
I know my towering light works when i press on the button the towering light stops but when i press again it feels like it doesnt return the value to 1.
My code:
#ifndef F_CPU
#define F_CPU 1000000UL
#endif
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define BIT_IS_CLEAR(byte, bit) (!(byte & (1 << bit)))
volatile int value = 1;
int main(void)
{
DDRD = 0b111110000;
DDRD &= ~(1 << PD2); // clear DDRD bit 2, sets PD2 (pin 4) for input
PORTD |= (1 << PD2); // set PD2/INT0 (pin 4) internal pull-up resistor
PCICR = 0b00000100;
PCMSK2 = 0b00000100;
sei();
while (value==1) //when value is 1 it should start having a towerlight
{
PORTD= 0x80;
_delay_ms(15000);
PORTD= 0x40;
_delay_ms(15000);
PORTD= 0x20;
_delay_ms(15000);
PORTD= 0x10;
_delay_ms(15000);
PORTD= 0x08;
_delay_ms(15000);
}
}
ISR(PCINT2_vect)
{
if(BIT_IS_CLEAR(PIND, PD2) & value==1) { // if switch is pressed (logic low)
value=0;
} else if(value == 0) {
value=1;
} else {
// ideally should never get here, but may occasionally due to timing
}
}```
regarding:
while (value==1)
{
....
}
When value is 0 the loop is exited, execution then exits the program. This is a serious logic flaw in the program
You are using a bitwise AND where it should be a logical AND. Change this
if(BIT_IS_CLEAR(PIND, PD2) & value==1)
to this
if(BIT_IS_CLEAR(PIND, PD2) && (0 != value))
Comparing for non-zero as opposed to equal to 1 guards against value being corrupted; since you want a zero or non-zero condition. Adding brackets makes the intention very clear. Yoda comparisons (putting the constant on the left hand side) prevents accidental assignment.
One other thing to consider is what you're doing to debounce the switch - in an analogue fashion with R-C+Schmitt or monostable, or in a digital fashion where you have a timer routine that samples the input every so often and counts "how many 1s or 0s over the last, say, 16 samples"?
I find that edge triggered interrupts don't work particularly well for manual switch inputs.
I'm relatively new to programming microcontrollers, so I started experimenting on an ATtiny85.
First program was simply turning on an LED and off with a little delay (classic blinky program).
Now I wanted to slowly increase the brightness of the LED. Here is the code:
#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>
#include <stdint.h>
#define LED PB0
void setup(void);
void loop(void);
void analogOutput(int8_t brightness);
int main(void)
{
setup();
while (1)
{
loop();
}
return 0;
}
void setup()
{
TCCR0A = (1 << WGM00) | (1 << WGM01);
TCCR0B = (1 << CS00) | (1 << CS01);
DDRB = (1 << LED);
PORTB = 0;
}
void loop()
{
int8_t brightness = 0;
analogOutput(brightness);
_delay_ms(500);
for (brightness = 0; brightness < 256; ++brightness)
{
analogOutput(brightness);
_delay_ms(10);
}
}
void analogOutput(int8_t brightness)
{
if (brightness == 0)
{
// digital output LOW
TCCR0A &= ~(1 << COM0A1);
PORTB &= ~(1 << LED);
}
else if (brightness == 255)
{
// digital output HIGH
TCCR0A &= ~(1 << COM0A1);
PORTB |= (1 << LED);
}
else
{
PORTB &= ~(1 << LED);
// analog output
TCCR0A |= (1 << COM0A1);
OCR0A = brightness;
}
}
I expected the LED to be turned off for half a second and then increase the brightness.
But if i run the code the LED simply becomes more bright, as if the _delay_ms(500) was ignored.
The analogOutput function is inspired by the function of the Arduino library.
What is wrong here? Why does the delay not work as expected?
EDIT
I changed my loop function, new content:
void loop()
{
analogOutput(127);
_delay_ms(500);
analogOutput(255);
_delay_ms(500);
}
This works, I now have a LED that constantly switches from dimmed to fully on and back.
__delay_ms() is converted to cycle-wasting instructions by the compiler, so as long as your F_CPU matches the current speed of the processor, then it should work as expected.
I would look for other places in your code that are not working as you expect them to.
A half second delay at power up is not a great diagnostic test since it happens quickly, only once, and there may be other things going on then as well.
Maybe instead try blinking between two different brightness levels with a 500ms delay between. Visually this should be unambiguous.
if that works as expected, then maybe try doing a brightness ramp and changing the delay inside the loop to see if that works as expected.
Eventually you will work your way up to something that is not as expected, and then you will know where the problem is. It is still possible you will not understand what is going on, but at least you will be able to ask a much more specific question here and be more likely to get a helpful answer.
Report back with your results!
I feel stupid.. It was just an overflow error, since I used uint8_t and set < 256 in the for loop, the condition was always true (255 + 1 = 0).
My solution was to use uint16_t for the loop
I am new to AVR programming, so sorry if question is trivial.
Using :
OS : Windows7
IDE : Atmel studio
uC = m328p
Pins:
ADC signal - ADC0/PC0
LED_values - (PB0 - PB7)
LED_START - PD1
LED_LIGHT - PD0
BUTTON - PD2
Goal: When you press the button it turns On the LED_START and it needs to start with conversion.
AVR gets interrupt and starts ADC conversion. Basically program has two interrupts. I know that INT0 interrupt has highest priority.
I dont know how to deal with them.
I have tried several things like adding global variable "start" and changing it. And also when i only set LED START it turns On and it stays in that state until LED_values reach certain value, then LED START turns Off by it self.
So please can you show me how to handle two interrupts so that fulfills stated goal and explain me what im doing wrong.
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
#define F_CPU 1000000UL
#define BIT_IS_SET(byte, bit) (byte & (1 << bit))
#define BIT_IS_CLEAR(byte, bit) (!(byte & (1 << bit)))
typedef enum{false, true} bool;
bool previousState = false;
bool start = false;
char num;
void setup();
void loop();
void ADC_init();
void EI_init(); // External Interrupt
int main(void)
{
setup();
loop();
}
void setup(){
DDRC &= ~(0x1); // LDR Input
DDRB = 0xFF; //LEDs value Output
DDRD |= 0x3; //LED light LED start Output
DDRD &= ~(1 << PIND2); //Button Input
}
void loop(){
PORTD |= (1 << PIND2);
EI_init();
ADC_init();
sei();
if(start){
ADCSRA |= (1 << ADSC);
}
while(1){}
}
void ADC_init(){
ADMUX = 0x60;
ADCSRA = 0x8B;
ADCSRB = 0x0;
ADCH = 0x0;
}
ISR(ADC_vect) {
PORTB = ADCH; // assign contents of ADC high register to Port D pins
int b = (int)ADCH;
if(b > 180) { //100
PORTD = 0x1;
}else{
PORTD &= ~(0x1);
}
_delay_ms(100);
ADCSRA |= (1 << ADSC); // start next ADC
}
void EI_init(){
EIMSK |= (1 << INT0); // Interrupt enabled
EICRA |= (1 << ISC00); // any state change
}
ISR(INT0_vect){
if(BIT_IS_CLEAR(PORTD,PIND2)){
start = true;
}else{
start = false;
}
}
Here is scheme : scheme
First of all, you should make start be volatile since it is being used by both the main loop and the interrupt. The volatile keyword tells the compiler that the variable might be modified by things outside of its control, so it cannot optimize away any reads or writes to the variable:
volatile bool start = false;
Secondly, you probably want to remove this line you wrote at the end of loop:
while(1){}
That line is bad because it causes your program to go into an infinite loop where it does nothing. I think you actually want the code you wrote about it in the loop function to run multiple times.
Secondly, after you detect that the start flag has been set, you probably need to set it to 0, or else it will just be 1 forever.
Third, setting start to false in the INT0 ISR might be a bad idea, because it might get set to false before you main loop has a chance to observe it being true and handle the event. I guess it really depends on exactly what you are trying to do. You could try adding details to your question about exactly what problem you are trying to solve using the AVR. See What is the XY problem?.
There are probably other issues with your code that need to be debugged. Can you think of any ways to make this simpler? Maybe you can reduce the number of interrupts you are using. To debug, you can try blinking some LEDs to figure out what parts of your program are executing.
I am trying to do a data transfer control. A button with a pull-up resistor is connected to PINA3 of ATmega644P(Slave). I want to send some data when the button is pressed, if only the bus line is free. For testing purposes, I use another ATmega644P(Master) to send data ,which makes the bus busy, to the Slave one.
This is what I am trying to do: When the button is pressed, start the timer and wait for 300ms. During this time, if it receives some data, go to bus_not_free state. If the timer reaches 300ms while the bus is free, send the data. It works great when the bus is free, but sometimes, it sends the data when the bus is busy.
#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include "uart.h"
#include "bus_free_check.h"
#include "specs.h"
#define F_CPU 4000000UL
#define UART_BAUD_RATE 19200
enum states{
idle_s,
start_timer_s,
wait_s,
bus_check_s,
send_data_s,
bus_not_free_s
};
enum states state=idle_s;
int main(void)
{
DDRA = 0x00; // PORTA is input.
DDRB = 0xFF; // PORTB is output.
DDRC = 0xFF; // PORTC is output.
DDRD = 0b11111010; // PORTD input/output.
PORTA = 0x00;
PORTB = 0x00;
PORTC = 0x00;
TCCR1B = (1 << WGM12);
TIMSK1 = (1 << OCIE1A);
OCR1A = 1171; // 300ms
uart_init( UART_BAUD_SELECT(UART_BAUD_RATE,F_CPU) );
sei();
while(1)
{
switch(state)
{
case idle_s:
bus_free=0;
PORTC=0x01;
if (bit_is_clear(PINA,3)) // If the button is pressed
{
while(bit_is_clear(PINA,3)); // Wait until it is unpressed
{
state=start_timer_s; // Start the timer
}
}
else
{
state=idle_s; // Wait in the idle_s until the button is pressed
}
break;
case start_timer_s:
TCCR1B |= (1 << CS10) | (1<< CS12); // Start the timer
state=wait_s; // Go to wait state and wait for 300ms
break;
case wait_s:
if (bit_is_clear(PIND,0)) // During this waiting, if at any time there is a data transfer going on, go to bus_not_free state.
{
TCCR1B &= ~((1 << CS10) | (1 << CS11) | (1 << CS12)); // Stop the timer
TCNT1=0; // Reset the counter
bus_free=0;
state=bus_not_free_s; // Go to bus_not_free state
}
break;
case send_data_s:
send_target_seri_no(); // Bus is free, you can send the data
TCCR1B &= ~((1 << CS10) | (1 << CS11) | (1 << CS12)); // After sending the data, stop the timer.
TCNT1=0; // And reset the counter.
state=idle_s; // Go to idle_s and get ready
break;
case bus_not_free_s:
uart_puts("Bus is not free \r"); // Bus is not free, can't send the data
state=idle_s; // Go to idle state, and don't give up hope
break;
default:
uart_puts("Fatal Error \r");
break;
}
}
}
ISR(TIMER1_COMPA_vect) // If the timer reaches 300ms, that means the bus is free
{
bus_free=1;
if(state==wait_s) // When the timer hits 300ms, if it is still in wait_s state, send the data.
{
state=send_data_s;
}
}
Thank you.
You've got several problems here. One of them is that your state machine is a little off.
Starting in the idle state, you only leave it if the button is pressed. Is this a mechanical button? If so, it needs to be debounced. That means, basically, waiting for it's state to become stable, and THEN determining if a button press has occurred. Spurious input from the button could cause you to pop in and out of the idle state, confusing both you and your program.
You are also abusing the timer a bit. You don't need the timer on if you are not trying to send something, so turn it off when you are done with it. You are also setting the timer over and over again while waiting for the timeout. Not necessary. I'm pretty sure the logic in the timer function is off. It's hard to tell what you meant to do.
The bus check state is the biggest problem. It does too much. You only need to start the timer once when moving out of the idle state. So separate that into another state. Then all the check state does is move to the send state when the bus is free. Your "three tries" loop is not doing anything: it doesn't wait for anything to change.
The reason you get stuck in the "bus not free" state is this: Suppose that the bus input is 0 (not busy) and bus_free=0. How will it ever set bus_free to 1? Your check time function will, in that case, turn off the timer, so the ISR never fires to change bus_free.
I did not get intended function but I can see two bugs.
1) useless code
for(int i=0; i>3; i++) // Try three times before canceling
{
state=bus_check_s; // Go back and check again
PORTB=0x00; // Led is off
bus_free=0; // Bus is not free
}
state=bus_not_free_s; // After 3 attempts, go to bus_not_free_s and warn the user
bus_free=0; // Bus is not free
it can be simplified to
state=bus_not_free_s;
PORTB=0x00;
bus_free=0; // Bus is not free
2) copy&paste error (if you want really stop timer)
TCCR1B |= (0 << CS10) | (0 << CS11) | (0 << CS12); // Stop the timer.
change to
TCCR1B &= ~((1 << CS10) | (1 << CS11) | (1 << CS12))
I have changed the if condition in wait_s as:
case wait_s:
// rec_char=uart_getc();
if ((unsigned char) rec_char) // During this waiting, if at any time there is a data transfer going on, go to bus_not_free state.
{
TCCR1B &= ~((1 << CS10) | (1 << CS11) | (1 << CS12)); // Stop the timer
TCNT1=0; // Reset the counter
state=bus_not_free_s; // Go to bus_not_free state
}
Now I am not facing the problem I stated in my question. I guess my mistake was to say bus is busy if I receive a zero bit only.