PWM
The driving mode of the two-digit digital tube is dynamic scanning, and each bit is only lit for 50% of the time. We call this value its duty cycle. Let the pin output high level to light up the LED, and the duty cycle is 100%.
When driving the digital tube, we have to make the duty cycle 50% because we cannot let the two digits display different numbers at the same time. However, we can also intentionally make the duty cycle of the LED less than 100% to reduce its brightness.
The duty cycle can be adjusted by program. The following program allows the user to adjust the duty cycle of the blue LED by pressing buttons and display it through the digital tube.
#include #define DUTY_MAX 9 int main() { led_heat(); 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); } } } duty is an integer ranging from 0 to 9, which means the duty cycle of the LED is 0/9 to 9/9. For example, when the duty cycle is 4/9, in a 9 millisecond cycle, the LED is on for the first 4 milliseconds and off for the last 5 milliseconds. It can be seen that the larger the duty cycle, the higher the LED brightness. It turns out that the LED has an intermediate state between bright and dark. We do this not by letting the pin output a voltage between 0V and 5V, but by letting the pin level change rapidly between high and low. This technology, which achieves analog effects through rapid level changes, is called pulse width modulation, or PWM for short. Timer Most MCU timers can output PWM waves, and the AVR MCU with rich peripherals is no exception. In the previous lecture, we mentioned that Timer 0 has four working modes, the last two of which are fast PWM mode and phase correction PWM mode. In fast PWM mode, the action of TCNT0 register is the same as that in normal mode, but OCR0A can also be used as the upper limit. For non-inverting output, after TCNT0 reaches the upper limit and is cleared, the pin will output a high level; when TCNT0 matches OCR0A or OCR0B, OC0A and OC0B will output a low level respectively; for inverting output, the former is low and the latter is high. Generally, non-inverting is used, and the frequency of the output PWM wave is fCPU/256N (for the case where the upper limit is 255; N is the frequency division coefficient), and the duty cycle is (OCR0x+1)/256. Since the denominator of the duty ratio 256 is 2 to the 8th power, this PWM output has an 8-bit resolution. Phase correction PWM is mainly used in motor control and other occasions where the shape of the PWM wave is strictly required. I will not go into details here. Timer 1 has more working modes, and Timer 2 has a richer clock system. You can find out more in the data sheet. Library In the duty cycle formula (OCR0x+1)/256, OCR0x can take values from 0 to 255, so the duty cycle can reach 1, and the LED can reach maximum brightness in PWM mode; the duty cycle cannot reach 0, so the LED controlled by PWM cannot be completely dark. This is a bit troublesome, and the PWM must be turned off to dim the LED, not just write a value to OCR0x. For future convenience, we use functions to wrap register operations (the entire library is doing this). In Atmel Studio, static libraries and executable programs both belong to projects and can exist side by side in a solution. In the solution to which the PWM software program belongs, click the menu bar File->New->Project (or Ctrl+Shift+N), select "GCC C Static Library Project", name it "pwm", select "Add to solution" in "Solution:", select "OK" and select the MCU model, and the static library project will be created, with a library.c file by default. In "Solution Explorer", rename library.c to oc0a.c. Select the "pwm" project, right-click -> Add -> New Item or menu bar -> Project -> Add New Item or Ctrl+Shift+A, select "Include File", and name it oc0a.h (usually the same name is used, but it is not necessary). This library needs to provide two functions: oc0a_init is used to configure the OC0A pin as PWM output, and oc0a_pwm sets the output PWM duty cycle. The parameter is an unsigned 8-bit integer. // oc0a.h #ifndef OC0A_H #define OC0A_H #include /* * Function: oc0a_init * Parameters: None * Return: void * Function: Configure OC0A pin as PWM output with a duty cycle of 0. */ void oc0a_init(); /* * Function: oc0a_pwm * Parameter: uint8_t _duty - integer representation of duty cycle * Return: void * Function: Set the duty cycle of the PWM wave output by the OC0A pin to (_duty / 256). */ void oc0a_pwm(uint8_t _duty); #endif In the header file oc0a.h, we define these two functions and provide descriptions in the form of comments, including parameters, return values, and functions. Then, provide the implementation of these functions in oc0a.c. // oc0a.c #include "oc0a.h" #include 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; // divide by 8 } 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 } The implementation file should first include the corresponding header file to ensure consistent function interfaces. As the encapsulation of the underlying operation, these functions involve many registers. The operation of the register is not written as a direct assignment of a number, but is a combination of multiple bit operations, which is unique to single-chip microcomputer programming. For example, the PORTB3 macro is defined in Moreover, there are many reasons for writing like this: for registers such as PORTB, the function is only responsible for one bit, and the assignment statement will affect other bits; for registers such as OCR0A, clearly writing the name and value of each bit in the code can enhance readability. If it is an open source library, the comments are for users who want to learn more about it; if it is a closed source library, published in the form of header files and library files, the comments are for future generations to read; in short, comments are needed. The purpose of comments is to eliminate the doubts of readers (including yourself). If the reader does not know the meaning of 0b010 << CS00, then "8 division" is noted, which is written in the data sheet; if the reader does not understand why -1 is required in the assignment statement of OCR0A, then the duty cycle formula is put there, which contains +1. It is also worth noting that the above code is not very portable, because binary numbers with the 0b prefix are a GCC extension and are not part of the C language standard. The closest standard representation to binary is hexadecimal, but this requires manual conversion (creating a mapping between 0b0000 to 0b1111 and 0x0 to 0xF, just like F-AB to K-BD on a test paper), which is why the register assignments are written in expanded form. Breathing light To test this library, we create another new project, this time select "GCC C Executable Project". You must have done the following process many times. The difference is that the way to write the reference header file is a little different. The oc0a.h written before is located in the ../pwm/ directory, and ../ means the parent directory; and you need to manually add this library. In the "Solution Explorer", right-click on the "Libraries" of the project, click "Add Library", and check the "pwm" project in the "Project Libraries" page; now you can use the two functions you just wrote. Let's achieve the effect of a breathing light, that is, the LED slowly changes from dark to bright and then dark again, like breathing. #include #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); } } Connect the OC0A pin to any one of the RGBW on the left side of the development board, and you will see that the corresponding LED has a breathing light effect.
Previous article:AVR MCU Tutorial - LCD1602
Next article:Design of intelligent tracking vehicle model system based on ATMEGAl6 and graded steering module
Recommended ReadingLatest update time:2024-11-24 18:55
- Popular Resources
- Popular amplifiers
- Wireless Sensor Network Technology and Applications (Edited by Mou Si, Yin Hong, and Su Xing)
- Modern Electronic Technology Training Course (Edited by Yao Youfeng)
- Modern arc welding power supply and its control
- Small AC Servo Motor Control Circuit Design (by Masaru Ishijima; translated by Xue Liang and Zhu Jianjun, by Masaru Ishijima, Xue Liang, and Zhu Jianjun)
- Naxin Micro and Xinxian jointly launched the NS800RT series of real-time control MCUs
- How to learn embedded systems based on ARM platform
- Summary of jffs2_scan_eraseblock issues
- Application of SPCOMM Control in Serial Communication of Delphi7.0
- Using TComm component to realize serial communication in Delphi environment
- Bar chart code for embedded development practices
- Embedded Development Learning (10)
- Embedded Development Learning (8)
- Embedded Development Learning (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- Intel promotes AI with multi-dimensional efforts in technology, application, and ecology
- ChinaJoy Qualcomm Snapdragon Theme Pavilion takes you to experience the new changes in digital entertainment in the 5G era
- Infineon's latest generation IGBT technology platform enables precise control of speed and position
- Two test methods for LED lighting life
- Don't Let Lightning Induced Surges Scare You
- Application of brushless motor controller ML4425/4426
- Easy identification of LED power supply quality
- World's first integrated photovoltaic solar system completed in Israel
- Sliding window mean filter for avr microcontroller AD conversion
- What does call mean in the detailed explanation of ABB robot programming instructions?
- STMicroelectronics discloses its 2027-2028 financial model and path to achieve its 2030 goals
- 2024 China Automotive Charging and Battery Swapping Ecosystem Conference held in Taiyuan
- State-owned enterprises team up to invest in solid-state battery giant
- The evolution of electronic and electrical architecture is accelerating
- The first! National Automotive Chip Quality Inspection Center established
- BYD releases self-developed automotive chip using 4nm process, with a running score of up to 1.15 million
- GEODNET launches GEO-PULSE, a car GPS navigation device
- Should Chinese car companies develop their own high-computing chips?
- Infineon and Siemens combine embedded automotive software platform with microcontrollers to provide the necessary functions for next-generation SDVs
- Continental launches invisible biometric sensor display to monitor passengers' vital signs
- Design of electronic piano based on LPC2000 series ARM chip
- LORA is used in wireless smoke detection system
- TOP851 universal programmer
- Comparative Analysis of UPS Inverter Control Methods
- Principle and practical technology of tms320c2000 series dsp
- TPS61322 6μA Quiescent Current 1.8A Switching Current Boost Converter
- How do analog switches ensure bandwidth without distortion?
- A brief discussion on the technological innovation and market prospects of Wi-Fi 6E
- How to use a multimeter from entry level to mastery
- Looking for lmk04826B5 routine