搜索
bottom↓
回复: 9

马老师avr应用实践教程中 状态机仿真PROTEUS7.4软件问题

[复制链接]

出0入0汤圆

发表于 2009-3-8 21:15:07 | 显示全部楼层 |阅读模式
按照马潮老师 “avr单片机嵌入式系统原理与应用实践” 第一版教程中的298页搭建了一个仿真电路,
结果只能显示数码管的初始状态“————————”(8个—),矩阵按键按下没有作用。请各位大侠指点一下啊。


仿真效果图片 (原文件名:状态机仿真图.gif)

/*********************************************
File name                        : demo_9_3.c
Chip type           : ATmega16
Program type        : Application
Clock frequency     : 4.000000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 256
*********************************************/

#include <mega16.h>
flash unsigned char led_7[13]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,        // 字型码
                      0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x40};
                                                                  // 后3位为 "A","b","-"       
flash unsigned char position[8]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};

unsigned char        dis_buff[8];                                // 显示缓冲区,存放要显示的8个字符的段码值
unsigned char        key_stime_counter;
unsigned char        posit;
bit        key_stime_ok;

//volatile  unsigned char key_state, key_value, key_line;

void display(void)                                        // 8位LED数码管动态扫描函数
{
        PORTC = 0xff;
        PORTA = led_7[dis_buff[posit]];
        PORTC = position[posit];
        if (++posit >=8 ) posit = 0;
}

// Timer 0 比较匹配中断服务,2ms定时
interrupt [TIM0_COMP] void timer0_comp_isr(void)
{
        display();                                                // 调用LED扫描显示
        if (++key_stime_counter >=5)
        {
                key_stime_counter = 0;
                key_stime_ok = 1;                                // 10ms到
        }       
}

#define No_key         255
#define K1_1        1
#define K1_2        2
#define K1_3        3
#define K2_1        4
#define K2_2        5
#define K2_3        6
#define K3_1        7
#define K3_2        8
#define K3_3        9
#define K4_1        10
#define K4_2        0
#define K4_3        11
#define Key_mask        0b00000111

unsigned char read_keyboard()
{       
        static unsigned char key_state = 0, key_value, key_line;
    unsigned char key_return = No_key,i;
       
        switch (key_state)
        {
                case 0:
                        key_line = 0b00001000;
                        for (i=1; i<=4; i++)                                        // 扫描键盘
                        {       
                                PORTD = ~key_line;                                        // 输出行线电平
                                PORTD = ~key_line;                                        // 必须送2次!!!
                key_value = Key_mask & PIND;                // 读列电平
                                if (key_value == Key_mask)
                                        key_line <<= 1;                                // 没有按键,继续扫描
                                else
                                {
                                        key_state++;                                        // 有按键,停止扫描
                                        break;                                                // 转消抖确认状态
                                }
                        }
                        break;
                case 1:
                        if (key_value == (Key_mask & PIND))                // 再次读列电平,
                        {
                                switch (key_line | key_value)                // 与状态0的相同,确认按键
                                {                                                                // 键盘编码,返回编码值
                                        case 0b00001110:
                                                key_return = K1_1;
                                                break;
                                        case 0b00001101:
                                                key_return = K1_2;
                                                break;
                                        case 0b00001011:
                                                key_return = K1_3;
                                                break;
                                        case 0b00010110:
                                                key_return = K2_1;
                                                break;
                                        case 0b00010101:
                                                key_return = K2_2;
                                                break;
                                        case 0b00010011:
                                                key_return = K2_3;
                                                break;
                                        case 0b00100110:
                                                key_return = K3_1;
                                                break;
                                        case 0b00100101:
                                                key_return = K3_2;
                                                break;
                                        case 0b00100011:
                                                key_return = K3_3;
                                                break;
                                        case 0b01000110:
                                                key_return = K4_1;
                                                break;
                                        case 0b01000101:
                                                key_return = K4_2;
                                                break;
                                        case 0b01000011:
                                                key_return = K4_3;
                                                break;
                                }
                                key_state++;                                // 转入等待按键释放状态
                        }
                        else
                                key_state--;                                // 两次列电平不同返回状态0,(消抖处理)
                        break;                                               
                case 2:                                                        // 等待按键释放状态
                        PORTD = 0b00000111;                        // 行线全部输出低电平
                        PORTD = 0b00000111;                        // 重复送一次
                        if ( (Key_mask & PIND) == Key_mask)
                                key_state=0;                                // 列线全部为高电平返回状态0
                        break;
        }
        return key_return;
}

void main(void)
{
        unsigned char i, key_temp;
       
        PORTA = 0x00;                                                        // 显示控制I/O端口初始化
        DDRA = 0xFF;
        PORTC = 0xFF;
        DDRC = 0xFF;
        PORTD = 0xFF;                                                        // 键盘接口初始化
        DDRD = 0xF8;                                                        // PD2、PD1、PD0列线,输入方式,上拉有效
        // T/C0 初始化
        TCCR0=0x0B;                        // 内部时钟,64分频(4M/64=62.5KHz),CTC模式
        TCNT0=0x00;
        OCR0=0x7C;                        // OCR0 = 0x7C(124),(124+1)/62.5=2ms
        TIMSK=0x02;                        // 允许T/C0比较匹配中断

        for (i=0; i<8 ;i++)
        {dis_buff= 12;}                // LED初始显示8个"-"
        #asm("sei")                        // 开放全局中断

        while (1)
        {
                if (key_stime_ok)                               
                {
                        key_stime_ok = 0;                                // 10ms到
                        key_temp = read_keyboard();                // 调用键盘接口函数读键盘
                        if (key_temp != No_key)
                        {                                                        // 有按键按下
                                for (i=0; i<7; i++)
                                {dis_buff = dis_buff[i+1];}        // LED显示左移一位
                                dis_buff[7] = key_temp;                 // 最右显示新按下键的键值
                        }
                }
        }
}

程序文件+仿真 (cvavr)ourdev_424462.rar(文件大小:69K) (原文件名:demo_9_3.rar)

阿莫论坛20周年了!感谢大家的支持与爱护!!

一只鸟敢站在脆弱的枝条上歇脚,它依仗的不是枝条不会断,而是自己有翅膀,会飞。

出0入0汤圆

发表于 2009-3-8 22:10:40 | 显示全部楼层
我的已经仿真成功了。

出0入0汤圆

 楼主| 发表于 2009-3-8 23:06:12 | 显示全部楼层
555---忙了好几个小时,发现是版本问题啊。安装proteus7.2后,重新画个一样的电路(7.4版本的仿真图在7.2版本中不能打开,需要重新画)然后仿真,也成功了。但问题是7.2的版本不能和AVR studio联调啊?  1楼你的proteus 什么版本啊?
麻烦大侠们用proteus7.4版本仿真该程序试验看看,是不是软件7.4软件本身问题还是我的破解问题?谢谢大家

proteus7.2依次按下 1-2-3按键的仿真图,可以看到仿真达到预期效果 (原文件名:PROTEUS7.2仿真图.gif)

出0入0汤圆

发表于 2009-3-15 11:17:17 | 显示全部楼层
我也出现了同样的问题,用旧版的PROTEUS可以仿真,换了7.4 SP3后就不行了。我对比了一下,新版的MEGA16属性里多了一些东西,似乎和旧版不同了,但不知道如何设置?进行单步调试时发现,主循环执行到一定时候,就会出错,提示PC=0096没有代码(似乎是这个意思)。

出0入0汤圆

发表于 2009-3-15 19:28:36 | 显示全部楼层
终于找出问题所在,新版的MEGA16,默认WDT一直开启的,因此程序里需要加入喂狗指令,我的做法是在初始化中加入:
#asm("WDR")
#pragma optsize-       //#pragma optsize-         pragma optsize+   表示不对之间的代码进行优化
WDTCR=0x1f;          //这句可以控制WDT定时长度,
WDTCR=0x00;         // 对于新版的MEGA16,取消WDT不起作用,原因还望高手解答
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif

再在主循环适当位置加入:
#asm("WDR")//喂狗

至于如何取消新版的MEGA16的WDT,期待PROTEUS高手们解答

出0入0汤圆

发表于 2009-3-15 20:05:35 | 显示全部楼层
我不懂 AVR,但是我试了一下。
仿真时,一定要注意看 Simulation Log,那里有重要的提示信息。

本例提示信息如下:
! PC=0x007C.[AVR WATCHDOG]Watchdog setup sequence is timed out. Do nothing.
! PC=0x0290.[AVR WATCHDOG]Timer expired - processor will be reset.

出0入0汤圆

 楼主| 发表于 2009-3-18 21:04:50 | 显示全部楼层
谢谢 averzgw 和 5楼JQ_Lin 。受益非浅
当加入喂狗以后,仿真顺利进行了。如果知道在proteus中,怎样取消WDT看门狗就好了

出0入0汤圆

发表于 2009-7-25 21:31:09 | 显示全部楼层
现 在有人知道了吗?

出0入0汤圆

发表于 2009-10-20 12:01:34 | 显示全部楼层
我把程序稍作修改,用ICC AVR7.20 编译,不用喂狗,用PROTEUS7.4 SP3打开一楼的文件,一切正常。

/*********************************************
File name                        : demo_9_3.c
Chip type           : ATmega16
Program type        : Application
Clock frequency     : 4.000000 MHz
Memory model        : Small
External SRAM size  : 0
Data Stack size     : 256
*********************************************/

#include "iom16v.h"
#include <macros.h>  //ICC AVR下编译加入

__flash unsigned char led_7[13]={0x3F,0x06,0x5B,0x4F,0x66,0x6D,        // 字型码
                      0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x40};
                                                                  // 后3位为 "A","b","-"       
__flash unsigned char position[8]={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};

unsigned char        dis_buff[8];                                // 显示缓冲区,存放要显示的8个字符的段码值
unsigned char        key_stime_counter;
unsigned char        posit;
//bit        key_stime_ok;                 //ICC AVR下编译加入,修改
unsigned char key_stime_ok;              //ICC AVR下编译加入,修改
//volatile  unsigned char key_state, key_value, key_line;

void display(void)                                        // 8位LED数码管动态扫描函数
{
        PORTC = 0xff;
        PORTA = led_7[dis_buff[posit]];
        PORTC = position[posit];
        if (++posit >=8 ) posit = 0;
}

// Timer 0 比较匹配中断服务,2ms定时
//interrupt [TIM0_COMP] void timer0_comp_isr(void)     //ICC AVR下编译加入,修改
#pragma interrupt_handler timer0_comp_isr:iv_TIM0_COMP  //ICC AVR下编译加入,修改
void timer0_comp_isr(void)                              //ICC AVR下编译加入,修改
{
        display();                                                // 调用LED扫描显示
        if (++key_stime_counter >=5)
        {
                key_stime_counter = 0;
                key_stime_ok = 1;                                // 10ms到
        }       
}

#define No_key         255
#define K1_1        1
#define K1_2        2
#define K1_3        3
#define K2_1        4
#define K2_2        5
#define K2_3        6
#define K3_1        7
#define K3_2        8
#define K3_3        9
#define K4_1        10
#define K4_2        0
#define K4_3        11
#define Key_mask        0b00000111

unsigned char read_keyboard()
{       
        static unsigned char key_state = 0, key_value, key_line;
    unsigned char key_return = No_key,i;
       
        switch (key_state)
        {
                case 0:
                        key_line = 0b00001000;
                        for (i=1; i<=4; i++)                                        // 扫描键盘
                        {       
                                PORTD = ~key_line;                                        // 输出行线电平
                                PORTD = ~key_line;                                        // 必须送2次!!!
                key_value = Key_mask & PIND;                // 读列电平
                                if (key_value == Key_mask)
                                        key_line <<= 1;                                // 没有按键,继续扫描
                                else
                                {
                                        key_state++;                                        // 有按键,停止扫描
                                        break;                                                // 转消抖确认状态
                                }
                        }
                        break;
                case 1:
                        if (key_value == (Key_mask & PIND))                // 再次读列电平,
                        {
                                switch (key_line | key_value)                // 与状态0的相同,确认按键
                                {                                                                // 键盘编码,返回编码值
                                        case 0b00001110:
                                                key_return = K1_1;
                                                break;
                                        case 0b00001101:
                                                key_return = K1_2;
                                                break;
                                        case 0b00001011:
                                                key_return = K1_3;
                                                break;
                                        case 0b00010110:
                                                key_return = K2_1;
                                                break;
                                        case 0b00010101:
                                                key_return = K2_2;
                                                break;
                                        case 0b00010011:
                                                key_return = K2_3;
                                                break;
                                        case 0b00100110:
                                                key_return = K3_1;
                                                break;
                                        case 0b00100101:
                                                key_return = K3_2;
                                                break;
                                        case 0b00100011:
                                                key_return = K3_3;
                                                break;
                                        case 0b01000110:
                                                key_return = K4_1;
                                                break;
                                        case 0b01000101:
                                                key_return = K4_2;
                                                break;
                                        case 0b01000011:
                                                key_return = K4_3;
                                                break;
                                }
                                key_state++;                                // 转入等待按键释放状态
                        }
                        else
                                key_state--;                                // 两次列电平不同返回状态0,(消抖处理)
                        break;                                               
                case 2:                                                        // 等待按键释放状态
                        PORTD = 0b00000111;                        // 行线全部输出低电平
                        PORTD = 0b00000111;                        // 重复送一次
                        if ( (Key_mask & PIND) == Key_mask)
                                key_state=0;                                // 列线全部为高电平返回状态0
                        break;
        }
        return key_return;
}

void main(void)
{
        unsigned char i, key_temp;
       
        PORTA = 0x00;                                                        // 显示控制I/O端口初始化
        DDRA = 0xFF;
        PORTC = 0xFF;
        DDRC = 0xFF;
        PORTD = 0xFF;                                                        // 键盘接口初始化
        DDRD = 0xF8;                                                        // PD2、PD1、PD0列线,输入方式,上拉有效
        // T/C0 初始化
        TCCR0=0x0B;                        // 内部时钟,64分频(4M/64=62.5KHz),CTC模式
        TCNT0=0x00;
        OCR0=0x7C;                        // OCR0 = 0x7C(124),(124+1)/62.5=2ms
        TIMSK=0x02;                        // 允许T/C0比较匹配中断

        for (i=0; i<8 ;i++)
        {dis_buff= 12;}                // LED初始显示8个"-"
        //#asm("sei")                        // 开放全局中断  //ICC AVR下编译加入,修改
    SEI(); //re-enable interrupts                                      //ICC AVR下编译加入,修改
        while (1)
        {
                if (key_stime_ok)                               
                {
                        key_stime_ok = 0;                                // 10ms到
                        key_temp = read_keyboard();                // 调用键盘接口函数读键盘
                        if (key_temp != No_key)
                        {                                                        // 有按键按下
                                for (i=0; i<7; i++)
                                {dis_buff = dis_buff[i+1];}        // LED显示左移一位
                                dis_buff[7] = key_temp;                 // 最右显示新按下键的键值
                        }
                }
        }
}
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子技术论坛 ( 粤ICP备2022115958号, 版权所有:东莞阿莫电子贸易商行 创办于2004年 (公安交互式论坛备案:44190002001997 ) )

GMT+8, 2024-5-16 03:56

© Since 2004 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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