AVR microcontroller tutorial --PWM dimming

PWM

Drive two way digital control of dynamic scanning, each have only 50% of the time is bright, we call this the value of its duty cycle. Let pin high lighting LED, the duty ratio is 100%.

When digital drive, we forced to make 50% duty cycle, because you can not really let the two simultaneously display different numbers. However, we can also deliberately let the LED duty cycle of less than 100% in order to reduce the brightness.

Duty cycle can be adjusted in the program. The following procedure allows the user to adjust the duty cycle of the blue LED with the key, and display through digital control.

#include <ee1/ee.h>

#define DUTY_MAX 9

int main()
{
    led_init();
    button_init(PIN_NULL, PIN_NULL);
    segment_init(PIN_NULL, PIN_8);
    uint8_t duty = 0;
    while (1)
    {
        if (button_pressed(BUTTON_0) && duty > 0)
            --duty;
        if (button_pressed(BUTTON_1) && duty < DUTY_MAX)
            ++duty;
        segment_dec(duty);
        segment_display(SEGMENT_DIGIT_R);
        for (uint8_t i = 0; i != DUTY_MAX; ++i)
        {
            if (i < duty)
                led_set(LED_BLUE, true);
            else
                led_set(LED_BLUE, false);
            delay(1);
        }
    }
}

dutyIs an integer in the range 0to 9, respectively, the duty ratio of the LED 0/9to 9/9. For example, when the duty ratio is 4/9, the cycle of 9 msec, the first LED light 4 ms, 5 ms after the LED is not illuminated.

Can be seen, the greater the duty cycle, LED brightness is higher. Originally, between light and dark, there is an intermediate state of the LED. We are not by having a pin output voltage between 0V and 5V, but to quickly pin level varies between high and low to achieve.

This effect is achieved by the analog electrical fast hopping flat technique called pulse width modulation, referred to as PWM.

Timer

Timer most microcontroller can output PWM wave, peripheral-rich AVR microcontroller is no exception. Last lecture mentioned Timer 0 has four operating modes, the latter two is fast PWM phase correct PWM mode.

In fast PWM mode, TCNT0the operation of the normal register mode, but may also be the OCR0Aupper limit. For non-inverted output, TCNT0reaches the upper limit and is cleared, the output pin is high; and when TCNT0with OCR0Aor OCR0Bmatch, OC0Aand OC0Boutputs a low level, respectively; for inverted output, the former is low, which is high. Usually a non-inverted PWM output frequency is \ (F_ {} the CPU / 256N \) (in the case of the upper limit is 255; \ (N \) is the division factor), the duty ratio is \ ((OCR0x + 1) / 256 \) . Since the duty cycle is a denominator 256 8 th power of 2, the 8-bit PWM output resolution.

Mainly used for phase correction PWM motor control PWM wave shape of more stringent requirements of the case, it does not go into detail here. Timer 1 have more work mode, more abundant timer clock system 2, you can take a closer look at the data sheet.

Storehouse

Duty cycle formula ((OCR0x + 1) / 256 \) \ in OCR0xcan take 0the 255value, the duty cycle can reach 1, LED brightness up to the maximum PWM mode; 0 duty cycle can not be achieved, so with LED PWM control is not all dark. This is a bit of trouble, you must turn off the LED PWM to dim, not just to OCR0xthe value of a write. For future ease of use, we use the function to register operations wrap up (the entire library doing it).

In the Atmel Studio, the static libraries and executables belong to the project, can exist side by side in solution in. In the solution of the above software PWM program belongs, click on the menu bar File-> New-> Project (or Ctrl + Shift + N), select the "GCC C Static Library Project", named "pwm", in the "Solution:" select "Add to solution", select the type of MCU after "OK", a static library project has been created, with a default library.cfile.

In the "Solution Explorer", the library.crename oc0a.c. Select the "pwm" project, right -> Add-> New Item or menu bar -> Project-> Add New Item or Ctrl + Shift + A, select "Include File", named oc0a.h(usually take the same name, but not required of).

The library needs to provide two functions: oc0a_initfor OC0Apins as PWM output, oc0a_pwmset the output PWM duty cycle parameter is an unsigned 8-bit integer.

// oc0a.h

#ifndef OC0A_H
#define OC0A_H

#include <stdint.h>

/*
 * 函数:oc0a_init
 * 参数:无
 * 返回:void
 * 功能:将OC0A引脚配置为PWM输出,占空比为0。
 */
void oc0a_init();

/*
 * 函数:oc0a_pwm
 * 参数:uint8_t _duty - 占空比的整数表示
 * 返回:void
 * 功能:将OC0A引脚输出PWM波的占空比设置为(_duty / 256)。
 */
void oc0a_pwm(uint8_t _duty);

#endif

In the header file oc0a.h, we define the two functions, and provide a description as comments, including parameters, return values and functions.

Then, oc0a.cwe provide implementations of these functions.

#include "oc0a.h"
#include <avr/io.h>

void oc0a_init()
{
    PORTB &=   ~(1 << PORTB3); // PB3 low level
    DDRB  |=     1 << DDB3;    // PB3 output mode
    TCCR0A =  0b00 << COM0A0   // normal port operation
           |  0b11 << WGM00;   // fast PWM mode
    TCCR0B =   0b0 << WGM02    // fast PWM mode
           | 0b010 << CS00;    // 8 prescale
}

void oc0a_pwm(uint8_t _duty)
{
#define COMA_MASK (~(0b11 << COM0A0)) // mask for COMnA bits
    if (_duty)                        // fast PWM mode
        TCCR0A = (TCCR0A & COMA_MASK) // protect other bits
               | 0b10 << COM0A0,      // non-inverting mode
        OCR0A  = _duty - 1;           // duty = (OCRnx + 1) / 256
    else                              // turn PWM off
        PORTB &= ~(1 << PORTB3);      // PB3 low level
        TCCR0A = (TCCR0A & COMA_MASK) // protect other bits
               | 0b00 << COM0A0;      // normal port operation
}

First implementation file should contain the corresponding header files, to ensure consistent function interface.

Underlying operating as a package, which involves a lot of register functions. Operation of the register is not written directly to assign a number, but the combination of a variety of bit operation, which is unique to microcontroller programming. For example, PORTB3the macro is defined <avr/io.h>, the value of 3the significance of PORTBthe first 3bit (least significant bit for the first 0bit) control PB3pin; 1 << PORTB3generating this bit is a 1remaining bit 0number; take it ~, only this one is obtained 0, the remaining bits 1of the number; so that PORTBwith the number of &=operations can be kept unchanged while the other bits of this one becomes 0, because the 0result of an "aND" is 0rather 1the result of an "aND" is that bit value. Another example COM0A0is 6, 0b00 << COM0A0the COM0A1:0two fill 00, empathy 0b11 << WGM00to WGM1:0fill 11, the number two |operator, put TCCR0Ain these two simultaneously filled out (refer to Datasheet View bit definition).

Also, write a variety of reasons: for PORTBsuch as register, the function is only responsible for one of them, and the assignment will affect the other bits; for OCR0Asuch as register, the code expressly written every name and value can be enhanced readability.

If the library is open source, the comment was addressed to want to understand the user to see; if it is closed source libraries, header files and distributed in the form of library files, comments are later wrote to his look; in short, the need for comments. The purpose is to eliminate doubts reader comments (including myself) are. Readers do not know 0b010 << CS00the meaning, marked "divide by 8", which is written in the data sheet; readers do not understand why OCR0Athe assignment is required -1, put the duty cycle of the formula is entered, including +1.

Also need to be reminded that some of the lack of portability of the code above, because the 0bbinary number prefix is the GCC extension, not part of the standard C language. Most close to the standard binary representation is hexadecimal, but the need to manually convert (in 0b0000the 0b1111and 0x0to 0xFestablish a mapping between, like painted the answer sheet when the F-AB to as K-BD), which is assigned to register expand the reason for writing.

Breathing light

To test this library, we'll create a new project, this time selecting "GCC C Executable Project", the process presumably after you've done many times before. The difference is that references the header file written a little change, before writing oc0a.hlocated in ../pwm/the directory, ../meaning the parent directory; add the library as well as the need to manually in the "Solution Explorer" on the project "Libraries" right-click "Add Library "in the" "check a page" pwm "project project Libraries; so you can use the two functions just written.

Our results achieved breathing light, i.e., light from the LED gradually becomes dark, then dimmer, like breathing.

#include <ee1/delay.h>
#include "../pwm/oc0a.h"

int main()
{
    oc0a_init();
    int brightness = 0, fadeAmount = 5;
    while (1)
    {
        oc0a_pwm(brightness);
        brightness = brightness + fadeAmount;
        if (brightness <= 0 || brightness >= 255)
            fadeAmount = -fadeAmount;
        delay(30);
    }
}

The OC0Apin is connected to the left in any one of RGBW development board, and you will see the corresponding LED has the effect of breathing light.

RGBW

RGBW representative of red, green, blue and white. Theoretically, green and blue can be a combination of all colors, i.e., the addition of white provide pure white light, it can also enhance the brightness of the LED.

If you light observed under the above procedure results in an indoor, you would find that although variable brightness, called brightness is varies linearly with time, but the visual effect, the entire lit process, clearly fast the first half of the brightness change, behind almost constant brightness. And if you go with a flashlight shining and then observe it, you can feel the change in brightness of the second half. This is probably because the human eye is not sensitive to changes in light under low light conditions.

rgbw_setFunction solves this problem. It is not directly forwarded to the parameters pwm_set, but called with parameters mapped; this mapping as a function of mathematical, in xsmaller when yslower growth, large rapid growth, offsetting the illusion of the human eye.

// oc0a.c

#include <ee1/delay.h>
#include <ee1/rgbw.h>

void init();
void breathe();
void flash();

int main()
{
    init();
    while (1)
        breathe(), flash();
}

void init()
{
    rgbw_init(PIN_4, PIN_5, PIN_6, PIN_7);
}

void breathe_phase(uint8_t* _status, int8_t* _alter)
{
    for (uint8_t step = 0; step != 200; ++step)
    {
        for (uint8_t which = 0; which != 4; ++which)
            rgbw_set(which, _status[which] += _alter[which]);
        delay(5);
    }
}

void breathe()
{
    uint8_t status[4] = {0, 0, 0, 0};
    int8_t pre[4] = {1, 0, 0, 0};
    int8_t loop[][4] =
    {
        {-1, 1, 0, 0},
        {0, -1, 1, 0},
        {1, 0, -1, 0},
    };
    int8_t post[4] = {-1, 0, 0, 0};
    breathe_phase(status, pre);
    for (uint8_t cnt = 2; cnt--;)
        for (uint8_t pha = 0; pha != sizeof(loop) / sizeof(*loop); ++pha)
            breathe_phase(status, loop[pha]);
    breathe_phase(status, post);
}

void flash_phase(bool* _pattern)
{
    for (uint8_t which = 0; which != 4; ++which)
        rgbw_set(which, _pattern[which] ? 200 : 0);
    delay(500);
}

void flash()
{
    bool extra[4] = {0, 0, 0, 0};
    bool loop[][4] =
    {
        {1, 0, 0, 0},
        {1, 1, 0, 0},
        {0, 1, 0, 0},
        {0, 1, 1, 0},
        {0, 0, 1, 0},
        {1, 0, 1, 0},
    };
    flash_phase(extra);
    for (uint8_t cnt = 2; cnt--;)
        for (uint8_t pha = 0; pha != sizeof(loop) / sizeof(*loop); ++pha)
            flash_phase(loop[pha]);
    flash_phase(extra);
}

This code changes the lamp mode is represented by numbers rather than constant parameter function call to hard-code program is easy to modify and expand.

operation

  1. The data sheet, realized in OC1Athe output of 12-bit resolution, with the phase and frequency correction wave PWM pin. Note duty cycles of 0's and 1's.

  2. Light play it!

Guess you like

Origin www.cnblogs.com/jerry-fuyi/p/12164485.html