Arduino bitwise runninglight with a single delay - c

I have the assignment to create a running light on my Arduino with the following requirements/constraints
Requirements
Implement a socalled “runninglight” with 3 leds.
The on time of each led is the same, being 0.5 sec.
After the last Led is lit, the first starts again.
Constraints
The leds are connected to pin 8,9 and 10 of the Arduino. (don’t forget the resistors)
The running led must be implemented in the loop of the Arduino
The setup() function may contain any Arduino function/macro.
Only the Arduino function delay() is allowed in the loop.
No use of any other Arduino function/macro etc. in the loop.
No use of for/while in the loop() function
No use of your own defined functions
Your loop function must contain not more then 10 statements.
So in the loop you may only use
o Delay function
o Your variables
o The following characters/digits: * ( ) & ~ | ; = < > ! + - 0 1 2 3 4 5 6 7 8 9
I have tried the following code
#include <Arduino.h>
//mask for setting correct bits in register to setup leds to 1
#define DDBMASK 0x07
//masks for setting correct bits in register for led control to 1
#define LEDMASK 0x01
byte *ddBPtr; //point to ddB register (data direction)
byte *portBPtr; //point to portB resiger (data register)
void setup() {
//setup data direction register to set pins to output
ddBPtr = (byte *) 0x24;
*ddBPtr |= DDBMASK; // 0b0000 0111
//assign pointer to right register
portBPtr = (byte *) 0x25;
}
void loop() {
//use data register (portB) for controlling leds
*portBPtr ^= LEDMASK;
delay(500);
*portBPtr ^= LEDMASK;
*portBPtr ^= LEDMASK << 1;
delay(500);
*portBPtr ^= LEDMASK << 1;
*portBPtr ^= LEDMASK << 2;
delay(500);
*portBPtr ^= LEDMASK << 2;
}
Apparently this is possible with only one delay function and following the requirements and constrains. I have been stuck on this for hours and cant figure it out.
I tried the following which also does not work because I'm unable to reset my counter back to zero
void loop() {
//use data register (portB) for controlling leds
*portBPtr ^= (LEDMASK << ledCount);
delay(500);
*portBPtr ^= (LEDMASK << ledCount);
ledCount++;
//cant figure out a way to reset ledCount back to 0 after 3 itterations
//running light only runs once so first led does not light after third led is lit
}
What am I missing?

Perhaps this:
*portBPtr |= (1 << ledCount);
delay(500);
*portBPtr &= ~(1 << ledCount);
ledCount = (9 >> (ledCount << 1)) & 3;
This also avoids ^ which is not in your list of allowed symbols.
The line ledCount = (9 >> (ledCount << 1)) & 3 is like a lookup table with each entry taking up two bits. It maps 0 to 1, 1 to 2, 2 to 0. The normal way to write this code is ledCount = (1 + ledCount) % 3;
This might also work, being a little simpler:
delay(500);
*portBPtr = (1 << ledCount);
ledCount = (9 >> (ledCount << 1)) & 3;
By using assignment rather than | or & or ^, all the other bits will be zero, presumably causing the non-current leds to turn off.

Related

How to check if a certain PIN on PORTx is pressed

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.

How to read data from dht11 using atmega328p

Using IDE : AtmelStudio, uC = atmega328p, sensor DHT11
Im trying to read the values from DHT11.
I have set the start signal
DDRC |= 1 << TEMP_SENZOR; //output
_delay_ms(2000); // wait for 2s according to datasheet
PORTC &= ~ (1 << TEMP_SENZOR); //set low for at least 18 ms
_delay_ms(20);
PORTC |= 1 << TEMP_SENZOR; // set high
Then i have set getResponse metod
DDRC &= ~ (1 << TEMP_SENZOR); // set for input
while(PINC & (1 << TEMP_SENZOR));
while(PINC & (1 << TEMP_SENZOR) == 0);
while(PINC & (1 << TEMP_SENZOR));
I have tried to code function several times, to get values but unfortunately i get some values that have nothing to do with reading from Proteus DHT11.
Im new to this, so sorry if question is trivial.

AVR ATMega328P ADC channel selection issue

I'm tinkering around with an ATMega328P right now and wanted to read an analogue value from a pin through the ADC and simply output the value to 4 LEDs. Really simple
#define F_CPU 20000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#define BRIGHTNESS_PIN 2
#define ADC_SAMPLES 5
void init_adc()
{
//set ADC VRef to AVCC
ADMUX |= (1 << REFS0);
//enable ADC and set pre-scaler to 128
ADCSRA = (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | (1 << ADEN);
}
uint16_t read_adc(unsigned char channel)
{
//clear lower 4 bits of ADMUX and select ADC channel according to argument
ADMUX &= (0xF0);
ADMUX |= (channel & 0x0F); //set channel, limit channel selection to lower 4 bits
//start ADC conversion
ADCSRA |= (1 << ADSC);
//wait for conversion to finish
while(!(ADCSRA & (1 << ADIF)));
ADCSRA |= (1 << ADIF); //reset as required
return ADC;
}
int main(void)
{
uint32_t brightness_total;
uint16_t brightness = 0;
uint32_t i = 0;
init_adc();
sei();
while (1)
{
//reset LED pins
PORTB &= ~(1 << PINB0);
PORTD &= ~(1 << PIND7);
PORTD &= ~(1 << PIND6);
PORTD &= ~(1 << PIND5);
PORTB |= (1 << PINB1); //just blink
read_adc(BRIGHTNESS_PIN); //first throw-away read
//read n sample values from the ADC and average them out
brightness_total = 0;
for(i = 0; i < ADC_SAMPLES; ++i)
{
brightness_total += read_adc(BRIGHTNESS_PIN);
}
brightness = brightness_total / ADC_SAMPLES;
//set pins for LEDs depending on read value.
if(brightness > 768)
{
PORTB |= (1 << PINB0);
PORTD |= (1 << PIND7);
PORTD |= (1 << PIND6);
PORTD |= (1 << PIND5);
}
else if (brightness <= 768 && brightness > 512)
{
PORTB |= (1 << PINB0);
PORTD |= (1 << PIND7);
PORTD |= (1 << PIND6);
}
else if (brightness <= 512 && brightness > 256)
{
PORTB |= (1 << PINB0);
PORTD |= (1 << PIND7);
}
else if (brightness <= 256 && brightness >=64)
{
PORTB |= (1 << PINB0);
}
_delay_ms(500);
PORTB &= ~(1 << PINB1); //just blink
_delay_ms(500);
}
}
This works kind of fine, except the channel selection. When I select a channel it works fine, but independently from the selected channel, channel 0 also always reads and converts. What I mean with that is that if I plug the cable into the selected channel pin, it reads the values correctly. When I plug it into any other channel pin it obviously doesn't, except for ADC0. No matter what channel I set, not only does that one read but also ADC0.
Why is that and how do I fix that?
I already checked my PCB for solder bridges, but there are none and I would also expect some slightly different behaviour with that.
Also ADC4 and ADC5 don't seem to properly convert either. Any idea why that is? The only clue I found in the datasheet is, that those two use digital power, while all the other ADCs use analogue. What's the difference, why does it matter and why does it not correctly convert my anlogue signal?
Both ARef and AVCC are connected according to the datasheet, with the exception that the inductor for ARef is missing.
I think what is happening is that
ADMUX &= (0xF0);
is setting the channel to 0, and
ADMUX |= (channel & 0x0F);
is then setting the channel to the one you want. You're then taking a reading and throwing the result away, which should mean that the initial channel being set to 0 doesn't matter.
Howevever, when you then try to take an actual reading, you are setting the channel again, by using read_adc to take the reading. So, you don't ever throw a reading away.
What I would do is replace your ADMUX setting commands with:
ADMUX = (0xF0) | (channel & 0x0F)
Then move this into a separate function called something like set_adc_channel(int channel). Include a throw away read in that function, then remove the ADMUX setting from your read_adc function. Just start a conversion and get the result.
Also note that since you only ever use one channel, you could move the channel setting part to init_adc(). I assume it's in a separate function so you could later read more than one channel.
I hope that's clear. Let me know if not.
EDIT: So as you stated, ADIF is really reset by writing logic 1.
I've just tested your adc_read function and it is working for me (if you don't mind Arduino mixture)
uint16_t read_adc(unsigned char channel)
{
//clear lower 4 bits of ADMUX and select ADC channel according to argument
ADMUX &= (0xF0);
ADMUX |= (channel & 0x0F); //set channel, limit channel selection to lower 4 bits
//start ADC conversion
ADCSRA |= (1 << ADSC);
//wait for conversion to finish
while(!(ADCSRA & (1 << ADIF)));
ADCSRA |= (1 << ADIF); //reset as required
return ADC;
}
void setup() {
Serial.begin(57600);
//set ADC VRef to AVCC
ADMUX |= (1 << REFS0);
//enable ADC and set pre-scaler to 128
ADCSRA = (1 << ADPS0) | (1 << ADPS1) | (1 << ADPS2) | (1 << ADEN);
pinMode(A0, INPUT_PULLUP);
pinMode(A1, INPUT_PULLUP);
pinMode(A2, INPUT_PULLUP);
pinMode(A3, INPUT_PULLUP);
}
void loop() {
Serial.println(read_adc(0));
Serial.println(read_adc(1));
Serial.println(read_adc(2));
Serial.println(read_adc(3));
delay(1000);
}
I just connect one of these channels to 3.3V pin and it'll read 713 on it. Other channels are pulled up to levels about 1017.

ADC LPC1768 simple

this is my code
#include "LPC17xx.h" // Device header
#include "GPIO_LPC17xx.h" // Keil::Device:GPIO
uint32_t voltag1 = 0 ;
uint32_t voltag2 = 0 ;
volatile uint32_t adstat;
int blink=1;
int main()
{
//Config timer
LPC_TIM1->MCR=2;
LPC_TIM1->MR0=20000000; //Match Resgister
LPC_TIM1->TCR=1;
LPC_TIM1->EMR = 0x00000030 ;
//Config ADC
LPC_PINCON->PINSEL1 |= (1 << 14) | (1 << 16); // connect pin to ADC
LPC_SC->PCONP |= ((1 << 12)); //enable power of ADC
LPC_ADC->ADCR = 0x06202001; //initialaze ADC
LPC_ADC->ADINTEN = 0x00000100; // global interup
NVIC_EnableIRQ(ADC_IRQn);
GPIO_SetDir(3,25,GPIO_DIR_OUTPUT);
while(1) {}
}
void ADC_IRQHandler(void)
{
adstat = LPC_ADC->ADSTAT; /* Read ADC clears interrupt */
blink++;
GPIO_PinWrite(3,25,blink%2);
voltag1 = (LPC_ADC->ADGDR >> 4) & 0xFFF;
//voltag2 = (LPC_ADC->ADDR1 >> 4) & 0xFFF;
}
when i use LPC_ADC->ADGDR it work fine but when i use LPC_ADC->ADDR1 its not working , why?
i used MAT for ADC interup
and when i use LPC_ADC->ADGDR everything works fine
but when i use LPC_ADC->ADDR1 for reading its not working and not change with MAT edge
There are way too many magic numbers in your code. Please use the corresponding defines for all those bits. This code is unreadable as is.
There is a wrong comment in your code, the interrupt flag for ADGDR is not cleared on ADSTAT read. Only a read of ADGDR clears it, and thats why your code does not work with ADDR1.
Had you used the definition to set ADINTEN, you might have spotted your error sooner. That flag is named ADGDINTEN and corresponds to the flag in ADGDR only.
You should enable IRQ of ch1 by:
LPC_ADC->ADINTEN = 1 << 1; //0x0002

glitches when using time overflow interrupt

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
}

Resources