So I have an assignment that wants me to perform certain code when a button on PD2 is presed.
The problem i'am having right now is i don't really know what to check for neither do
i understand the underlying logic.
So this is the code i've come up with thus far.
DDRD=0x00; //PORTD pin 0 as input
PORTD=0x00;
DDRB=0xFF; //PORTB as output
PORTB=0x00;
while(1){
if (PIND & (1<<PD2)==1) // check if PD2 is pressed on PIND2
{
// modify bits on PORTB here
}
}
I'm using Atmega328 and running this on AtmelStudio 7
To set PD2 to an input, run this C/C++ code, which clears bit 2 in register DDRD. There is no need to modify entire registers:
DDRD &= ~(1 << 2);
(You could skip this because every pin will be an input by default after the AVR resets.)
I don't know how you wired your button. If the button is wired between the pin and GND, and there is no external pull-up resistor to pull your pin high, then you should enable the internal pull-up after the pin is an input by running this code, which sets bit 2 in register PORTD:
PORTD |= 1 << 2;
Now to read the state of the button, you can use this C/C++ expression, which will evaluate to 0 if the pin is low or non-zero if the pin is high:
PIND & (1 << 2)
The expression below also works. It has the advantage of always evaluating to 0 or 1:
PIND >> 2 & 1
Here's some (untested) code that ties it all together, reading from PD2 and writing the value of PD2 to an output on PB3:
#include <avr/io.h>
int main()
{
// Make PD2 an input, pulled high.
DDRD &= ~(1 << 2);
PORTD |= 1 << 2;
// Make PB3 an output.
DDRB |= 1 << 3;
while (1)
{
if (PIND >> 2 & 1)
{
// PD2 is high, so drive PB3 high.
PORTB |= 1 << 3;
}
else
{
// PD2 is low, so drive PB3 low.
PORTB &= ~(1 << 3);
}
}
}
You might have to adapt the part of the code that deals with the output. I don't actually know what pin your output is on and what kind of behavior you want, so I just did something simple.
This code is full of the C bitwise operators. It is important for you to get a good book about C and learn exactly what those operators do, and this answer would be too long if I attempted to teach you all of them. Here is a list of operators for you to learn: <<, >>, &, &=, |, |=, ~.
unsigned char input_byte;
while(1){
__no_operation(); /* using nop for synchronization */
input_byte = PIND; /* read entire PIND */
}
This way you should be able to read your inputs every program cycle. But I've not tested this. It's important to use __no_operation() before reading again PIND.
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 am currently in the process of building a drum machine with a micro controller. I am trying to figure out the logic of the sequencer. I have 16 LED's which will indicate at which 16th note the drum machine currently is playing.
So for instance, let's say the beats per minute (BPM) is 120, then the led should shift twice every second.
So for the shifting part I have written code such that if the step number is 0, we shift in a 1. If the step number is > 0, we shift in a 0. The method is called every (60/BPM) seconds.
PD5 is the serial input,
PD4 is the latch pin, PD3 is the clock pin.
void update_led(void) {
if (step_number == 0){
PORTD |= (1 << PD5); //Send a 1
PORTD |= (1 << PD4); //Read the input to 1st led
PORTD &= ~(1 << PD5);
PORTD &= ~(1 << PD4);
PORTD |= (1 << PD3); //Shift to next led
_delay_ms(40); //Apparently I need a delay here to light up LED
PORTD &= ~(1 << PD3);
}else{
PORTD |= (1 << PD4);
PORTD &= ~(1 << PD4);
PORTD |= (1 << PD3);
_delay_ms(40);
PORTD &= ~(1 << PD3);
}
}
But I also want the LEDs to static be lit for each step a user has programmed a sound. So for instance if the user presses button 1 and 5 and 9 and 13 with the instrument kick drum, the LED 1,5,9,13 should be static lit up but the leds should also shift like the above code. Does anyone have any tips on how to efficiently implement this? Is there a smart way to make a specific LED always light up when shifting the other LED's?
this code is supposed to read the state of a digital input pin via a pushbutton and output the state to an LED.
i.e. when input is high, LED is on and vice versa
Since the pushbutton is connected to pull-up resistor, When the pushbutton is pressed the input is supposed to read a LOW and vice versa.
My code:
#include "board.h"
#include <stdio.h>
//setting pointers
#define Port0 ((LPC_GPIO_T *) 0x50000000) //Port 0
#define IOCON ((LPC_IOCON_T *) 0x40044000) //IO configuration
int main(void)
{
/* Initialize pins */
Port0->DIR &= ~((1 << 1)); //PIO0_1 input - onboard switch (unpressed state is pulled-up)
Port0->DIR |= (1<<7); //PIO0_7 output - onboard LED
//Pin configuration
IOCON->REG[IOCON_PIO0_7] &= 0x0 << 3; //No addition pin function
IOCON->REG[IOCON_PIO0_1] &= 0x0 << 3; // "
Port0->DATA[1<<7] &= ~(1<<7); // output initially low
while (1) {
if((Port0->DATA[1<<1]) & (1<<1)) //When input is high
{
Port0->DATA[1<<7] |= (1<<7); //drive PIO0_7 High
}
else
{
Port0->DATA[1<<7] &= ~(1<<7); //Drive PIO0_7 Low
}
}
return 0;
}
When this part of the code is executed PIO0_7 remains remains low unless the button is pressed..However isn't it meant to work the opposite way since switch is pulled-up? I also double checked this with the voltmeter.
I tried changing
if((Port0->DATA[1<<1]) & (1<<1)) //When input is high
to
if(!(Port0->DATA[1<<1]) & (1<<1)) //When input is Low
The LED output remains High, even when the button is pressed.
Assuming your Port0->DATA[0] is pointing to Base-Address 0x5000 0000 and defined as aligned 8bit array then your Pin-Port addressing/masking is wrong.
See LPC111x user manual UM10398 Rev. 12.4 p196 Chapter 12.4.1 write/read data operation:
In order for software to be able to set GPIO bits without affecting any other pins in a single
write operation, bits [13:2] of a 14-bit wide address bus are used to create a 12-bit wide
mask for write and read operations on the 12 GPIO pins for each port.
So there is an offset of 2 bit in the address to get/set the value of your desired pin.
Therefore you must shift your addressing by 2 bit, the following should do the trick:
Port0->DATA[1<<(7+2)] &= ~(1<<7); // output initially low
while (1) {
if((Port0->DATA[1<<(1+2)]) & (1<<1)) //When input is high
{
Port0->DATA[1<<(7+2)] |= (1<<7); //drive PIO0_7 High
}
else
{
Port0->DATA[1<<(7+2)] &= ~(1<<7); //Drive PIO0_7 Low
}
}
I have some trouble with, I guess, the overflow interrupt (used to increase resolution on 8-bit timer from 16µs/step to 1µs/step) in my CODE. It seems like the overflow interrupt triggers while the program is in the if-statements in my main loop and thereby screws thigns up!
if(pInt == 1) //PCNINT-if-statment
{
pulse16 = (tot_overflow << 8) | TCNT1; //adds tot_overflow and TCNT1 to be able to set if-statements in PCINT-while-loop with µs
if(PINB & (1 << PINB3)) //if PB3 is HIGH
{
TCNT1 = 0; //resets Timer/Counter1
tot_overflow = 0; //resets tot_overflow variable
}
else
{
if (pulse16 >1555) //when stick 1 travels from 1555 µs towards 2006 µs
{
PORTB &= ~(1 << relayPin); //relay pole switch, + & - on motor
PORTB |= (1 << greenLED); //LED green indicates forward motion
PORTB &= ~(1 << redLED); //turn off red LED
}
else if (pulse16 <1490) //when stick 1 travels from 1490 ms towards 920 µs
{
PORTB |= (1 << relayPin); //relay pole switch, - & + on motor
PORTB &= ~(1 << greenLED); //turn off green LED
PORTB |= (1 << redLED); //LED red indicates backward motion
}
else //if µs is 1490> or <1555 - dead-span to prevent gliteches on relay when stick is in centre position
{
}
}
pInt = 0; //resets pInt to exit PCNINT-if-statment
}
The pInt is a "flag-variable" that indicates PCINT is triggered.
The tot_overflow variable is increment every time the overflow interrupt is triggered.
I use an ATtiny85 as a RC-switch, it should go LOW on PB2-pin when µs from receiver is above 1555 and go HIGH when µs goes below 1490.
What happens is the following: when checking if µs is above 1555 or below 1490 and using overflow interrupt it sometimes turn the PB2-pin HIGH/LOW in the "dead-span" of 1490-1555 when it shouldn't, and sometimes outside the "dead-span"! Here's a VIDEO on the glitch. Notice that the green LED is the redLED, and the yellow LED is greenLED in my code.
I'm quite new at this and I'm not sure why this is happening and I don't understand why the code won't work. I have looked at the CTC function but I can't see that it would help since I still have to use the timer overflow interrupt to get my wanted reolution of 1µs/step.
EDIT
I have tried a couple of variations (of #yann-vernier's suggestions) and still don't get it to work properly, this is what gets closest to a working code:
while(1) //leave and/or put your own code here
{
static uint8_t tot_overflow; //variable to count the number of Timer/Counter1 overflows
if (TIFR & (1 << TOV1) )
{
TIFR |= (1 << TOV1); // clear timer-overflow-flag
tot_overflow ++;
}
if(GIFR & (1 << PCIF) ) //PCINT-flag idicates PCINT
{
uint16_t pulse; //variable to make a 16 bit integer from tot_overflow and TCNT1
// PORTB |= (1 << debugPin); //pin is HIGH on when interrupt is intialized
pulse = (tot_overflow << 8) | TCNT1; //adds tot_overflow and TCNT1 to be able to set if-statements in PCINT-while-loop with µs
this part I don't get to work:
if ( ((TIFR & (1 << TOV1)) && ((pulse & 0xff))) < 0x80)
{
pulse += 0x100; // Overflow had not been counted
}
Im not sure I get what is happening above, the only thing I know is that I probably do it the wrong way! When I comment the above part it works the same as mu old code!
else
{
if(PINB & (1 << PINB3)) //if PB3 is HIGH
{
TCNT1 = 0; //resets Timer/Counter1
tot_overflow = 0; //resets tot_overflow variable
}
else
{
if (pulse > 1555) //when stick 1 travels from 1555 µs towards 2006 µs
{
PORTB &= ~(1 << relayPin); //relay pole switch, + & - on motor
PORTB |= (1 << greenLED); //LED green indicates forward motion
PORTB &= ~(1 << redLED); //turn off red LED
}
else if (pulse < 1490) //when stick 1 travels from 1490 ms towards 920 µs
{
PORTB |= (1 << relayPin); //relay pole switch, - & + on motor
PORTB &= ~(1 << greenLED); //turn off green LED
PORTB |= (1 << redLED); //LED red indicates backward motion
}
else //if µs is 1490> or <1555 - dead-span to prevent gliteches on relay when stick is in centre position
{
// PORTB |= (1 << greenLED); //for debug to indicate dead-span
// PORTB |= (1 << redLED); //for debug to indicate dead-span
}
}
}
GIFR |= (1 << PCIF); //clear PCINT-flag
}
else
{
}
}
}
ISR(TIMER1_OVF_vect) //when Counter/Timer1 overflows
{
}
ISR(PCINT0_vect) //when pin-level changes on PB3
{
}
Is it close or am I still out in the blue?
Yes, the overflow interrupt could happen at any time (although it does happen regularly). So could the pin change interrupt. Each of them in this case only touch a single 8-bit volatile variable (tot_overflow and pInt respectively), which in turn are polled by your main loop. This construction doesn't really help you unless the main loop has other work to do which may take longer than one full timer period (256*8=2048 cycles in this case).
So instead of enabling interrupts, you could check GIFR bit PCIF (instead of pInt) and TIFR bit TOV1 in your main loop, which would get you more easily understood behaviour. Resetting them is a special case of writing a 1 to that bit and only that bit. This would save you time for the pin change interrupt, as the main loop check could happen more frequently and would not need the latency of jumping to the interrupt service routine.
It would not, however, fix your problem. You are trying to measure a pulse width, which on slightly larger AVRs is easily done using the timer input capture feature. The timers in the ATtiny85 don't have this feature. On AVRs that do, the input capture and timer overflow interrupts are arranged in such a way that the input capture interrupt can safely read the software driven overflow counter.
When you get a timer overflow, you increment tot_overflow, and when you've detected a pin change, you read TCNT1 to combine the values. These two counters, while one feeds the other, are not read at the same time. Your threshold values are 0x5d2 and 0x613. If the rollover occurs after reading tot_overflow but before reading TCNT1, you may well get a time like 0x501 at a time which should be 0x601. Similarly if you stop tot_overflow from updating. If you read TCNT1 before tot_overflow, you might get 0x6fe when you should have read 0x5fe. Simply put, there is no safe order - but there is a relation. What we need to know is if the overflow count value was up to date or not at the time the counter value was read.
static uint8_t ovf;
if (TIFR & 1<<TOV1) {
TIFR = 1<<TOV1; // Clear overflow flag
ovf++;
}
if (GIFR & 1<<PCIF) {
GIFR = 1<<PCIF; // clear pin change flag
uint16_t timestamp = ovf<<8 | TCNT1;
if (TIFR&1<<TOV1 && (timestamp&0xff)<0x80)
timestamp += 0x100; // Overflow had not been counted
// Do what we like with the timestamp here
}
The key is that we check for an overflow after we have already loaded the timer value. If the overflow occurred before we read the value, the value read must be low; otherwise we want the old count. This particular sample actually relies on the overflow not being handled in an interrupt, but you can use the same method by enclosing the block in an interrupt masking. We need the two reads of TOV1 and ovf to match, and to read TOV1 after TCNT1. What we don't want is to have an interrupt process and thus clear TOV1 so that we can't infer the order of ovf and TCNT1. Note that using a pin change interrupt to do this logic grants us that automatically, since interrupt handlers run with interrupts disabled.
You won't get higher precision on your pulse widths than the latency variance for responding to the pin change, and in the code you've shown, the timer reset (which should also reset the prescaler, bit PSR1 in GTCCR). This usually means you do want to read the timer in the interrupt handler itself. We can also observe that you could choose a timer pace that makes your thresholds fit in the 8 bit value; for instance with timer1 running at 8MHz/64, your thresholds would be at 186 and 194, with an offset of 16-24µs. One might even do tricks like setting one threshold precisely at an overflow since the timer doesn't have to start at 0.
#YannVernier Thanks for pushing me in the right direction, or giving me the rigth way to do it! ;-) I think I finally nailed it, with a litle extra help from a friend that is!
Here is the final CODE
I didn't first get that I had to remove the TIMSK enable ande sei() plus the ISR routines, also the else-statement that was accidently put after:
if ( ((TIFR & (1 << TOV1)) && ((pulse & 0xff))) < 0x80)
{
pulse += 0x100; // Overflow had not been counted
}