Atmega328p Introduction for Digital and PWM Output Brion L Fuller II Robotics Club
ARDUINO NANO
This is how to connect an LED to a pin on the micro controller. This one we used D6 according to the silkscreen. Which is PortD Pin 6 when programming.
The led can be wired in two different ways. Use the Pin as groundUse the Pin as 5v
Start up AVR Studios Create a new project (C++) // this is the header file that tells the compiler what pins and ports, etc // are available on this chip #include // define what pins the LEDs are connected to // in reality, PD6 is really just '6' #define LED PD // Some macros that make the code more readable #define output_low(port,pin) port &= ~(1<<pin) #define output_high(port,pin) port |= (1<<pin) #define set_input(portdir,pin) portdir &= ~(1<<pin) #define set_output(portdir,pin) portdir |= (1<<pin) // this is just a program that 'kills time' in a calibrated method void delay_ms(uint8_t ms) { uint16_t delay_count = F_CPU / 17500; volatile uint16_t i; while (ms != 0) { for (i=0; i != delay_count; i++); ms--; } } int main(void) { // initialize the direction of PORTD #6 to be an output set_output(DDRD, LED);set_output(DDRD, LED); while (1) { // turn on the LED for 200ms output_high(PORTD, LED);output_high(PORTD, LED); delay_ms(200);delay_ms(200); // now turn off the LED for another 200ms output_low(PORTD, LED);output_low(PORTD, LED); delay_ms(200);delay_ms(200); // now start over } }
Setup This part of the code is called a header file that has instructions on how to convert parts of codes to the microcontroller we are using. The file is usually found in C:\WinAVR\avr\include\avr // this is the header file that tells the compiler what pins and ports, etc // are available on this chip #include Definitions help translate code to make programs easier to read and troubleshot for people // define what pin the LED is connected to // in reality, PD6 is really just '6' #define LED PD // Some macros that make the code more readable #define output_low(port,pin) port &= ~(1<<pin) #define output_high(port,pin) port |= (1<<pin) Port &= ~(1<<pin) sets the pin to ground or zero Port |= (1<<pin) sets the pin to high or one #define set_input(portdir,pin) portdir &= ~(1<<pin) #define set_output(portdir,pin) portdir |= (1<<pin) This part is important. The microcontroller needs to know what pins are going to be receiving information and what pins will be giving information.
Important Delay // this is just a program that 'kills time' in a calibrated method void delay_ms(uint16_t ms) { uint16_t delay_count = F_CPU / 17500; volatile uint16_t i; while (ms != 0) { for (i=0; i != delay_count; i++); ms--; } } Our processor runs at 16Mhz which can be found on a datasheet. That is 16,000,000 (1/s) which is 62.5ns. This is a very important number to remember for any processor because this defines the time it takes for a line of code to finish. This defined function makes delays easier by giving a format of delay(N ms) based on the CPU frequency.
Important Delay // this is just a program that 'kills time' in a calibrated method void delay_ms(uint8_t ms) { uint16_t delay_count = F_CPU / 17500; volatile uint16_t i; while (ms != 0) { for (i=0; i != delay_count; i++); ms--; } } Our processor runs at 16Mhz which can be found on a datasheet. That is 16,000,000 (1/s) which is 62.5ns. This is a very important number to remember for any processor because this defines the time it takes for a line of code to finish. This defined function makes delays easier by giving a format of delay(N ms) based on the CPU frequency. (Only up to 255ms) // this delay is for seconds void delay_s(uint8_t s) { uint16_t delay_count = F_CPU / 17500; volatile uint16_t i; for (s; s != 0; s--){ delay_ms(250); delay_ms(250); delay_ms(250); delay_ms(250); } }
Main Program //This is the start of the program int main(void){ This lets the processor know when to start executing a program //initialize the direction of PortD #6 to output set_output(DDRD, LED); This is the setup phase for the program. Most of the time global variables and default direction lines should be place near to the main function //repeat program forever because 1 is always true while (1) { // turn on the LED for 200ms output_high(PORTD, LED);output_high(PORTD, LED); delay_ms(200);delay_ms(200); // now turn off the LED for another 200ms output_low(PORTD, LED);output_low(PORTD, LED); delay_ms(200);delay_ms(200); // now start over } This is our program finally. This is a simple blink LED for 200ms at a time.
Exercises 1.Make the LED blink twice as fast 2.Make the LED blink twice as slow 3.Change the line that sets the pin direction so that its an input, what happens? (Turn off the light and observe. This effect will be explained later...) 4.Make the LED blink REALLY fast (like, only 5ms delay) What happens? Turn off the light and wave the board around. 5.Make the LED blink out morse code for SOS (3 short blinks, a pause, 3 long blinks, a pause, 3 short blinks, a long pause...)
But what about dimming the LED? Is it too bright not bright enough? I don’t want to replace the resistor everytime I would like to change the intensity. Well that’s where PWM comes in. GREAT NOW THE LED IS ON OR OFF
Back to the pin mapping. We have modified some software to PWM in the last exercise but in all honestly that is a very processor hungry activity and what if you want to do other things while PWM. That is where some of these random functions in the ( ) are put to use. These all have special function that are connected to the pins and can perform specialized tasks to free up processor time. For PWM, we will look at the pins that have (OC_ _) this tells us that there are build in hardware timers at these pins.
#define pwm6(pwm) OCR0A = pwm Here is the setup that goes near the top of the main function // Motor Initialization routine -- this function must be called // before you use any of the above functions { //Configure for inverted PWM output on motor and clear on TCCR0A = b ; TCCR0B = b ; //set the pwm to 0% pwm6(0); } Now we can change the simple output to the LED from on or off to dimming while (1) { // turn on the LED for at 50% duty cycle pwm6(127); delay_ms(200);delay_ms(200); // now turn off the LED to 100% pwm6(255); delay_ms(200);delay_ms(200); //now turn off the LED pwm6(0); delay_ms(200);delay_ms(200); // now start over } }
00001 // this is the header file that tells the compiler what pins and ports, etc // are available on this chip #include // define what pins the LEDs are connected to // in reality, PD6 is really just '6' #define LED // Some macros that make the code more readable #define output_low(port,pin) port &= ~(1<<pin) #define output_high(port,pin) port |= (1<<pin) #define set_input(portdir,pin) portdir &= ~(1<<pin) #define set_output(portdir,pin) portdir |= (1<<pin) #define pwm6(pwm) OCR0A = pwm // this is just a program that 'kills time' in a calibrated method void delay_ms(uint8_t ms) { uint16_t delay_count = F_CPU / 17500; volatile uint16_t i; while (ms != 0) { for (i=0; i != delay_count; i++); ms--; } } // this delay is for seconds void delay_s(uint8_t s) { uint16_t delay_count = F_CPU / 17500; volatile uint16_t i; for (s; s != 0; s--){ delay_ms(250); delay_ms(250); delay_ms(250); delay_ms(250); } } int main(void) { // initialize the direction of PORTD #6 to be an output set_output(DDRD, LED);set_output(DDRD, LED); //Configure for inverted PWM output on motor and clear on TCCR0A = b ; TCCR0B = b ; //set the pwm to 0% pwm6(0); while (1) { // turn on the LED for at 50% duty cycle pwm6(127); delay_ms(200);delay_ms(200); // now turn off the LED to 100% pwm6(255); delay_ms(200);delay_ms(200); //now turn off the LED pwm6(0); delay_ms(200);delay_ms(200); // now start over } }
Exercises 1.Make the LED brighten and dim 2.Change the Pin that the LED is on 3.Replace the LED with a motor