Introduction: AVR Microcontroller. LEDs Blinking in "serial", "parallel" and "random" Modes. Using the Analog Digital Convertor to Read Digital Values As Seed Values for Random Number Generator

About: Welcome to FOG channel! "Hobby" "DIY" "Electronics" "Programming" "Video Games" "Media Design" "Digital Art" and more... https://www.you…

In this case we learn how to make simple program in C code for AVR microcontroller to control the flashing of three LEDs in serial, parallel and random flashing modes. To do this, we will use three i/o ports of the microcontroller.

Also we will cover the pseudo random number generation function. We will try to use the analog digital convertor to read digital values as seed values for random number generator.

We will assemble the electrical circuit on base of the AVR ATmega328PU to check the work of program code.

Step 1: Programming in C Code for AVR Microcontrollers (Atmel Studio)

1. Telling controller crystal frequency (16 MHz AVR ATMega328P):

#ifndef F_CPU
#define F_CPU 16000000UL 
#endif

2. Header to enable data flow control over pins. Defines pins, ports, etc.:

<p>include <avr/io.h></p><p><avr io.h=""></avr></p>

Header to enable delay function in program:

<p>#include <util/delay.h></p><p><util delay.h=""></util></p>

Header defines four variable types, several macros, and various functions for performing general functions:

#include <stdlib.h> 

Stdlib.h is a utility functions such as string conversion routines, memory allocation routines, random number generator, etc.

3. Defines pins of the chip i/o ports for attaching leds:

#define LED1 0 // Led1 (green) connected to port B pin 0 
#define LED2 1 // Led2 (blue) connected to port C pin 1
#define LED3 2 // Led3 (red) connected to port D pin 2

4. Defines the settings for options the modes of blinking leds:

#define NUMBER_OF_REPEAT 3  // Number of repetitions of blinking for each modes

5. Settings the delay time of blinking for each modes:

#define DELAY1 2 // 200 milliseconds delay
#define DELAY2 5 // 500 milliseconds delay
#define DELAY3 7 // 700 milliseconds delay
#define DELAY4 10 // 1 second delay

6. Definition of the function to execute a timing delay in milliseconds for the AVR microcontroller. The mode of delay is set by function in-parameter n_delay:

void ch_delay(unsigned char n_delay)
{
       switch(n_delay){
       case 2:
         _delay_ms(200); // to make a delay of 200 milliseconds
       break;
       case 5:
         _delay_ms(500); // to make a delay of 500 milliseconds
       break;
       case 7:
         _delay_ms(700); // to make a delay of 700 milliseconds
       break;
       default:
         _delay_ms(1000); // to make a delay of 1 second
       break;
       }
}

7. Definition of the function to set microcontroller i/o ports configuration:

void init_ports_mcu()
{
       DDRB=0xFFu; // Set all pins of the PORTB as output.

The Data Direction Register B (DDRB) allows us to make the bits of register B input or output. Writing a 1 makes them output, while a 0 would make them input. FF in binary notation it's all eight digits is one.

       PORTB=0x00u; // Write data on port B. Set all pins of PORTB low which turns it off.  

To do the same for ports C and D:

       DDRC=0xFFu;
       PORTC=0x00u;
       DDRD=0xFFu;
       PORTD=0x00u;
}

8. Definition of the function to blink leds in serial mode. The config of mode is set by function in-parameters:

void parallel_mode_leds(unsigned char number_repeat, unsigned char ms_delay)
{

The for loop executes turning on three leds at once a specific number of times, that depends on function parameter:

       for (unsigned char i=0; i < number_repeat; i++) 
       {
             PORTB = (1 << LED1); // to turn on the first LED
             PORTC = (1 << LED2); // to turn on the second LED
             PORTD = (1 << LED3); // to turn on the third LED
             ch_delay(ms_delay); // creates a delay
             PORTB &= ~(1 << LED1); // to turn off the first LED
             PORTC &= ~(1 << LED2); // to turn off the second LED
             PORTD &= ~(1 << LED3); // to turn off the third LED
             ch_delay(ms_delay); // creates a delay 
       }

The for loop executes turning on second and third leds at once a specific number of times, that depends on function parameter:

       for (unsigned char i=0; i < number_repeat; i++) 
       {
             PORTC = (1 << LED2); // to turn on the second LED
             PORTD = (1 << LED3); // to turn on the third LED
             ch_delay(ms_delay); // creates a delay      
             PORTC &= ~(1 << LED2); // to turn off the second LED
             PORTD &= ~(1 << LED3); // to turn off the third LED
             ch_delay(ms_delay); // creates a delay
       }

The for loop executes turning on first and second leds at once a specific number of times, that depends on function parameter:

       for (unsigned char i=0; i < number_repeat; i++) 
       {
             PORTB = (1 << LED1);
             PORTC = (1 << LED2);
             ch_delay(ms_delay);        
	     PORTB &= ~(1 << LED1);
             PORTC &= ~(1 << LED2);
             ch_delay(ms_delay);          
       }

The for loop executes turning on first and third leds at once a specific number of times, that depends on function parameter:

       for (unsigned char i=0; i < number_repeat; i++)
       {
             PORTB = (1 << LED1);
             PORTD = (1 << LED3);
             ch_delay(ms_delay);           
	     PORTB &= ~(1 << LED1);
             PORTD &= ~(1 << LED3);
             ch_delay(ms_delay);
       }
}

9. This statement is definition of the function to blink leds in random mode. The config of mode is set by function in-parameters:

void random_mode_leds(unsigned char number_repeat, unsigned char ms_delay)
{
       int rand_Led=0;

The for loop executes turning on leds in random order a specific number of times, that depends on function parameter:

       for (unsigned char i=0; i<number_repeat * 10; i++)
       {         

This statement returns a pseudo-random number in the range of 1 to 7. The number in binary notation defines the leds that will be turn on:

	    rand_Led = rand() % 7 + 1;
            PORTB = (((rand_Led & 1)>0)  << LED1); // turn on the first LED, depending on value of generated random number.
            PORTC = (((rand_Led & 2)>0)  << LED2); // turn on the second LED, depending on value of generated random number.
            PORTD = (((rand_Led & 4)>0)  << LED3); // and turn on the third LED, depending on value of generated random number.
            ch_delay(ms_delay); // creates a delay

<br>

To turn off all the leds:

	    PORTB &= ~(1 << LED1);
            PORTC &= ~(1 << LED2);
            PORTD &= ~(1 << LED3);
            ch_delay(ms_delay); //  creates a delay
       }}

The rand() function in C gives a pseudo-random number generator. The rand() function uses a formula that calculates new "random" numbers based on a formula that includes the previously generated value. So where to get the "starting point" for the first number in the formula?

The standard C library maintains an internal state for the random number generator, and function srand() can seed this state. What to pass to this function?

A trick you may be able to use, depending on your setup is to use the analog digital convert to read voltage level on a pin that is "floating" or otherwise not tied well to a particular voltage.

<p><avr io.h=""></avr></p><p><avr io.h=""></avr></p>

Step 2: Explanation: Analog to Digital Converter (ADC)

So for transferring external continuous information (analog information) into a digital/computing system, we must convert them into integer (digital) values. This type of conversion is carried out by Analog to Digital Converter (ADC). The process of converting an analog value into digital value is known as Analog to Digital Conversion.

In short, Analog signals are real world signals around us like sound and light. Digital signals are analog equivalents in digital or numeric format which are well understood by digital systems like microcontrollers. ADC is one such hardware which measures analog signals and produces a digital equivalent of the same signal. AVR microcontrollers has inbuilt ADC facility to convert analog voltage into an integer. AVR convert it into 10-bit number of range 0 to 1023.

Note that we have an Analog Reference (Aref) Voltage also, which will be considered equivalent to 1023 and any voltage value less than this Aref will have less number than 1023. The input range is 0-Aref and digital output is 0-1023. Here 0V will be equal to 0, and Aref/2 will be equal to 512 and so on.

The Atmega328PU comes with a 10-bit, 6 channel inbuilt Ananlog to Digital Convertor (ADC), so the controller can detect a sense a minimum change of 5 millivolts, so if the reference voltage is 5 volts. So for every 5 millivolts increment in the input we will have a increment of one at digital output.

First of all we need to enable the ADC feature in ADC. The ADC Control and Status Register A allows us to enable the ADC and set the sampling rate. (ADCSRA):

Bit 7 is ADC Enable. Writing this bit to one enables the ADC. By writing it to zero, the ADC is turned off.

Bits: 0,1,2 are ADC Prescaler Select Bits. These bits determine the division factor between the oscillator frequency and the input clock to the ADC.

We will first initialize the ADC with the following code: enable ADC by Setting «ADC Enable» bit of ADC Control and Status Register A and set the division factor between the oscillator frequency (i.e 16 MHz) and the input clock to the ADC as 128.

Select channel zero by default using the ADC Multiplexer Select register(ADMUX).

Bit 7 and 6 are Reference Selection Bits.These bits select the voltage reference for the ADC, in this case it is AREF, internal voltage reference turned off. So, bit 6 and 7 can be left set to 0.

Bits 0 to 4 are Analog Channel and Gain Selection Bits. The value of these bits selects which combination of analog inputs are connected to the ADC. We are going to choose channel 0.

Step 3: Continuation of Programming Code

10. The function performs ADC Initialization in accordance with our settings:

void ADC_init(){

Enable ADC and set prescaler to max value, 128:

       ADCSRA |= (1<<ADEN) | (1<<ADPS2) | (1<<ADPS1) | (1<<ADPS0);

Select the required channel for analog input is connected to the ADC, in this case it is channel 0:

      ADMUX=0x00;}

11. Let us write function to read the result of analog to digital convert:

uint16_t ADC_value()
{
       _delay_ms(10); // Wait for some time 

Start the ADC conversion by setting ADSC bit in ADC Control and Status Register A:

	ADCSRA |= (1<<ADSC);

Wait until ADC converstain complete. ADSC bit will be zero after completion:

	while(ADCSRA & (1<<ADSC));
	_delay_ms(10); // Wait for some time 

Read the ADC register and return the result of conversion:

	unsigned int seed = ADC;
	ADCSRA &= ~(1<<ADEN); // disable ADC
	return(seed); // return the result of conversion 
}

12. The main() function is unique and set apart from all other functions. Every C program must have exactly one main() function. main is where the AVR starts executing your code when the power first goes on, so it's the entry point of the program.

int main(void) { 

The Call of the function to set microcontroller i/o ports configuration:

     init_ports_mcu();

The Call of the function to perform ADC Initialization:

     ADC_init();

Initialization random number generator:

     srand(ADC_value());

The pseudo-random number generator is initialized using the argument passed as the result of analog to digital convert.

This statement is a loop, often referred to as the main loop or event loop. This code is always true. Therefore, it executes over and over again in an infinite loop. It never ceases:

     while (1) {
           parallel_mode_leds(NUMBER_OF_REPEAT, DELAY4); // the call of the function to blink leds in parallel mode
           serial_mode_leds(NUMBER_OF_REPEAT, DELAY2); // the call of the function to blink leds in serial mode
           random_mode_leds(NUMBER_OF_REPEAT, DELAY1); // the call of the function to blink leds in random mode
      }
      return (0); //this line is never actually reached
}

Ok! Programming is complete. The final step is the building the project. It means compiling and finally linking all object files to generate the executable file. This file is generated inside the folder Debug which is inside the Project folder. This file is ready to be loaded into the microcontroller chip.

Step 4: Burning the Program Into the Memory of the Microcontroller Using AVRDUDE

First, copy the hex file of program in to AVRDUDE directory. In my case it is AVRLeds.hex.

Then, type in DOS prompt window the command:

avrdude –c [name of programmer] –p m328p –u –U flash:w:[name of your hex file]

In my case: avrdude –c ISPProgv1 –p m328p –u –U flash:w:AVRLeds.hex

The command writes hex file to the microcontroller’s memory.

Ok! Now, the microcontroller works in accordance with the instructions of our program. Let's check it out!

Step 5: Assemble the Electrical Circuit to Check the Work of Program Code

Connect components in accordance with schematic diagram.

Step 6: Conclusion

I am studying the microcontroller technics, electronic technology and electronic projects.

In this case I created simple program in C code for AVR microcontroller to control the flashing of three LEDs in serial, parallel and random flashing modes. To do this, I used three i/o ports of the microcontroller. I tried to use the analog digital convertor to read digital values as seed values for random number generator. I assembled the electrical circuit on base of the AVR ATmega328p to check the work of program code. If you want to keep up to date on my base microcontrollers projects, subscribe to my YouTube! Watching and sharing my videos is way to support what I do.

Subscribe to YouTube FOG channel

Thanks!