找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 1479|回复: 9
收起左侧

在尝试制作avr16单片机带闹铃功能的时钟遇到一些问题,请求指教

[复制链接]
ID:962686 发表于 2021-9-2 18:36 | 显示全部楼层 |阅读模式
我打算设置闹钟响10s(count2==1000),但是闹钟会响11s.请问是什么原因。新手单片机入门,结果处处碰壁。单片机好难

//时钟频率8M

#include<iom16v.h>
#include"lcd1602.h"

#define key_state0 0
#define key_state1 1
#define key_state2 2
#define key_off 0
#define key1 1
#define key2 2
#define key3 3
#define time_display 0
#define clock_set 1
#define alarm_set 2
#define set_h 0
#define set_m 1
#define set_s 2

void main(void);
void io_init(void);
void time_init(void);
void cpu_init(void);
void time_10ms(void);
char key_scan(void);
void Pattern(void);//模式
void Alarm(void);//闹钟响
void Set(void);//设置状态
void time(void);

const char num[] = {"0123456789"};
char cs = 0,cm = 0,ch = 0;//实时时钟
char as = 5,am = 0,ah = 0;//闹钟
char ss,sm,sh;
char time_10ms_ok;//10ms按键扫描标志
char time_10s_ok;//10s闹铃标志
char key_sign;//存储按键状态
char set = set_h;
uint count1 = 0, count2 = 0;

void io_init(void)
{
         DDRA |= 1<<7;
        PORTA |= 1<<7;
         DDRB = 0X00;
        PORTB = 0XFF;
        DDRC = 0XFF;
        PORTC = 0XFF;
        DDRD |= (1<<7)|(1<<6)|(1<<5);
        PORTD |= (1<<7)|(1<<6)|(1<<5);
}

void time_init(void)
{
         TCCR1A = 0X00;
         TCCR1B = (1<<WGM12)|(3<<CS10);//CTC,64分频
        OCR1AH = 1250>>8;
        OCR1AL = 1250;
        TCNT1 = 0;
         TIMSK = 1<<OCIE1A;
         SREG = 1<<7;
}

void cpu_init(void)
{
         io_init();
        time_init();
        lcd_init();
}

#pragma interrupt_handler time_10ms:7
void time_10ms(void)
{
        count1++;
        count2++;
        time_10ms_ok = 1;//10ms按键扫描标志                                 
}

char key_scan(void)
{
         static char key_press, key_state = 0;
        char key_return = key_off;
        key_press = PINB;
        switch(key_state)
        {
                 case key_state0:
                {
                         if(key_press != 0XFF)//若有按键按下,则进入确认状态
                        {
                                 key_state = key_state1;
                        }
                        break;
                }
                case key_state1:
                {
                         if(key_press != 0XFF)//确认有按键按下,开始识别按键按下位置
                        {
                                 if(!(key_press & (1<<PB0))) key_return = key1;//按键1
                                else if(!(key_press & (1<<PB1))) key_return = key2;//按键2
                                else if(!(key_press & (1<<PB2))) key_return = key3;//按键3
                                 key_state = key_state2;
                        }
                        else key_state = key_state0;//若按键抬起则回到初始状态
                        break;
                }
                case key_state2:
                {
                         if(key_press == 0XFF)//若按键抬起则回到初始状态
                        {
                                 key_state = key_state0;
                        }
                        break;
                }
                default: key_state = key_state0; break;
        }
        return key_return;
}

void Pattern(void)//模式函数
{
         static char pattern = time_display;
         switch(pattern)
        {
                 case time_display://模式1,实时时钟显示
                {
                         if(time_10ms_ok)
                        {
                                 time_10ms_ok = 0;
                                key_sign = key_scan();
                                if(key_sign == key1)
                                {
                                         pattern = clock_set;//时钟设置
                                        set = set_h;//回到小时位调整
                                        ss = cs;
                                        sm = cm;
                                        sh = ch;
                                }
                                else time();
                        }
                        break;
                }
                case clock_set://模式2,时钟设置
                {
                         if(time_10ms_ok)
                        {
                                 time_10ms_ok = 0;
                                key_sign = key_scan();
                                if(key_sign == key1)
                                {
                                         pattern = alarm_set;//闹钟设置
                                        set = set_h;//回到小时位调整
                                        cs = ss;
                                        cm = sm;
                                        ch = sh;
                                        ss = as;
                                        sm = am;
                                        sh = ah;
                                        TCNT1 = 0;//计数器清零
                                }
                                else Set();
                        }
                        break;
                }
                case alarm_set://模式3,闹钟设置
                {
                         if(time_10ms_ok)
                        {
                                 time_10ms_ok = 0;
                                key_sign = key_scan();
                                if(key_sign == key1)
                                {
                                         pattern = time_display;
                                        as = ss;
                                        am = sm;
                                        ah = sh;
                                }
                                else Set();
                        }
                        break;
                }
                default: pattern = time_display; break;
        }
}

void Set(void)
{
        
        lcd_write_char(4,0,num[sh/10]);
        lcd_write_char(5,0,num[sh%10]);
        lcd_write_char(7,0,num[sm/10]);
        lcd_write_char(8,0,num[sm%10]);
        lcd_write_char(10,0,num[ss/10]);
        lcd_write_char(11,0,num[ss%10]);
        
         switch(set)
        {
                 case set_h://调整小时位
                {                        
                        if(key_sign == key2)
                        {
                                sh = (++sh)%24;
                        }
                        else if(key_sign == key3) set = set_m;
                        break;
                }
                case set_m://调整分钟位
                {
                        if(key_sign == key2)
                        {
                                sm = (++sm)%60;
                        }
                        else if(key_sign == key3) set = set_s;
                        break;
                }
                case set_s://调整秒位
                {
                        if(key_sign == key2)
                        {
                                ss = (++ss)%60;
                        }
                        else if(key_sign == key3) set = set_h;
                        break;
                }
                 default: set = set_h; break;
        }
}

void time(void)
{
        lcd_write_char(4,0,num[ch/10]);
        lcd_write_char(5,0,num[ch%10]);
        lcd_write_char(7,0,num[cm/10]);
        lcd_write_char(8,0,num[cm%10]);
        lcd_write_char(10,0,num[cs/10]);
        lcd_write_char(11,0,num[cs%10]);
}

void Alarm(void)
{
         if(count2 == 1000) time_10s_ok = 1;
         if((ch == ah) && (cm == am) && (cs == as))
        {
                PORTA &= ~(1<<PA7);
                count2 =0;
                time_10s_ok = 0;
        }
        else if(time_10s_ok) PORTA |= (1<<PA7);
}

void time_send(void)
{
         if((cs == 0)&&(cm == 0)) ;
}

void main(void)//主函数
{
         cpu_init();
        time_display_init();
        while(1)
        {
                 if(count1 == 100)//实时时间计时
                {
                          cs++;
                        count1 = 0;
                        if(cs == 60)
                        {
                                 cm++;
                                cs = 0;
                                if(cm == 60)
                                {
                                          ch++;
                                         cm = 0;
                                        if(ch == 24) ch = 0;
                                }
                        }
                }
                 Pattern();
                Alarm();
        }
}
回复

使用道具 举报

ID:962686 发表于 2021-9-2 19:45 | 显示全部楼层
我测试了一下,发现我的定时器不精准,但是我用8M的外部时钟频率,按道理我用64预分频那么通过计算:8000000/64=125000—>那么我用ctc比较匹配的话。把计数上限OCR1A设置为1250不就是10ms了吗???但是实时为什么会慢那么多
回复

使用道具 举报

ID:624769 发表于 2021-9-2 20:26 | 显示全部楼层
AVR没用过不清楚,但是,预分频能影响到定时器么?很多单片机,定时器有专门的寄存器来选择到底用主频预分频,还是主频,还是定时器的专用分频器,你最好先确认一下这个。
回复

使用道具 举报

ID:962686 发表于 2021-9-2 20:35 | 显示全部楼层
188610329 发表于 2021-9-2 20:26
AVR没用过不清楚,但是,预分频能影响到定时器么?很多单片机,定时器有专门的寄存器来选择到底用主频预分 ...

我定1s误差很小,但是到10ms就很大了。我不知道是什么原因,小白菜一个请求大佬指教一下
回复

使用道具 举报

ID:962686 发表于 2021-9-2 21:09 | 显示全部楼层
wzl570 发表于 2021-9-2 19:45
我测试了一下,发现我的定时器不精准,但是我用8M的外部时钟频率,按道理我用64预分频那么通过计算:800000 ...

我设置1s(count1 == 100)误差不大,但是10s(count2==1000)就是11秒了
回复

使用道具 举报

ID:419968 发表于 2021-9-2 22:30 | 显示全部楼层
设置成8分频 8000000/64=1000000 OCR1A=9999
如64分频 OCR1a=1249 误差大
回复

使用道具 举报

ID:624769 发表于 2021-9-3 00:34 | 显示全部楼层
wzl570 发表于 2021-9-2 21:09
我设置1s(count1 == 100)误差不大,但是10s(count2==1000)就是11秒了

做两个尝试,
1,不预分频,看看误差大不大。
2,既然你都测试10秒了,索性干脆点, 测试1分钟(6000),5分钟(30000),看看误差到底是等比,还是等差。等比,就是定时器或者说时钟有问题,等差,就是你程序或者中断本身有问题。到时候再分析。
回复

使用道具 举报

ID:962686 发表于 2021-9-3 11:09 | 显示全部楼层
adject 发表于 2021-9-2 22:30
设置成8分频 8000000/64=1000000 OCR1A=9999
如64分频 OCR1a=1249 误差大

请问一下误差是硬件问题吗
回复

使用道具 举报

ID:962686 发表于 2021-9-3 11:11 | 显示全部楼层
188610329 发表于 2021-9-3 00:34
做两个尝试,
1,不预分频,看看误差大不大。
2,既然你都测试10秒了,索性干脆点, 测试1分钟(6000) ...

我只知道他有一点误差,但是具体多少我不知道怎么测试。请问我该如何得到精准一点的误差
回复

使用道具 举报

ID:962686 发表于 2021-9-4 13:29 | 显示全部楼层
求大佬指点啊
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表