Circular bit shift with LED's

Go To Last Post
18 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello 

 

First of all I'm totally new to bit manipulation and I have only before this worked with Arduino. So what I'm trying to do is that I'm programming a function on atmega1284, where I'm trying to make a LED chaser on PORTA. It should run for x amount of shifts going from LSB to MSB (or for that sake the other way around). So for that I'm guessing I would be needing a circular bit shift.

 

An example would be that rand() gives out integer 21 and the function then shifts the bits so the LED light up one by one for 2 full circles and ending on 0b00010000 where it stops.

 

This is my code so far (and yes there are parts that is work under progress and mostly for testing and learning). Any help is very appreciated because if been stuck for 20+ hours and this point on trying to make this work.

 

#define F_CPU 16000000

#include <avr/io.h>
#include <util/delay.h>
#include <stdlib.h>

#define BUTTON_PRESSED(0 == (PIND & (1 << PORTD2)))
#define LEDSOFF 0
#define LEDSRUNNING_SLOW 1
#define LEDSRUNNING_FAST 2
#define LEDSBET 3

unsigned char LEDState = LEDSBET;
unsigned char CurrentButtonState = 0;
unsigned char PreviousButtonState = 0;

int RotateLeftSlow(int shift) {
    if (shift & (PORTA = (unsigned char) 0x8000)) {
        PORTA = (shift <<= 1);
        _delay_ms(100);
        PORTA = (shift |= 1);
        _delay_ms(100);
    } else {
        PORTA = (shift <<= 1);
        _delay_ms(200);
    }
    return (shift);
}

int RotateRightFast(int shift) {
    if (shift & (PORTA = 1)) {
        PORTA = (shift >>= 1);
        PORTA = (shift |= 0x8000);
        _delay_ms(100);
    } else {
        PORTA = (shift >>= 1);
        _delay_ms(100);
    }
    return (shift);
}

void LEDOff() {
    PORTA = 0x00;
}

void Bet() // Make a betting system....
{
    /*for(int i=0; i<8; i++)
    {
    	PORTA = (0b00000001<< i);
    	_delay_ms(100);
    }*/
}

int main(void) {
    // Using DDR register to set all pins for PORTA to output
    DDRA = 0xFF;

    // If PORTxn is written logic one when the pin is configured as an input pin, the pull-up resistor is activated.
    DDRD &= ~(1 << DDD2);
    PORTD |= (1 << DDD2);

    while (1) {
        // Handle the Button activation (going from not pressed to being pressed)
        PreviousButtonState = CurrentButtonState;
        CurrentButtonState = BUTTON_PRESSED;

        if ((PreviousButtonState == 0) && (CurrentButtonState != 0)) { // Change LED state based on the previous state
            if (LEDState == LEDSOFF) {
                LEDState = LEDSRUNNING_SLOW;
            } else
            if (LEDState == LEDSRUNNING_SLOW) {
                LEDState = LEDSRUNNING_FAST;
            } else
            if (LEDState == LEDSRUNNING_FAST) {
                LEDState = LEDSBET;
            } else
            if (LEDState == LEDSBET) {
                LEDState = LEDSOFF;
            }
        }
        // Handling the output based on the state
        if (LEDState == LEDSOFF) {
            LEDOff();
        }

        if (LEDState == LEDSRUNNING_SLOW) {
            RotateLeftSlow(rand() % 25);
        }

        if (LEDState == LEDSRUNNING_FAST) {
            RotateRightFast(rand() % 25);
        }

        if (LEDState == LEDSBET) {
            Bet();
        }
    }
}

 

This topic has a solution.
Last Edited: Wed. Sep 15, 2021 - 10:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

welcome to AVRFreaks

 

kte86 wrote:
I'm totally new to bit manipulation

Start here: https://www.avrfreaks.net/forum/tut-c-bit-manipulation-aka-programming-101

 

note that it's all standard C syntax - so you could practice on a "normal" system, like a PC

 

the key thing about a rotate is that, after you've shifted a bit out of one end,  you have to shift it back in at the other ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

How many LEDS are you working with, just 8 connected to PORTA?

 

 

 

 

Last Edited: Tue. Sep 14, 2021 - 03:28 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you and I will have a look at it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

nikm wrote:

How many LEDS are you working with, just 8 connected to PORTA?

 

 

 

Yes that is correct. The general idea is like a roulette game where when I push the button the LEDS should rotate for x amount and then landing on a random single lighted LED

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Why mix in delays all over with the shift??---just makes a mess.

 

Do your shifting, then if wanted, call a delay afterwards

 

Don't forget to debounce you button(s)

 

If you use assembler, a ROR command will rotate right, through the carry bit

Some other brands of micros have an additional rotate style, that rotates from the lsb to msb (or msb into lsb), bypassing the carry bit (that would be perfect for your app)

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

kte86 wrote:
This is my code so far (and yes there are parts that is work under progress and mostly for testing and learning)
Don't write the code while you are still designing the solution or what you initially write may bog you down into some convoluted solution that is trying to work around what has already been written. Do the design work separately. When you have a very clear picture of how the code should look in your head then convert that into C and you'll end up with a much cleaner solution.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

I think you're introducing to many new concepts to start with.  The old adage goes 'make it work then make it better'

 

You don't need to do bit shifting for this to start with, it'll be plenty fast enough just to turn the leds on and off.  ie

 

While(button not pressed)

{

for x = 0; x < 8; x++)

     {

   Led(x) on

   led(x-1) off // Take care of 0 position ie if x =  0 then turn off 7

   _delay_ms(250);

    }

}

// button pressed

y = random(count)

 

while(-1)

{

for x = 0; x < 8; x++)

     {

     Led(x) on

     led(x-1) off // Take care of 0 position ie if x =  0 then turn off 7

     _delay_ms(250);

    count++;

    if(count > y)

       exit();

   }

}//end

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

avrcandies wrote:

Why mix in delays all over with the shift??---just makes a mess.

 

Do your shifting, then if wanted, call a delay afterwards

 

Don't forget to debounce you button(s)

 

If you use assembler, a ROR command will rotate right, through the carry bit

Some other brands of micros have an additional rotate style, that rotates from the lsb to msb (or msb into lsb), bypassing the carry bit (that would be perfect for your app)

 

Well without the delay they are flickering so fast that it looks like they are just on all the time but I will try and place the delay elsewhere. Unfortunately I don't know assembler so I would prefer it as pure c. 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for the advice. I will try and do more structured programming in the future.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kte86 wrote:
Well without the delay they are flickering so fast that it looks like they are just on all the time but I will try and place the delay elsewhere.

count = get_a_count();

for (i = 0; i < count; i++)
{
    shift_leds_one_over();
    delay();
}

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With high level language the "shifting" can just be done with high level loops.

 

have a routine that lights the needed led 

 

 this can just  be a bunch of if else

Illuminate_Led(position)

  if (position==0)

       {PORTA = 0x01;}

  else if (position==1)

       {PORTA = 0x02;}

  else if (position==2)

       {PORTA = 0x04;}

    etc

 

Then just form loops  ,

say from 0 to 6 and calling the Illuminate_Led

form a following loop  7 to 1 and calling Illuminate_Led

repeat these two loops endlessly   0123456 7654321 0123456 ....etc

 

toss in a delay at the exit of  Illuminate_Led

When in the dark remember-the future looks brighter than ever.   I look forward to being able to predict the future!

Last Edited: Tue. Sep 14, 2021 - 05:01 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

With a CPU as big (in memory and resources) as the ATmega1284, you don't want to tie up the whole CPU with something as simple as running a single 8-bit/8/_LED "Knight Rider/Cylon LED" strip.

 

Set one of the timers to interrupt every 1 milli-second.   Increment a volatile 32-bit integer counter ('millis')  on each interrupt.   Have a 16-bit integer that counts the number of 1ms interrupts before the LED changes.  This is the LED_interval.     Each single lighted LED position is a 'frame'.  Basically you have an animation with 14 frames, 8 frames of moving the LED right from position 0 to position 7, and then 6 frames of moving the LED left from position 6 to position 1.  Positions 0 and 7 are lighted for only one LED interval.

   Say you are rotating a single lighted LED back-and-forth from position 0 on the far right to position 7 on the far left; 8 positions total; twice a second.  That is 28 frames a second: 36 ms per frame.  Have an array of 14 8-bit values with each value representing how PORTA will be lighted with one LED for that frame:  LightedLED[14] = {  0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02 };    Now you need a 'frameCounter' variable.   Increment it each time that you change the LED frame with in the main's while(1) loop:

 

#define  MS_PER_FRAME  36

 

unsigned long   millis=0, currentFrameIntervalEnd = MS_PER_FRAME;

 

   if (millis == currentFrameIntervalEnd) {

        currentFrameIntervalEnd = millis + MS_PER_FRAME;

        PORTA = LightedLED[frameCounter]; 

        frameCounter += 1;

        if (frameCounter == 14) frameCounter=0;

    }

 

  There are no delay routines so that the CPU can be running through all the other things in the while(1) loop,  while running the LED chaser seemingly in the background.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you so much this really seems like something that would work great for me. Just have to read up on timers first. Haven't been working with that before 

This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

kte86 wrote:

int RotateLeftSlow(int shift) {
    if (shift & (PORTA = (unsigned char) 0x8000)) {
        PORTA = (shift <<= 1);

 

Your test against 0x8000 is wrong - 0x0100 is probably what you meant.

 

However - Your main bug is that you don't have a shifting loop for the number of shifts requested.

 

Try this code instead: (NB: I've used 8-bit shits instead of 16-bit shifts. You do know AVR is only an 8-bit processor so there is a cost for 16-bit operations)

 

void RotateLeftSlow (uint8_t nrShifts)
{
    /* You might want a random starting pattern; you didn't say */
#   define START_PATTERN 0b00000001

    uint8_t pattern = START_PATTERN;
    PORTA = pattern;
    _delay_ms(100);

    for (uint8_t shift = 0; shift < nrShifts; shift++) {
        if (pattern)
            pattern <<= 1;
        else
            pattern = 1;

        PORTA = pattern;
        _delay_ms(100);
    }
}
Last Edited: Wed. Sep 15, 2021 - 09:56 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you so much. This was exactly what I was looking for :)

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For a genuine circular shift, I'll just throw this in, would work with any pattern

static void circular_left_shift(uint8_t n)
{
    static uint8_t pattern = 0x80;
    while (n--)
    {
        pattern = (pattern << 1) | (pattern >> 7);
        printf("pattern %02x\n", (unsigned int)pattern);
    }
}

int main(void)
{
    circular_left_shift(5);
    circular_left_shift(5);
}

pattern 01
pattern 02
pattern 04
pattern 08
pattern 10
pattern 20
pattern 40
pattern 80
pattern 01
pattern 02

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I would suggest thinking in terms of individual pins where you are not tied to dealing with pins on a single port. You can then use as many/few pins as needed and they can be on any pins you want. This is handy for board layout, avoiding pins that are needed for peripheral duties, etc. For the most part, it matters little that there will be some extra clock cycles involved when 'manually' manipulating pins (peripherals take care of the precision jobs anyway). I do this for about everything- from multiplexed 14segment led's to 4bit lcd driver, and I don't think I have anything that deals with pins on a port wide basis. This also allows initializing pins on use so you do not need to create the usual pin init code.

 

https://godbolt.org/z/5qMa59fE9

 

There are many ways to go about it, and the above is one way. Although those pin functions can be made static/inline/always_inline, the 'original' avr like a 1284 will still end up with non-atomic code when something like the above is done so would need to interrupt protect them if also manipulating pins in isr's (which is what arduino has to do also). Newer avr's like the 0/1 series (and most other modern mcu's) have port set/clr registers so you get atomic in all cases.