马老师avr应用实践教程中 状态机仿真PROTEUS7.4软件问题
按照马潮老师 “avr单片机嵌入式系统原理与应用实践” 第一版教程中的298页搭建了一个仿真电路,结果只能显示数码管的初始状态“————————”(8个—),矩阵按键按下没有作用。请各位大侠指点一下啊。
http://cache.amobbs.com/bbs_upload782111/files_13/ourdev_424461.gif
仿真效果图片 (原文件名:状态机仿真图.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={0x3F,0x06,0x5B,0x4F,0x66,0x6D, // 字型码
0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x40};
// 后3位为 "A","b","-"
flash unsigned char position={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};
unsigned char dis_buff; // 显示缓冲区,存放要显示的8个字符的段码值
unsigned char key_stime_counter;
unsigned char posit;
bit key_stime_ok;
//volatileunsigned char key_state, key_value, key_line;
void display(void) // 8位LED数码管动态扫描函数
{
PORTC = 0xff;
PORTA = led_7];
PORTC = position;
if (++posit >=8 ) posit = 0;
}
// Timer 0 比较匹配中断服务,2ms定时
interrupt 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;} // LED显示左移一位
dis_buff = key_temp; // 最右显示新按下键的键值
}
}
}
}
程序文件+仿真 (cvavr)ourdev_424462.rar(文件大小:69K) (原文件名:demo_9_3.rar) 我的已经仿真成功了。 555---忙了好几个小时,发现是版本问题啊。安装proteus7.2后,重新画个一样的电路(7.4版本的仿真图在7.2版本中不能打开,需要重新画)然后仿真,也成功了。但问题是7.2的版本不能和AVR studio联调啊?1楼你的proteus 什么版本啊?
麻烦大侠们用proteus7.4版本仿真该程序试验看看,是不是软件7.4软件本身问题还是我的破解问题?谢谢大家
http://cache.amobbs.com/bbs_upload782111/files_13/ourdev_424521.gif
proteus7.2依次按下 1-2-3按键的仿真图,可以看到仿真达到预期效果 (原文件名:PROTEUS7.2仿真图.gif) 我也出现了同样的问题,用旧版的PROTEUS可以仿真,换了7.4 SP3后就不行了。我对比了一下,新版的MEGA16属性里多了一些东西,似乎和旧版不同了,但不知道如何设置?进行单步调试时发现,主循环执行到一定时候,就会出错,提示PC=0096没有代码(似乎是这个意思)。 终于找出问题所在,新版的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高手们解答 我不懂 AVR,但是我试了一下。
仿真时,一定要注意看 Simulation Log,那里有重要的提示信息。
本例提示信息如下:
! PC=0x007C.Watchdog setup sequence is timed out. Do nothing.
! PC=0x0290.Timer expired - processor will be reset. 谢谢 averzgw 和 5楼JQ_Lin 。受益非浅
当加入喂狗以后,仿真顺利进行了。如果知道在proteus中,怎样取消WDT看门狗就好了 现 在有人知道了吗? 我把程序稍作修改,用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={0x3F,0x06,0x5B,0x4F,0x66,0x6D, // 字型码
0x7D,0x07,0x7F,0x6F,0x77,0x7C,0x40};
// 后3位为 "A","b","-"
__flash unsigned char position={0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};
unsigned char dis_buff; // 显示缓冲区,存放要显示的8个字符的段码值
unsigned char key_stime_counter;
unsigned char posit;
//bit key_stime_ok; //ICC AVR下编译加入,修改
unsigned char key_stime_ok; //ICC AVR下编译加入,修改
//volatileunsigned char key_state, key_value, key_line;
void display(void) // 8位LED数码管动态扫描函数
{
PORTC = 0xff;
PORTA = led_7];
PORTC = position;
if (++posit >=8 ) posit = 0;
}
// Timer 0 比较匹配中断服务,2ms定时
//interrupt 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;} // LED显示左移一位
dis_buff = key_temp; // 最右显示新按下键的键值
}
}
}
}
页:
[1]