详细解释代码:#include <avr/io.h> #include<avr/interrupt.h> #include <util/delay.h> #define delay_ms(x) _delay_ms(x) const unsigned char disp[] = {0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00}; // 显示缓冲区,分别存放的是千位、百位、十位、个位的段码 unsigned char ledbuf[]={0x00,0x00,0x00,0x00}; // 定时器1的初始化,CTC模式,8分频,中断周期5ms unsigned char key_num=0; void disp_init(void) { OCR1A = 4999; //100Hz=8MHz/(2*8*(1+OCR1A)) TCCR1A = 0x00; TCCR1B = (1 << WGM12); //CTC模式 TCCR1B |= (1 << CS11); //8分频 TIMSK |= (1 << OCIE1A); //开比较匹配中断A } //数码管显示函数 void display(char num,char pos) { SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR1) | (1<<SPR0); PORTB &= 0x0F; //关位选 PORTB&=~(1<<0); SPDR=num; while(0==(SPSR&0X80)); PORTB|=(1<<0); PORTB |= 1<<(7-pos); } // 中断服务程序的功能:刷新段码与位控制,用变量k实现轮流刷新的目的 ISR(TIMER1_COMPA_vect) { static unsigned char k=0; //显示刷新标志 k=(++k)%4; display(ledbuf[k],k);//显示 PORTA = ledbuf[k];//传送数据位 } void io_init(void) //IO初始化 { DDRB=0xFF; PORTB=0x08; DDRC=0xFF; PORTC&=~(1<<7); //74HC595使能 DDRD=0x00; PORTD=0xFF;//PD口8个按键端口输入,上拉 } //扫描键盘,获得键码,更新显示缓冲区 void key_led(void) { unsigned char i,j; //键码记录 //按键表示的数字 key_num=key_num+1; if (key_num>9999) {key_num=0;} //更新显示缓冲区 ledbuf[0] = disp[key_num%10]; ledbuf[1] = disp[key_num%100/10]; ledbuf[2] = disp[key_num%1000/100]; ledbuf[3] = disp[key_num/1000]; } //等待按键松开 void main() { io_init(); disp_init(); sei(); while (1) { key_led(); //键盘扫描 delay_ms(100); //键盘扫描间隔 } }
时间: 2023-06-19 17:06:34 浏览: 114
这是一个基于AVR单片机的数码管显示程序。其中,代码的主要功能如下:
1. 定义了一个4位共阳数码管的段码缓冲区disp[],以及一个用于存储显示的数字的缓冲区ledbuf[]。
2. 定义了一个显示初始化函数disp_init(),用于初始化定时器1的工作模式和中断周期,以及开启比较匹配中断A。
3. 定义了一个数码管显示函数display(),用于将指定数字num显示在指定位置pos上。
4. 定义了一个键盘扫描函数key_led(),用于扫描8个按键的状态,获得键码并更新显示缓冲区。
5. 定义了一个IO初始化函数io_init(),用于初始化单片机的IO口,包括74HC595芯片的使能端口和按键输入端口。
6. 定义了一个定时器中断服务程序,在该函数中实现了轮流刷新数码管的功能。具体地,使用一个变量k记录当前需要刷新的数码管位置,然后调用display()函数将相应的数字显示在数码管上,最后将数据位传送给74HC595芯片。
7. 在主函数中,循环调用key_led()函数和delay_ms()函数实现键盘扫描和延时功能,并使用sei()函数开启中断。
相关问题
/* * final work.c * * Created: 2023/5/16 21:26:13 * Author : 86186 */ #define F_CPU 8000000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> #include "twi_lcd.h" unsigned char duty=50;//占空比,百分比 unsigned int freq=30;//30~8000Hz ISR(INT0_vect) { if(duty > 90);//百分比 duty = 10 else duty +=5; } ISR(INT1_vect) { if(freq >7999) freq = 30; else freq +=100; } int main(void) { TWI_Init(); LCD_Init(); DDRD &=~((1<<DDRD2) |(1<<DDRD3));//INT0(PD2)和INT1(PD3)分别调整占空比和频率 PORTD |=(1<<PORTD2)|(1<<PORTD3);//开启内部上拉电阻,即PD2和PD3管脚默认为高电平 DDRB |=(1<<DDRB1);//PB1控制蜂鸣器的IO MCUCR |=(1<<ISC01)|(1<<ISC11);//INT0和INT1下降沿触发中断(执行对应的ISR) GICR |=(1<<INT0)|(1<<INT1);//开中断 sei();//全局中断开 unsigned int high,low,i; while (1) { high = F_CPU/freq*duty/100;// low = F_CPU/freq - high;// if(low >1290)low-=1290; high /=12; low /=12; PORTB |=(1<<PORTB1);for(i=0;i<high;i++)_delay_us(1); PORTB &=~(1<<PORTB1);for(i=0;i<low;i++)_delay_us(1); } }解释以下这段代码
这段代码是一个基于AVR的蜂鸣器控制程序。主要功能是通过外部中断INT0和INT1来调整蜂鸣器的占空比和频率,然后控制蜂鸣器发声。
具体解释:
1. 定义了占空比和频率的变量duty和freq,初始值分别为50和30。
2. 在INT0和INT1的ISR中,通过判断当前占空比和频率的值,来对其进行加减操作,从而实现通过按键调整的功能。
3. 在主函数中,首先进行了TWI和LCD的初始化,然后设置了PD2和PD3为输入管脚,并开启了内部上拉电阻。同时设置PB1为输出管脚,用来控制蜂鸣器。
4. 通过高低电平时间的计算,控制蜂鸣器发声。具体实现是通过循环计数的方式来控制高低电平的时间长度。其中,高电平的时间长度通过占空比和频率的计算得出,低电平的时间长度则是通过频率计算得出,并减去高电平的时间长度。最后,通过控制PB1管脚的电平来控制蜂鸣器的发声。
需要注意的是,这段代码中使用了AVR的定时器来实现延时,具体使用方式是通过延时函数_delay_us()来实现微秒级别的精确延时。
#include<avr/io.h> #include<avr/interrupt.h> #include <util/delay.h> #define delay_ms(x) _delay_ms(x) const unsigned char disp[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f, 0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00}; unsigned char ledbuf[]={0x00,0x00,0x00,0x00}; unsigned int i; unsigned int sum; unsigned int y=0; int k[10]; void disp_init(void) { OCR1A = 4999; TCCR1A = 0x00; TCCR1B = (1 << WGM12); //CTC模式 TCCR1B |= (1 << CS11); //8分频 TIMSK |= (1 << OCIE1A); //开比较匹配中断A } void display(char num,char pos) { SPCR = (1<<SPE) | (1<<MSTR) | (1<<SPR1) | (1<<SPR0); PORTB &= 0x0F; //关位选 PORTB&=~(1<<0); SPDR=num; while(0==(SPSR&0X80)); PORTB|=(1<<0); PORTB |= 1<<(7-pos); } ISR(TIMER1_COMPA_vect) { static unsigned char k=0; k=(++k)%4; display(ledbuf[k],k); PORTA=ledbuf[k]; } void io_init(void) //IO初始化 { DDRB=0xFF; PORTB=0xF8; DDRC=0xFF; PORTC&=~(1<<7); //74HC595使能 DDRD=0x00; //PORTD=0xFF;//PD口8个按键端口输入,上拉 } void get(void) { //ADMUX=(0<<REFS1)|(1<<REFS0)|(1<<MUX1); ADMUX=(1<<REFS0); ADCSRA=(1<<ADEN) |(1<<ADPS0)|(1<<ADPS1)|(1<<ADPS0); ADCSRA|=(1<<ADSC); while(!(ADCSRA&(1<<ADIF))); ADCSRA|=(1<<ADIF); ADCSRA&=~(1<<ADEN); k[y]=ADC; y=y+1; if(y>=9) { for(y=0;y<=9;y++) { sum=k[y]+sum;} y=0; i=sum/9; sum=0; float v=i*5.0/1024; int a=(int)v; int b=(int)((v-a)*1000); ledbuf[0] = disp[a]|0x80; ledbuf[1] = disp[b/100]; ledbuf[2] = disp[(b%100)/10]; ledbuf[3] = disp[b%10]; } } void main() { io_init(); disp_init(); sei(); while (1) { get(); delay_ms(100); } }什么意思逐句解释
这是一个基于AVR单片机的程序,用来读取模拟电压并在数码管上显示其对应的数字。
1. `#include<avr/io.h>` 和 `#include<avr/interrupt.h>` 是AVR单片机编程所需的头文件。
2. `#include <util/delay.h>` 是AVR单片机编程中延时函数所需的头文件。
3. `#define delay_ms(x) _delay_ms(x)` 定义一个宏,用于延时x毫秒。
4. `const unsigned char disp[]={0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,0x7f, 0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71,0x00};` 定义了一个常量数组,存储了显示0~9、A~F的数码管的段码。
5. `unsigned char ledbuf[]={0x00,0x00,0x00,0x00};` 定义了一个长度为4的字符型数组,用于存储要显示的数据。
6. `unsigned int i;` 定义了一个无符号整数型变量i。
7. `unsigned int sum;` 定义了一个无符号整数型变量sum。
8. `unsigned int y=0;` 定义了一个无符号整数型变量y,并初始化为0。
9. `int k[10];` 定义了一个长度为10的整型数组k,用于存储读取到的10个模拟量数据。
10. `void disp_init(void)` 定义了一个名为disp_init的函数,用于初始化计时器1,以实现数码管的定时扫描。
11. `void display(char num,char pos)` 定义了一个名为display的函数,用于控制74HC595芯片输出数字和位选信号,实现数码管的显示。
12. `ISR(TIMER1_COMPA_vect)` 是定时器1比较匹配中断服务程序,用于定时扫描数码管并显示数码管上的数字。
13. `void io_init(void)` 定义了一个名为io_init的函数,用于初始化IO口,包括74HC595芯片和按键等。
14. `void get(void)` 定义了一个名为get的函数,用于读取模拟电压,并将其转换为数字并显示在数码管上。
15. `void main()` 是程序主函数。
16. `io_init();` 调用io_init函数,初始化IO口。
17. `disp_init();` 调用disp_init函数,初始化计时器1,以实现数码管的定时扫描。
18. `sei();` 开启全局中断。
19. `while (1) { get(); delay_ms(100); }` 无限循环,不断读取模拟电压并显示在数码管上,每次循环延时100毫秒。
阅读全文