如何编写AVR的C语言定时器程序实现LCD显示定时器及指定图示内容
Hey there! Let's build this AVR timer project with LCD display—covering both the timer info output and that custom graphic line-by-line display. I'll use the ATmega328P (super common for AVR projects) and a standard HD44780-compatible 16x2 LCD, writing pure C for AVR-GCC.
AVR Timer & LCD Display Implementation
Prerequisites
- ATmega328P (or similar 8-bit AVR)
- HD44780 16x2 LCD module
- AVR-GCC compiler & avrdude for flashing
- 16MHz crystal oscillator (or use internal 8MHz with prescaling)
- Breadboard & jumper wires
1. Hardware Connections
Let's map the pins first (you can adjust these to your setup):
- LCD RS → PD0
- LCD EN → PD1
- LCD D4 → PD2
- LCD D5 → PD3
- LCD D6 → PD4
- LCD D7 → PD5
- Timer1 uses its default pins (we'll use interrupts, no hardware output needed)
2. Code Implementation
Here's the full code with detailed comments, covering both required functions:
#include <avr/io.h> #include <avr/interrupt.h> #include <util/delay.h> #include <stdio.h> // LCD Pin Definitions #define LCD_RS PD0 #define LCD_EN PD1 #define LCD_D4 PD2 #define LCD_D5 PD3 #define LCD_D6 PD4 #define LCD_D7 PD5 // Timer Variables volatile uint8_t seconds = 0; volatile uint8_t minutes = 0; volatile uint8_t hours = 0; // Custom graphic data (matches the requested rounded square graphic, split into 8 lines) uint8_t custom_graphic[8] = { 0b00000, 0b01110, 0b10001, 0b10001, 0b11111, 0b10001, 0b10001, 0b01110 }; // LCD Helper Functions void lcd_send_command(uint8_t cmd) { // Send upper nibble PORTD = (PORTD & 0x0F) | (cmd & 0xF0); PORTD &= ~(1 << LCD_RS); PORTD |= (1 << LCD_EN); _delay_us(1); PORTD &= ~(1 << LCD_EN); _delay_us(200); // Send lower nibble PORTD = (PORTD & 0x0F) | ((cmd << 4) & 0xF0); PORTD |= (1 << LCD_EN); _delay_us(1); PORTD &= ~(1 << LCD_EN); _delay_ms(2); } void lcd_send_data(uint8_t data) { // Send upper nibble PORTD = (PORTD & 0x0F) | (data & 0xF0); PORTD |= (1 << LCD_RS); PORTD |= (1 << LCD_EN); _delay_us(1); PORTD &= ~(1 << LCD_EN); _delay_us(200); // Send lower nibble PORTD = (PORTD & 0x0F) | ((data << 4) & 0xF0); PORTD |= (1 << LCD_EN); _delay_us(1); PORTD &= ~(1 << LCD_EN); _delay_ms(2); } void lcd_init() { DDRD |= (1 << LCD_RS) | (1 << LCD_EN) | (1 << LCD_D4) | (1 << LCD_D5) | (1 << LCD_D6) | (1 << LCD_D7); _delay_ms(20); lcd_send_command(0x02); // Switch to 4-bit mode lcd_send_command(0x28); // 2 lines, 5x8 font lcd_send_command(0x0C); // Display on, cursor off lcd_send_command(0x06); // Auto-increment cursor lcd_send_command(0x01); // Clear display _delay_ms(2); } void lcd_set_cursor(uint8_t row, uint8_t col) { uint8_t address; if(row == 0) address = 0x80 + col; else address = 0xC0 + col; lcd_send_command(address); } void lcd_print_string(char *str) { while(*str != '\0') { lcd_send_data(*str); str++; } } // Load custom character into LCD CGRAM void lcd_load_custom_char(uint8_t location, uint8_t *char_data) { location &= 0x07; // Only 8 custom chars allowed (0-7) lcd_send_command(0x40 + (location * 8)); for(uint8_t i=0; i<8; i++) { lcd_send_data(char_data[i]); } } // Timer1 Initialization (CTC mode, 1 second interrupt) void timer1_init() { TCCR1B |= (1 << WGM12); // Enable CTC mode OCR1A = 15624; // For 16MHz clock, 1024 prescaler: (16e6 / 1024) - 1 = 15624 TIMSK1 |= (1 << OCIE1A); // Enable compare match interrupt TCCR1B |= (1 << CS12) | (1 << CS10); // Set 1024 prescaler sei(); // Enable global interrupts } // Timer1 Compare Match Interrupt (updates time variables) ISR(TIMER1_COMPA_vect) { seconds++; if(seconds == 60) { seconds = 0; minutes++; if(minutes == 60) { minutes = 0; hours++; if(hours == 24) hours = 0; } } } // Function to display timer info on LCD void display_timer_info() { char time_str[16]; lcd_set_cursor(0, 0); snprintf(time_str, sizeof(time_str), "Time: %02d:%02d:%02d", hours, minutes, seconds); lcd_print_string(time_str); } // Function to display custom graphic line by line void display_graphic_line_by_line() { lcd_send_command(0x01); // Clear display _delay_ms(2); // Display each line of the graphic one by one with delay for(uint8_t i=0; i<8; i++) { uint8_t temp_char[8] = {0}; temp_char[i] = custom_graphic[i]; // Only show the i-th line of the graphic lcd_load_custom_char(0, temp_char); lcd_set_cursor(0, 5); // Center the graphic on the LCD lcd_send_data(0); // Display the partial custom character _delay_ms(500); // Wait half a second between lines } // Show the full completed graphic for 2 seconds lcd_load_custom_char(0, custom_graphic); lcd_set_cursor(0, 5); lcd_send_data(0); _delay_ms(2000); } int main(void) { lcd_init(); timer1_init(); while(1) { // Toggle between timer info and graphic display every 5 seconds display_timer_info(); _delay_ms(5000); display_graphic_line_by_line(); } }
3. Key Explanations
- Timer Setup: We use Timer1 in CTC mode with a 1024 prescaler to trigger a 1-second interrupt, which updates our time variables (hours, minutes, seconds).
- LCD Custom Characters: The requested graphic is split into 8 5-bit lines (matching the LCD's 5x8 font grid). We load each line sequentially to build the graphic step-by-step.
- Display Toggle: The main loop switches between showing the timer info and the animated graphic every 5 seconds—you can adjust this delay or add a button for manual switching if needed.
4. Troubleshooting Tips
- Timer runs too fast/slow: Adjust the
OCR1Avalue based on your AVR's clock speed. For an 8MHz clock, useOCR1A = 7811with a 1024 prescaler. - LCD not displaying anything: Double-check pin connections, ensure 4-bit mode is configured correctly, and adjust the LCD contrast potentiometer.
- Graphic lines are incorrect: Tweak the
custom_graphicarray values to match the exact pixels of your target graphic.
内容的提问来源于stack exchange,提问作者Kimo
相关产品推荐
相关产品推荐

