搜索
bottom↓
回复: 46

请高手讲解马潮老师书中的这个程序的while循环

[复制链接]

出0入0汤圆

发表于 2008-9-12 18:43:00 | 显示全部楼层 |阅读模式
/*********************************************
File name           : demo_6_7.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>
#include <delay.h>

flash unsigned char char_7[8]={0x10,0x38,0x7C,0xFE,0x38,0x38,0x38,0x38};

void display(unsigned char row)
{
        unsigned char i;
        for (i=0;i<=7;i++)
        {
                if (row <= 7)
                        PORTA = char_7[row];
                else
                        PORTA = 0;
                PORTC = ~(1<<i);
                delay_ms(2);
                PORTC = 0xFF;
                if (++row >= 12 ) row = 0;                
        }
}

void main(void)
{
        unsigned char time_counter,i = 0;
        PORTA=0x00;
        DDRA=0xFF;
        PORTC=0xFF;
        DDRC=0xFF;

        while (1)
        {
                display(i);
                delay_ms(9);
                if (++time_counter >= 4)
                {
                        time_counter = 0;
                        if(++i >= 12) i=0;
                }
        };
}



怎么也看不懂while循环中的意思,这个程序和led动态扫描类似

出0入0汤圆

 楼主| 发表于 2008-9-13 09:05:57 | 显示全部楼层
怎么也没有人帮忙解决一下啊,实在是查不到资料了

出0入0汤圆

发表于 2008-9-13 09:17:03 | 显示全部楼层
马老师的编程风格很好

while (1)
        {
                display(i);                            -------》显示 i
                delay_ms(9);                           -------》延迟9ms
                if (++time_counter >= 4)               -------》判断时标
                {
                        time_counter = 0;         
                        if(++i >= 12) i=0;            --------》判断i+1是否 超过12 注意是先加1再判断
                }
        };

出0入0汤圆

发表于 2008-9-13 09:50:37 | 显示全部楼层
意思是每次进入display()时只显示其中的一位就马上退出.
这个说白了就是状态机啦,只不过它的状态指示是用参数传入的.如果把参数改全局变量,并在display()函数中自行修改增量,就是真正的基于状态机的子程序了.

出0入0汤圆

 楼主| 发表于 2008-9-13 16:56:00 | 显示全部楼层
2楼说的时标是什么啊?我是刚接触单片机不太了解,time_counter好像没有初始化呢

出0入0汤圆

发表于 2011-12-27 15:14:43 | 显示全部楼层
把 display(i) 中的变量i改成0-11的常数,看现象就晓得了

出0入0汤圆

发表于 2011-12-27 16:39:54 | 显示全部楼层
我这个程序也是怎么也看不懂,不懂为什么要要用12来判断,不是经过7个循环以后就跳出循环了吗?那row应该只能到7呀,怎么会到12的?还有就是那个time_counter也不懂,马老师书上的程序都好难懂,功夫不到家看不懂。

出0入0汤圆

发表于 2011-12-27 21:41:33 | 显示全部楼层
4 個 字
8 劃

= 12


估計!

出0入0汤圆

发表于 2011-12-27 22:17:47 | 显示全部楼层
3年多了,高手还是没有出现。
看不明白先暂时不看,考虑自己如何去写,完成同样的功能。

提示:

一个箭头由8行组成,数组flash unsigned char char_7[8]={0x10,0x38,0x7C,0xFE,0x38,0x38,0x38,0x38};就是箭头的图形。
移动的箭头实际为12行,8行为箭头,4行间隔(不亮,送0就可以:PORTA = 0;)
这段代码根本没有用定时器(还没有讲到),采用的是软件延时。主代码里的time_counter是软件计数(计时)器,用于调整移动的速度,计数上限越大,箭头移动的速度越慢。

要想真正成为高手(不是所谓的“十天学会单片机'),从开始的基础就需要仔细考虑每个细节的问题,时间至少要精确到MS。

在我的代码中,不考虑(忽略)代码执行时间,扫描8行显示一个画面(理解成帧),需要8*2= 16ms,加上主代码中延时9ms,一帧的时间为25ms,1秒就是40帧,保证图像不会闪烁。
移动的速度是100ms(25ms*4 "time_counter>=4" )向上运动一行。

出0入0汤圆

发表于 2011-12-28 00:18:39 | 显示全部楼层
"怎么也看不懂while循环中的意思"

this is an idiotically written piece of sh@t. I mean, code.

first, display():

"void display(unsigned char row)
{
unsigned char i;
for (i=0;i<=7;i++)
{
if (row <= 7)
PORTA = char_7[row];
else
PORTA = 0;
PORTC = ~(1<<i);
delay_ms(2);
PORTC = 0xFF;
if (++row >= 12 ) row = 0;        
}
} "

3 issues:

1) "if (++row >= 12 ) row = 0; ": row is passed to display() as a parameter. so changing it in display() will not persist outside of display().

thus "if (++row >= 12 ) row = 0; " is idiotic.

not to mention the fact that row is never used in that for loop. so why change it?

2) "PORTC = ~(1<<i);
delay_ms(2);
PORTC = 0xFF; ":

PORTC was assigned a value in the first sentence, and will be updated thereafter. so the only reason to reset PORTC to 0xff is just before you exit the loop.

3) PORTA's value is independent of i, so it can be moved outside of the for loop.

this would be what I would do:

void display(unsigned char row)
{
unsigned char i;

if (row <= 7)
PORTA = char_7[row];
else
PORTA = 0;

for (i=0;i<=7;i++)
{
PORTC = ~(1<<i);
delay_ms(2);
//if (++row >= 12 ) row = 0;        
}
PORTC = 0xFF; //reset portc to turn off all digits

}

it will provide identical execution as the original code.

now, the main loop:

1)time_counter is never initialized.

2)  "}; "

what's that about?

:)

looks like the code is trying to display(i) 4 times (9ms each time), and then increment i, and then loop around. for i=0..7, it outputs certain values on PORTA; and for i=8..11, it outputs 0 on PORTA.

a much simpler implementation would be to expand char_7[] with 4 0s in the back. and I would also use a mask, rather than a for loop.

something like this:

const unsigned char char_7[]={0x10,0x38,0x7C,0xFE,0x38,0x38,0x38,0x38,0,0,0,0};

void display(unsigned char row)
{
        unsigned char i=0x01;                                //mask, lsb first

        PORTA = char_7[row];                                //output char_7, last four bytes filled with zero
        do
        {
                PORTC = ~i;
                delay_ms(2);
                i = i<<1;                                                //advance the mask
        } while (i);
        PORTC = 0xFF;
}

出0入0汤圆

发表于 2011-12-29 01:27:15 | 显示全部楼层
9楼的朋友,又一次丢人现眼了。建议你最好弄本我写的教材,看明白该简单例子所要完成的事情,对着电路图重新考虑和修改你的代码。

不知道此位朋友是否有勇气做修正,还是像上次一样做缩头乌龟。

我的void display(unsigned char row) 函数,每调用执行一次,PA口是送出8个不同的数据,这位朋友的函数每调用执行一次,PA口只送出同一个固定的数据,就此一点,它们就完全不同了。

出0入0汤圆

发表于 2011-12-29 18:04:03 | 显示全部楼层
mk

出0入0汤圆

发表于 2012-1-1 19:59:46 | 显示全部楼层
真犀利 ?

一个例程都要高手先了解!

出0入0汤圆

发表于 2012-1-3 15:09:07 | 显示全部楼层
佩服!

出0入0汤圆

发表于 2012-1-3 17:35:09 | 显示全部楼层
今天有空,把这段代码解释并改写一下。

原第6章代码;
#include <mega16.h>
#include <delay.h>

flash unsigned char char_7[8]={0x10,0x38,0x7C,0xFE,0x38,0x38,0x38,0x38};  //每行点亮的LED位置,8行构成完整向上的尖头

void display(unsigned char row)             //执行一次,扫描8行,形成一帧图像,时间16ms
{
    unsigned char i;
    for (i=0;i<=7;i++)                      // 共8行
    {
        if (row <= 7)                       //  送出相应行的点位值
            PORTA = char_7[row];
        else
            PORTA = 0;                      // 以上送出1行点位
        PORTC = ~(1<<i);                    // 点亮第i行
        delay_ms(2);                        // 一行亮2ms
        PORTC = 0xFF;                       // 关显示,起到消隐作用
        if (++row >= 12 ) row = 0;
    }
}

void main(void)
{
    unsigned char time_counter,i = 0;
    PORTA=0x00;
    DDRA=0xFF;
    PORTC=0xFF;
    DDRC=0xFF;

    while (1)
   {
        display(i);
        delay_ms(9);
        if (++time_counter >= 4)
        {
            time_counter = 0;
            if(++i >= 12) i=0;
         }
   };
}
==========================================
1.先分析display函数,假定调用时参数row为0。
     a/ for循环8次,每次送出一行的点,并点亮2MS,8行逐行点亮一遍,用时16ms,显示了一个完整的箭头(由于调用时参数row为0,所以row不会大于等于8的)
     b/ 代码中PORTC = 0xFF;是关闭所有显示,起到消隐(或拖尾)作用。也就是说,当PA口送出新一行点位值时,上一行应该不显示了。否则PA口送出新一行点位值,会在前一行有短暂的显示,然后才是在新一行显示出来,尽管时间很短,但显示效果会有拖尾情况。另外,在这段代码中,如果没有此句,最后一行的扫描显示时间就不是2ms了,而是11ms,这样最后一行的显示就特别亮了。9楼的朋友把这句拉到循环外,这能保证亮度均匀,每行都是2ms,但是消隐作用就没有了。

2.如果理解了第1点,下面分析调用时参数row不为0的情况(箭头图案从0行计算)。
    a/ 假定调用时参数row为1,那么
       点亮第0行(i)的led点位是char_7[1],箭头图案的第1行
       点亮第1行(i)的led点位是char_7[2],箭头图案的第2行
       。。。。
       点亮第6行(i)的led点位是char_7[7],箭头图案的第7行
       点亮第7行(i)的led点位是0 (row>8 PORTA = 0), 补充间隔1行
       这样,原箭头的第一行没有了,最后一行补上是空行。相当箭头向上移动了一行。
    b/ 我设计的箭头移动过程中,两个箭头之间相隔4个空行,所以row的限定为12。比如调用时参数row为6,那么
       点亮第0行(i)的led点位是char_7[6],箭头图案的第6行
       点亮第1行(i)的led点位是char_7[7],箭头图案的第7行      上2行是上个箭头的尾巴
       点亮第2行(i)的led点位是0 (row=8 PORTA = 0), 补充间隔1行
       点亮第3行(i)的led点位是0 (row=9 PORTA = 0), 补充间隔1行
       点亮第4行(i)的led点位是0 (row=10 PORTA = 0),补充间隔1行
       点亮第5行(i)的led点位是0 (row=11 PORTA = 0),补充间隔1行
       点亮第6行(i)的led点位是char_7[0],箭头图案的第0行
       点亮第7行(i)的led点位是char_7[1],箭头图案的第1行      这2行是后一个箭头的开始2行

       如果需要调整移动中2个箭头之间的空行,只要调整row的限定值就可以了,比如需要相隔6行,那么限定值为14就可以了,非常方便。

3.理解上面2点,就知道display函数是完成一帧图像扫描显示的低层驱动。如果每次调用时的参数row都为0,那么就是显示一个静止不动的箭头;如果每次调动时,参数row依次加1,显示的就是移动的箭头。同时,调整参数增加1的时间间隔,也就意味着调整移动速度。

4.最后看主程序。

    while (1)
   {
        display(i);              // 显示一帧图案 16ms
        delay_ms(9);             // 加上9ms,循环时间为25ms. 这样1秒内扫描显示产生40帧图案,(超过25帧/s)不会产生抖动
        if (++time_counter >= 4)
        {
            time_counter = 0;     //4次循环,100ms时间,调整显示上移一行,这里是控制移动速度,给time_counter不同的上限值,移动速度也不同
            if(++i >= 12) i=0;    //此处的i就是display();中的参数row,每次加1,表示显示的图案向上移动一行              
        }
   };

  需要解释的是:
    a/ 主程序中的i和扫描函数中的i不是相同的变量,它们都是局部变量,互相没有任何影响。
    b/ 主程序的i作为参数传递到diaplay()的参变量row中,由于采用的是值传递(不是地址传递),所以diaplay()运行中row改变不会改变主程序i变量的值。
    如果后面2点不明白,请把C拿出仔细复习。

实际上,我在这段代码中为了让学习者巩固所学的C,故意取了两个相同的变量,以及参数传递等,增加点理解难度,借此是向许多的学习者说明,你们在学校所学的C语言,通常都是空架子。掌握不了嵌入式系统,一个重要的因素就是没有好的基础。

=========================================================================

    由于这段代码是出现在第6章,那时候T/C还没有介绍,所以使用的是软件延时,不是最好。改动的代码使用T/C定时中断,使用显示缓冲区,其它思想不变(参见第8章的思考与练习第10题:将第6章中所有采用软件延时的例程进行修改,不使用软件延时方式,用使用T/C加中断的定时方式代替。)

参考代码如下:
#include <mega16.h>
#include <delay.h>

flash unsigned char char_7[8]={0x10,0x38,0x7C,0xFE,0x38,0x38,0x38,0x38};
unsigned char disbuff[8];
unsigned char line,row;
unsigned char time_counter;
bit move_speed_ok;

//显示显示缓冲区中内容,2ms一行,一帧8行16ms,1秒内显示62.5帧。其实点亮就返回,基本不占用CPU的时间,利用率高
void display()
{
    PORTC = 0xFF;                    // 关闭显示,起到消隐作用
    PORTA = disbuff[line];
    PORTC = ~(1<<line);              // 点亮一行  
    if (++line >= 8 ) line = 0;
}

// Timer 0 比较匹配中断服务,2ms中断1次
interrupt [TIM0_COMP] void timer0_comp_isr(void)
{
    display();                                // 调用LED扫描显示一行
    if (++time_counter >= 50)                        // 此处控制移动速度
    {
        time_counter = 0;
        move_speed_ok = 1;
    }
}

void main(void)
{
    unsigned char i,j = 0;
    PORTA=0x00;
    DDRA=0xFF;
    PORTC=0xFF;
    DDRC=0xFF;
    // 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<=7;i++)  {disbuff = char_7};

    while (1)
    {
         if (move_speed_ok && line == 0)
         {
              move_speed_ok = 0;
              for (i=0;i<=7;i++)
              {
                  if (++j >= 12) j=0;
                  if (j<8)
                      disbuff = char_7[j];
                  else
                      disbuff = 0;
               }
          }
     }
}

==========================================================
1.进一步的练习是,增加4个按键,其中2个按键按下为显示静止的上(下)箭头,另外两个分别为箭头向上运动和箭头向下运动,模拟出电梯的上行和下行提示显示
2.加上ADC功能,检测一个输入的0-5v电压,电压越高,箭头移动速度越快,反之电压越低,箭头的移动速度越慢

注意:后面这段代码有3句语句漏打了,有能力的自己调试,实在找不出,看26楼正确的代码
头像被屏蔽

出0入0汤圆

发表于 2012-1-5 09:39:18 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
头像被屏蔽

出0入0汤圆

发表于 2012-1-5 10:02:52 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入0汤圆

发表于 2012-1-5 10:35:23 | 显示全部楼层
马老师的书看来要仔细研读了。虽然第一版买来一年了,看得比较多,但都是看各部分功能,程序还没有仔细研究,现在看来,程序部分才是精华。
头像被屏蔽

出0入0汤圆

发表于 2012-1-5 22:05:16 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入0汤圆

发表于 2012-1-6 12:24:13 | 显示全部楼层
先显示静止的箭头,把主程序的if部分注释掉,只一个while.另外下面的一句改一下,我打错了:

for (i=0;i<=7;i++)  (disbuff = char_7); ==》for (i=0;i<=7;i++)  {disbuff = char_7};
头像被屏蔽

出0入0汤圆

发表于 2012-1-8 22:29:50 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
头像被屏蔽

出0入0汤圆

发表于 2012-1-8 22:31:36 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽
头像被屏蔽

出0入0汤圆

发表于 2012-1-8 22:55:50 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入0汤圆

发表于 2012-1-9 00:13:38 | 显示全部楼层
"你修改的地方, 我通过实验发现还是不亮,一个点阵都不亮!"

it is one of those idiotically written pieces of code.


for example,

"//显示显示缓冲区中内容,2ms一行,一帧8行16ms,1秒内显示62.5帧。其实点亮就返回,基本不占用CPU的时间,利用率高
void display()  
{  
    PORTC = 0xFF;                    // 关闭显示,起到消隐作用
    PORTA = disbuff[line];
    PORTC = ~(1<<line);              // 点亮一行   
    if (++line >= 8 ) line = 0;
}  "

you want your code to be contained as much as possible. so here, put "line" as a local variable:


//显示显示缓冲区中内容,2ms一行,一帧8行16ms,1秒内显示62.5帧。其实点亮就返回,基本不占用CPU的时间,利用率高
void display()  
{  
    static unsigned char line = 0;   //current line to be displayed
    static unsigned char start_line = 0; //start line to be displayed

    PORTC = 0xFF;                    // 关闭显示,起到消隐作用
    PORTA = char_7[(start_line+line) % 8]; //display the current line
    PORTC = ~(1<<line);              // 点亮一行   
    if (++line >= 8 ) {
        line = 0;
        start_line +=1;              //increment start_line
        if (start_line >= 8) start_line = 0;
    }
}  

here, both line/start_line are local to the routine and it allows the start_line to advance by 1 after all 8 lines of char_7[] have been displayed.

as start_line advances from 0 - 7, it creates a moving image on the leds.

"个人觉得一般最好是用括号括起来,不需要而外去记优先级,我用括号就可以解除这种麻烦!不知道我的理解对不?"

you are absolutely correct. it is a lot easier to use brackets than to remember the priorities.

出0入0汤圆

发表于 2012-1-9 00:20:37 | 显示全部楼层
"PORTC = 0xFF;                    // 关闭显示,起到消隐作用  
    PORTA = char_7[(start_line+line) % 8]; //display the current line
    PORTC = ~(1<<line);              // 点亮一行    "

something like this should be avoided. hard coding ports makes changing them later very difficult. instead, define them upfront and code to the logic ports.

#define LED_PORT  PORTC
#define COL_PORT  PORTA
...

  LED_PORT = 0xff;
  COL_PORT = char_7[(....)];

so in the future, if you are to change the ports, just change the defines and recompile.

here is what I wrote - it largely follows the original code but took out the garbage code.

/*
*/

#include <avr/io.h>                                                //we use gcc avr
#include <avr/interrupt.h>                                //we use tmr interrupt

//#include "gpio.h"

//hardware configuration
#define ROW_PORT                        PORTC                //row leds driven by ROW_PORT. active high
#define ROW_DDR                                DDRC
#define ROWs                                0xff

#define COL_PORT                        PORTD                //column leds driven by COL_PORT. active low
#define COL_DDR                                DDRD
#define COLs                                0xff

#define COL_PR                                0x7c                //duration for each col
//end hardware configuration

//define port related macros
#define IO_SET(port, bits)        port |= (bits)
#define IO_CLR(port, bits)        port &=~(bits)
#define IO_IN(ddr, bits)        ddr &=~(bits)
#define IO_OUT(ddr, bits)        ddr |= (bits)

//#include <mega16.h>
//#include <delay.h>

const unsigned char char_7[8]={0x10,0x38,0x7C,0xFE,0x38,0x38,0x38,0x38};
//unsigned char disbuff[8];
//unsigned char /*line,*/row;
//unsigned char time_counter;
//unsigned char move_speed_ok;

//显示显示缓冲区中内容,2ms一行,一帧8行16ms,1秒内显示62.5帧。其实点亮就返回,基本不占用CPU的时间,利用率高
void display(void)
{
        static unsigned char line=0;
        static unsigned char start_line=0;
    COL_PORT = 0xff;                                        //PORTC = 0xFF;                    // 关闭显示,起到消隐作用
    ROW_PORT = char_7[(start_line+line) % 8];                        //PORTA = disbuff[line];
    COL_PORT = ~(1<<line);                                //PORTC = ~(1<<line);              // 点亮一行
    if (++line >= 8 ) {
            line = 0;
            start_line+=1;
            if (start_line >= 8) start_line = 0;
    }
}

// Timer 0 比较匹配中断服务,2ms中断1次
//interrupt [TIM0_COMP] void timer0_comp_isr(void)
ISR(TIMER0_COMP_vect)
{
    display();         // 调用LED扫描显示一行
/*    if (++time_counter >= 50)                        // 此处控制移动速度
    {
        time_counter = 0;
        move_speed_ok = 1;
    }
*/
}

void tmr0_init(void) {
    // T/C0 初始化
    //TCCR0=0x0B;         // 内部时钟,64分频(4M/64=62.5KHz),CTC模式
    TCCR0 =         (0<<FOC0) |                                //no forced output
                                (0<<WGM00) |                        //ctc mode, wgm1..0 = 0b10
                                (0<<COM01) | (0<<COM00) |        //com01..0 = 0b00 -> normal port operation
                                (1<<WGM01) |                        //ctc mode, wgm1..0 = 0b10
                                (0<<CS02) | (1<<CS01) | (1<<CS00)        //cs2..0 = 0b011 -> Fosc/64
                                ;
    TCNT0=0x00;                                                        //reset the tmr/counter
    //OCR0=0x7C;          // OCR0 = 0x7C(124),(124+1)/62.5=2ms
    OCR0 =                COL_PR;
    //TIMSK=0x02;         // 允许T/C0比较匹配中断
    TIMSK =                (TIMSK & 0xfc) |                //retain existing bits
                                (1<<OCIE0) |                        //output compare interrupt enabled
                                (0<<TOIE0)                                //tmr0 overflow interrupt disabled
                                ;

}

//initialize the leds
void led_init(void) {
    IO_CLR(ROW_PORT, ROWs);                        //clear rows - turn off the leds
    IO_OUT(ROW_DDR, ROWs);                        //rows as output

    IO_SET(COL_PORT, COLs);                        //set cols - turn off the leds
    IO_OUT(COL_DDR, COLs);                        //cols as output

    //PORTA=0x00;
    //DDRA=0xFF;
    //PORTC=0xFF;
    //DDRC=0xFF;
}

void mcu_init(void) {
}


int main(void)
{
    //unsigned char i,j = 0;

        //initialize the leds
        led_init();

        //initialize the tmr
        tmr0_init();
    //for (i=0;i<=7;i++)  {disbuff = char_7};

    sei();                                                        //enable global interrupt

    while (1)
    {
/*
         if (move_speed_ok && line == 0);
         {
              move_speed_ok = 0;
              for (i=0;i<=7;i++)
              {
                  if (++j >= 12) j=0;
                  if (j<8)
                      disbuff = char_7[j];
                  else
                      disbuff = 0;
               }
          }
*/
     }

     return 0;
}


in applications like this, you should stripe out a tmr0 related routine and use a function pointer (pointing to display()) to make the code more modular.

出0入0汤圆

发表于 2012-1-9 12:48:56 | 显示全部楼层
回复【23楼】taocongrong 从戎
还有马老师这句话逻辑上有问题if (move_speed_ok && line == 0)   其中&&优先级为11,==优先级为7,个人觉得一般最好是用括号括起来,不需要而外去记优先级,我用括号就可以解除这种麻烦!不知道我的理解对不?我把它改为if((move_speed_ok&&line)==0)
-----------------------------------------------------------------------

你的基础比较差,所以更容易被一些所谓的“行家”迷惑住。

其实 if (move_speed_ok && line == 0)  与 if(move_speed_ok && (line == 0)) 相同,更简单的写法是 if (!line && move_speed_ok)。语法上的理解你去看C语言的教科书吧。至于这句的意义我后面解释。


======================================
14楼的后面的代码,我是看着上面原来的代码随手打上去的,没有调试过,有一些细节上的错误,但如果你基础好的话,能明白思路,是可以自己修改的。

下面我贴上修改过的代码,经过了调试,肯定可以显示移动箭头的。
#include <mega16.h>  

flash unsigned char char_7[8]={0x10,0x38,0x7C,0xFE,0x38,0x38,0x38,0x38};  
unsigned char disbuff[8];
unsigned char line,row;
unsigned char time_counter;
bit move_speed_ok;

//显示显示缓冲区中内容,2ms一行,一帧8行16ms,1秒内显示62.5帧。其实点亮就返回,基本不占用CPU的时间,利用率高
void display()  
{  
    PORTC = 0xFF;                    // 关闭显示,起到消隐作用
    PORTA = disbuff[line];
    PORTC = ~(1<<line);              // 点亮一行   
    if (++line >= 8 ) line = 0;
}  

// Timer 0 比较匹配中断服务,2ms中断1次
interrupt [TIM0_COMP] void timer0_comp_isr(void)
{
    display(); // 调用LED扫描显示一行
    if (++time_counter >= 50)                        // 此处控制移动速度
    {
        time_counter = 0;
        move_speed_ok = 1;
    }
}

void main(void)  
{  
    unsigned char i,j=0;  
    PORTA=0x00;  
    DDRA=0xFF;  
    PORTC=0xFF;  
    DDRC=0xFF;  
    // 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比较匹配中断
    #asm("sei")        // 开中断
    for (i=0;i<=7;i++)  {disbuff = char_7;}

    while (1)  
    {
         if (move_speed_ok && line == 0)
         {
              move_speed_ok = 0;
              if (++row >= 12) row=0;
              j = row;
              for (i=0;i<=7;i++)
              {
                if (++j >= 12) j=0;
                if (j < 8)
                    disbuff = char_7[j];
                else
                    disbuff = 0;
              }
          }
     }
}  

与14楼代码比较,
1.少打了开全局中断的语句(你如果不亮,这个应该能发现的)
2.少打了对变量row操作的2句(变量定义过了,匆忙之中漏打了,此时箭头移动会出问题的)

我曾经让你先调试出静止的箭头,这与2是无关的,应该发现全局中断允许没有打开,当然什么也不会显示的。


===========================================================
采用t/c中断的代码,不是对原代码简单的提升,而是应该要记住的基础和重要程序结构和思路!

一、
   这段代码建立了一个8个字节的显示缓冲区,里面存放要显示的一帧图案。在2ms中断中调用display(),构成8*8点阵的底层驱动,可以保证显示缓冲区中的内容正确,不闪烁,均匀的显示,而且效率是最高的,基本不占用MCU的时间,把更多的时间留给其它的事情。
    至于显示什么内容,中断调用display()所构成的这个显示驱动底层是不管的。而显示内容则由主代码中(或需要的地方)将要显示的图案正确填入显示缓冲区。
   这样处理,就使得程序非常结构化和层次化了,调试也方便。只要底层驱动没有问题,如果显示不对,就是你的图案填充出问题了(所以我让你先调试显示静止的箭头)。

二、
    更换显示内容,就是更换显示缓冲区的8个字节的内容,为了保证一帧显示图案的完整性,更换的时机应该是显示扫描到最后一行后。当line为0时,意味着要开始扫描显示第1行了,也就是一帧显示的开始。所以在主程序的if判断需要两个条件同时成立:line == 0,并且移动时间到,所以语句为;
    if(move_speed_ok && line == 0).

    满足这两个条件后,里面就是更换显示图案了。

三、
   24、25楼朋友的帖子你不要去理睬他。他根本就没看懂和也理解不了我的代码思路,给出的85%都是错误的解释,而且还没有勇气承认和改正。就他代码中的2个问题解释如下;

   1. 类似下面的定义和使用是正确的,我在实际的产品设计中也是采用这样的方式。好处是如果硬件有所变化,软件上的修改可以方便的多了。但这个当你有了更多的实践经验后,自己会总结体会的。可是,对于初学者讲,这样定义会给阅读代码带来困难。会让学习者化精力放在前后的对应关系上,而把对最重要的程序思路和结构的理解和掌握忽视掉了。
      #define LED_PORT  PORTC
      #define COL_PORT  PORTA

      许多学生的C根本就是白学的,类似 a |= (1<< 5)都不懂,你再来个
      #define b  5
      a |= 1<< b;
      就更摸不到头脑了。

   2.在我的代码中,display()就是实现显示底层扫描驱动的,不管显示的内容。更换显示内容在主代码中。这样的处理是真正面向应用的结构。一个电梯的显示,不是单纯显示向上移动的箭头:
    当电梯停止时显示当前的楼层号(假定在1楼,则显示静止的1);而在电梯上升过程中,显示向上移动的内容,这个内容是1、箭头、箭头、2、箭头、箭头、3.。。。。。箭头、箭头、9;停在9楼,显示静止的“9”。如果下楼:显示向下移动的内容,这个内容是9、下箭头、下箭头、8、下箭头、下箭头、7.。。。。。下箭头、下箭头、1。

    因此,利用我的程序,只要在主代码中考虑如何填入正确的显示图案就可以了(就是考虑 if(move_speed_ok && line==0) {.....}这个判断里面如何变化),而其它的部分,中断、和显示底层的驱动diaplay()是不要改动的。

    而25楼的,把显示内容和显示扫描驱动混在一起,如果要实现上面的实用功能,他的代码根本没有任何参考的价值了。

出0入0汤圆

发表于 2012-1-9 19:40:47 | 显示全部楼层
回复【25楼】millwood0
"portc = 0xff;                    // 关闭显示,起到消隐作用   
    porta = char_7[(start_line+line) % 8]; //display the current line  
    portc = ~(1&lt;&lt;line);              // 点亮一行    "
something like this should be avoided. hard coding ports makes changing them later very difficult. instead, define them upfront and code to the logic ports.
#define led_port  portc
#define col_port  porta
...
  led_......
-----------------------------------------------------------------------

朋友,不要给中国人丢脸。在偶的地盘,还是对你客气的,如果你在国外学习,也是如此低能,无礼貌,真的是给中国人丢脸。

你25楼的的代码运行过吗?显示的是什么?实现的与我的代码功能完全相同吗?箭头移动的速度如何调整?两个箭头之间的4个空行也没有了。

其实你的代码运行后,显示点阵是一片亮点,连个箭头的影子也看不出的。

劝你不要在这里班门弄斧了,卖弄那点所谓的C的标准,关键的东西你差远了。
要卖弄,也要卖弄点有价值的东西,写个代码,连基本要求的功能都不能实现,写上100个define定义有何用?


下面是我做的某个产品设计的H文件,你的那些用define什么的,都在这里使用。

/*-----------------------------------------------
文件名:global.h
------------------------------------------------*/
#ifndef _Include_Global_H
#define        _Include_Global_H  0xff

#include <intrins.H>
#include <STC_NEW_8051.H>
#define uchar        unsigned char
#define uint        unsigned int
#define ulong        unsigned long

#define true        1
#define false        !true
#define NULL        0

#define T0_VECTOR                1
#define T1_VECTOR                3
#define UART_VECTOR                4
#define LVD_VECTOR                6
#define PCA_VECTOR                7
#define UART2_VECTOR        8

#define T0_HL                65536 - 1843                                //((2000*12000000)/12000000)  1843=>11.0592M                                // 2ms
#define T0CONSTL        T0_HL&0x00ff
#define T0CONSTH        (T0_HL>>8)&0x00ff
#define PCA_int_v        691                                                //PCA 中断间隔 8K, 12M/2/750  691=>11.0592M

#define T1_HL                0xf4                                //4800bps UART0 => 11.0592  f4==>2400bps
#define BTR_v                184                                        //9600bps 256 - (11.0592/32/4800)  

#define voice_delay_time        125                        //2.5s
#define com_err_dis_time        150                        //3s
#define key_delay_time                500                        //10s  20ms*500

//--输入信号定义---
sbit HJ_YD                =        P1^6;   //摘机/挂机输入
sbit SEL_s1                =        P3^6;        //选择1 in
sbit SEL_s2                =        P3^7;        //选择2 in
sbit mp3_busy        =        P2^0;        //busy in
sbit power_down =         P4^6;

//控制定义
sbit power_c = P2^2;
#define power_c_on        power_c = true
#define power_c_off power_c = false

sbit VOICE_EN = P3^2;        //声音控制 out
#define VOICE_EN_on                VOICE_EN = false
#define VOICE_EN_off        VOICE_EN = true

sbit hand_c = P1^7;                //摘机用手柄
#define hand_on                hand_c = true
#define hand_off        hand_c = false

sbit MIC_EN                =        P3^3;        //麦克风控制 out
#define MIC_EN_on        MIC_EN = false
#define MIC_EN_off        MIC_EN = true

sbit CENT_EN        =        P1^5;   //中心连接控制 out
#define CENT_EN_on                CENT_EN = true
#define CENT_EN_off                CENT_EN = false


//LED定义
#define led_buffer        disbuff[3]
#define power1_on                led_buffer |= 0x02;led_buffer &= 0xfe
#define power2_on                led_buffer |= 0x01;led_buffer &= 0xfd
#define work_call_on        led_buffer &= 0x03;led_buffer |= 0x04
#define work_call_off         led_buffer &= 0xfb
#define work_check_on        led_buffer &= 0x03;led_buffer |= 0x08
#define work_check_off        led_buffer &= 0xf7
#define work_ala_on                led_buffer &= 0x03;led_buffer |= 0x10
#define work_ala_off        led_buffer &= 0xef
#define work_set_on                led_buffer &= 0x03;led_buffer |= 0x20
#define work_set_off        led_buffer &= 0xdf
#define work_all_off        led_buffer &= 0xc3

//显示字符编码
#define char_0        0
#define char_1        1
#define char_2        2
#define char_3        3
#define char_4        4
#define char_5        5
#define char_6        6
#define char_7        7
#define char_8        8
#define char_9        9
#define char__        10
#define char_E        11
#define char_P        12
#define char_b        13
#define char_L        14
#define char_F        15
#define char_S        16
#define char_H        17
#define char_h_1 18
#define char_h_2 19
#define char_h_3 20
#define char_k_u 21
#define char_k_d 22
#define char_c_s 23
#define char_c_b 24
#define char_A        25
#define char_l  26
#define char_h        27
#define char_        28

// 按键定义
#define key_no                0xff
#define key_0                0x00
#define        key_1                0x01
#define key_2                0x02
#define key_3                0x03
#define key_4                0x04
#define key_5                0x05
#define key_6                0x06
#define key_7                0x07
#define key_8                0x08
#define key_9                0x09
#define key_x                0x0a
#define key_j                0x0b
#define key_enter        0x0f
#define key_1_1                0x11
#define key_1_2                0x12
#define key_1_3                0x13
#define key_1_4                0x14
#define key_1_5                0x15
#define key_2_1                0x21
#define key_2_2                0x22
#define key_2_3                0x23
#define key_3_1                0x31
#define key_3_2                0x32
#define key_3_3                0x33

//--输出控制定义---
sbit DIALL_1        =        P1^0;        //语音1 out
sbit DIALL_2        =        P1^1;        //语音2 out

//机房机工作状态
#define SLAVE_add                                0x00

#define work_s_stand_by                        1
#define work_s_call_f_sub                2
#define work_s_talk_pass                3
#define work_s_talk_t_sub                4
#define work_s_talk_t_sub_call        5
#define work_s_talk_t_all                6
#define work_s_alarm                        7
#define work_s_check                        8
#define work_s_com_err                        9
#define work_s_wait                                10

//管理主机状态
#define work_m_standby                1        //待机
#define work_m_call_f_s                2        //有呼叫
#define work_m_talk_t_all        3        //对讲
#define work_m_talk_s                4        //子机通话
#define work_m_check                5        //检查线路
#define work_m_alarm                6        //紧急报警
#define work_m_com_err                7        //线路故障
#define work_m_talk_off                8
#define work_m_talk_on                9

#define work_m_ent_num                10
#define work_m_pass_int                11

#define work_m_talk_to                13
#define work_m_standby_to        14
#define work_m_call_wait        15
#define work_m_group_call        16

#define work_m_init                        20        //搜索有效子机
#define work_m_init_1                21        //搜索结束,等待确认

#define work_m_set_1                30
#define work_m_set_2                31

//set 状态
#define set_s_init                        0
#define set_s_1                                10
#define set_s_1_1                        11
#define set_s_1_2                        12
#define set_s_1_3                        13
#define set_s_1_4                        14
#define set_s_1_5                        15
#define set_s_1_6                        16
#define set_s_2                                20
#define set_s_2_1                        21
#define set_s_2_2                        22
#define set_s_2_3                        23
#define set_s_2_4                        24
#define set_s_2_5                        25
#define set_s_2_6                        26

//线路检查状态
#define check_1                                1
#define check_2                                2
#define check_3                                3
#define check_4                                4
#define check_5                                5
#define check_6                                6
#define check_7                                7
#define check_8                                8

//紧急呼叫状态
#define alarm_s_1                        1
#define alarm_s_2                        2
#define alarm_s_3                        3

//群呼状态
#define gc_s_1                        1
#define gc_s_2                        2
#define gc_s_3                        3

//通信状态
#define com_s_1                1
#define com_s_2                2
#define com_s_3                3
#define com_s_4                4
#define com_s_5                5
#define com_s_6                6
#define com_s_7                7
#define com_s_8                8
#define com_s_9                9
#define com_s_10        10


//管理主机下发命令
#define main_add                0xa0
#define all_add                        0x80
//com[0]
#define com_ask                        0x00        //询问
#define com_on                        0x01        //通话
#define com_off                        0x02    //关闭通话
#define com_wait                0x03
#define com_check                0x04
#define com_alarm                0x05
#define com_set                        0x06        //设置从机地址
#define com_group_callg        0x07       

//LED 工作状态
#define led_s_off                0
#define led_s_on                1
#define led_s_blink1        2
#define led_s_blink2        3

extern bit Uart_RecvFlag,Uart_TranFlag;
extern uchar rx_buffer[];
extern uchar command[],sub_add,total_sub,time;
extern uchar xdata sub_jf[99][2];
#define slave_ok                0
//#define slave_state                1
#define slave_com_err        1

extern uchar self_add,time;
void rs232_tx_manager(void);

extern bit music_on;
//声音
#define v_hjjk        1
#define v_txgz        2
#define v_jjss        3
#define v_hjy        4
#define v_wcfj        5


void play_on_voice(uchar num);
//void play_off_voice(void);

#define valid        0xf5
#define invalid        0xfa
#define DATA_add        0x0200                        //EEPROM 保护字扇区
#define DATA_add_hi 0x02                        //
#define save_data_number        4
extern uchar err_mem[save_data_number];

//void Sector_Erase(uchar add_h);
uchar read_ee_work_data(void);
void write_ee_back_data(void);

extern uchar disbuff[],blik_mode;
extern bit dis_blik;
void display(void);
uchar read_key(void);

extern bit time_20ms_ok,com_time_ok;
extern        bit zaiji,zaiji_pre,zaiji_temp;

extern uchar com_send_over;
#define com_err_alarm_nub        125
void look_proc(void);
void set_proc(void);
void check_proc(void);
void alarm_proc(void);
void group_call_proc(void);

#endif

出0入0汤圆

发表于 2012-1-9 23:52:25 | 显示全部楼层
"朋友,不要给中国人丢脸。在偶的地盘,还是对你客气的,如果你在国外学习,也是如此低能,无礼貌,真的是给中国人丢脸。"

sounds like you are mad.

"你25楼的的代码运行过吗?"

yes.

"箭头移动的速度如何调整?"

easy. you can change start_line's increments: incrementing start_line makes it move in one direction, decrementing start_line makes it move in another direction, and not incrementing start_line holds the picture steady.

most people will be able to do it in a matter of seconds and even you should be able to do it in a week or so.

"其实你的代码运行后,显示点阵是一片亮点,连个箭头的影子也看不出的。"

maybe you didn't do it right? tell us how you did it and we will help you.

"劝你不要在这里班门弄斧了,"

your 班门? more accurately, your brain-门 - because you have none.

I just wanted to help those who got duped to buy your book.
头像被屏蔽

出0入0汤圆

发表于 2012-1-10 00:07:17 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入0汤圆

发表于 2012-1-10 01:03:04 | 显示全部楼层
回复【29楼】taocongrong 从戎
马老师 有个问题想问您一下:
if (move_speed_ok &amp;&amp; line == 0)   
         {  
              move_speed_ok = 0;  
/* if (++row &gt;= 12)  
   row=0;  
   j=row;
   //row++;*/为什么这里要这样处理,要额外的引入一个j呢?
   if(++j&gt;=12)
   j=1;
            
              for (i=0;i&lt;=7;i++)   
              {  
                if (++j &gt;=12) j=1;  
                if (j&lt; 8)   
                    disbuff = char_......
-----------------------------------------------------------------------

你把row定为1,然后分析循环后disbuff[]中是什么数据,然后在假定row为,再看循环后disbuff[]中是什么数据,。。。。

出0入0汤圆

发表于 2012-1-10 01:19:44 | 显示全部楼层
回复【28楼】millwood0
"朋友,不要给中国人丢脸。在偶的地盘,还是对你客气的,如果你在国外学习,也是如此低能,无礼貌,真的是给中国人丢脸。"
sounds like you are mad.
"你25楼的的代码运行过吗?"
yes.
"箭头移动的速度如何调整?"
easy. you can change start_line's increments: incrementing start_line makes it move in one direction, decrementing start_line makes it move in another direction, and not incrementing start_line holds the picture steady.
most people will be able to do it in a matter of secon......
-----------------------------------------------------------------------

程序不完整,有问题就应该勇于承认和修改,我写错了,会承认修改的。你是硬着头皮死顶。

你就不认账,程序中速度调整没有就是没有。你最多只是软件模拟过,模拟的速度和实际运行速度差多了。模拟过程中是箭头移动,但实际运行由于移动速度太快,显示效果就成了亮点一片。

出0入0汤圆

发表于 2012-1-10 03:04:27 | 显示全部楼层
"不明白马老师为什么这样处理那几句话(我去掉的那几句话),"

that portion of the code does a boundary check on row. The way he implemented the code is to insert 4 dark rows into the image.

you can ignore the whole thing by inserting four rows into char_7[], and make corresponding changes to accommodate 4 dark rows - that would have been a lot more easier to code and understand.

"你就不认账,程序中速度调整没有就是没有"

you simply do not understand how the code works and implemented it incorrectly.

出0入0汤圆

发表于 2012-1-10 08:58:23 | 显示全部楼层
"程序中速度调整没有就是没有"

since our professor insisted on being able to change speed, here is one implementation.

display_speedset() sets the frame rate at which the display is updated.


/*
*/

#include <avr/io.h>                                                //we use gcc avr
#include <avr/interrupt.h>                                //we use tmr interrupt

//#include "gpio.h"

//hardware configuration
#define ROW_PORT                        PORTC                //row leds driven by ROW_PORT. active high
#define ROW_DDR                                DDRC
#define ROWs                                0xff

#define COL_PORT                        PORTD                //column leds driven by COL_PORT. active low
#define COL_DDR                                DDRD
#define COLs                                0xff

#define COL_PR                                0x3c                //duration for each col, in tmr ticks
//end hardware configuration

//define port related macros
#define IO_SET(port, bits)        port |= (bits)
#define IO_CLR(port, bits)        port &=~(bits)
#define IO_IN(ddr, bits)        ddr &=~(bits)
#define IO_OUT(ddr, bits)        ddr |= (bits)

//#include <mega16.h>
//#include <delay.h>

#define UPDATE_PAUSE                0                                                //statitionary image
#define UPDATE_FAST                        1                                                //fast update
#define UPDATE_MEDIUM                5                                                //medium update speed
#define UPDATE_SLOW                        10                                                //slow update speed

//default user isr
void tmr0_isr_default(void) {
}

void (* tmr0_isr_ptr) (void)=tmr0_isr_default;                //tmr0 isr pointer

unsigned char _display_updatespeed=UPDATE_FAST;                //display speed
volatile unsigned char _display_updatecount=0;                                //update count

const unsigned char char_7[]={0x10,0x38,0x7C,0xFE,0x38,0x38,0x38,0x38,
        0x00, 0x00, 0x00, 0x00};                                                //four rows of blanks
//unsigned char disbuff[8];
//unsigned char /*line,*/row;
//unsigned char time_counter;
//unsigned char move_speed_ok;

//显示显示缓冲区中内容,2ms一行,一帧8行16ms,1秒内显示62.5帧。其实点亮就返回,基本不占用CPU的时间,利用率高
void display(void)
{
        static unsigned char line=0;
        static unsigned char start_line=0;
    COL_PORT = 0xff;                                        //PORTC = 0xFF;                    // 关闭显示,起到消隐作用
    ROW_PORT = char_7[(start_line+line) % 12];                        //PORTA = disbuff[line];
    COL_PORT = ~(1<<line);                                //PORTC = ~(1<<line);              // 点亮一行
    if (++line >= 12 ) {
            line = 0;
                _display_updatecount+=1;                                        //update count
                if (_display_updatecount == _display_updatespeed) {
                        _display_updatecount = 0;                                //reset the update count
                        start_line+=1;
                        if (start_line >= 12) start_line = 0;
                }
    }
}

void display_speedset(unsigned char speed) {
        _display_updatespeed = speed;                                        //update the display speed
}
// Timer 0 比较匹配中断服务,2ms中断1次
//interrupt [TIM0_COMP] void timer0_comp_isr(void)
ISR(TIMER0_COMP_vect)
{
        tmr0_isr_ptr();                                                                                //run the user isr
    //display();         // 调用LED扫描显示一行
/*    if (++time_counter >= 50)                        // 此处控制移动速度
    {
        time_counter = 0;
        move_speed_ok = 1;
    }
*/
}

void tmr0_init(unsigned char pr) {
    // T/C0 初始化
    //TCCR0=0x0B;         // 内部时钟,64分频(4M/64=62.5KHz),CTC模式
    TCCR0 =         (0<<FOC0) |                                //no forced output
                                (0<<WGM00) |                        //ctc mode, wgm1..0 = 0b10
                                (0<<COM01) | (0<<COM00) |        //com01..0 = 0b00 -> normal port operation
                                (1<<WGM01) |                        //ctc mode, wgm1..0 = 0b10
                                (0<<CS02) | (1<<CS01) | (1<<CS00)        //cs2..0 = 0b011 -> Fosc/64
                                ;
    TCNT0=0x00;                                                        //reset the tmr/counter
    //OCR0=0x7C;          // OCR0 = 0x7C(124),(124+1)/62.5=2ms
    OCR0 =                pr;                                                //set up the period
    //TIMSK=0x02;         // 允许T/C0比较匹配中断
    TIMSK =                (TIMSK & 0xfc) |                //retain existing bits
                                (1<<OCIE0) |                        //output compare interrupt enabled
                                (0<<TOIE0)                                //tmr0 overflow interrupt disabled
                                ;

        //tmr0_isr_ptr = tmr0_isr_default;        //install an empty isr routine

}

void tmr0_act(void (* funct_ptr)(void)) {
        tmr0_isr_ptr = funct_ptr;                        //install user isr routine
}

//initialize the leds
void led_init(void) {
    IO_CLR(ROW_PORT, ROWs);                        //clear rows - turn off the leds
    IO_OUT(ROW_DDR, ROWs);                        //rows as output

    IO_SET(COL_PORT, COLs);                        //set cols - turn off the leds
    IO_OUT(COL_DDR, COLs);                        //cols as output

    //PORTA=0x00;
    //DDRA=0xFF;
    //PORTC=0xFF;
    //DDRC=0xFF;
}

void mcu_init(void) {
}


int main(void)
{
    //unsigned char i,j = 0;

        //initialize the leds
        led_init();

        //initialize the tmr
        tmr0_init(COL_PR);
    //for (i=0;i<=7;i++)  {disbuff = char_7};

    tmr0_act(display);                                //install display as user isr
        display_speedset(UPDATE_PAUSE);        //set the update speed
    sei();                                                        //enable global interrupt

    while (1)
    {
/*
         if (move_speed_ok && line == 0);
         {
              move_speed_ok = 0;
              for (i=0;i<=7;i++)
              {
                  if (++j >= 12) j=0;
                  if (j<8)
                      disbuff = char_7[j];
                  else
                      disbuff = 0;
               }
          }
*/
     }

     return 0;
}


you can play around display_speedset() with different parameters, to get the update to go from totally stationary, to very very fast.

it also display four bank rows, as the professor insisted.

all of them are done in a modular way. display() is written so that it can be called from within an isr, or separately.

出0入0汤圆

发表于 2012-1-10 08:58:52 | 显示全部楼层
it is also quite easy to modify the code so that the images go backward.

出0入0汤圆

发表于 2012-1-10 09:01:05 | 显示全部楼层
in case you are wondering, the user code is hooked in via a function pointer.

this allows tmr0_init() and tmr0_act() to be written in separate modules and used in future projects.

出0入0汤圆

发表于 2012-1-10 11:00:01 | 显示全部楼层
马老师,if (move_speed_ok && line == 0)
         {
              move_speed_ok = 0;
              if (++row >= 12) row=0;
              j = row;
              for (i=0;i<=7;i++)
              {
                if (++j >= 12) j=0;
                if (j < 8)
                    disbuff = char_7[j];
                else
                    disbuff = 0;
              }
          }  

我理解这个程序中j = row;  if (++j >= 12) j=0; 好像下一帧的图像从原来的图像后两行开始的?如原来显示的第一行row=0,如内容disbuf[0],现在从j=2开始,就是原来的disbuff[2].

是这样吗?

还有一个问题:if (move_speed_ok && line == 0) 是说正好显示完一帧而且100ms时间到,就更新disbuff,准备显示下一帧图像.但实际上这个100ms不是真正的100ms.因为显示完整的一帧图像16ms,应该是16*7=112ms,才开始显示下一帧图像.理解对吗?

出0入0汤圆

发表于 2012-1-10 20:32:05 | 显示全部楼层
回复【36楼】atmega48
马老师,if (move_speed_ok &amp;&amp; line == 0)  
         {
              move_speed_ok = 0;
              if (++row &gt;= 12) row=0;
              j = row;
              for (i=0;i&lt;=7;i++)  
              {
                if (++j &gt;= 12) j=0;
                if (j &lt; 8)  
                    disbuff = char_7[j];
                else
                    disbuff = 0;
           ......
-----------------------------------------------------------------------
1. row = 0  j = 0-7       disbuff[]中是原整个箭头
   row = 1  j = 1-8       disbuff[]中是去掉前1行的箭头,后面补1行空行
   row = 2  j = 2-9       disbuff[]中是去掉前2行的箭头,后面补2行空行
   row = 3  j = 3-10      disbuff[]中是去掉前3行的箭头,后面补3行空行
   row = 4  j = 4-11      disbuff[]中是去掉前4行的箭头,后面补4行空行
   row = 5  j = 5-11,0    disbuff[]中是去掉前5行的箭头,跟补4行空行,后面是箭头的第1行
   row = 6  j = 6-11,0-1  disbuff[]中是去掉前6行的箭头,跟补4行空行,后面是箭头的第2行
   。。。。。。。。
   row = 11 j = 11,0-6    disbuff[]中第1行为空行,后面是箭头的第前7行
   row = 12 等同 row = 0  disbuff[]中是原整个箭头,完成整个箭头的一次移动

   如果这个看不明白的,说明C的基础太差,程序设计能力还达不到学习嵌入式系统应用的要求。类似像35楼朋友所说的“in case you are wondering, the user code is hooked in via a function pointer”,更是无法理解他在说什么了。

   顺便解释一下,35楼朋友对于C还是比较精通的,把“函数指针”都搬出来了。尽管他的方法是可行的,我都明白,也会用,可是面对学生我是没有任何信心用这样“高质量”的代码。

   华东师范大学,也算是全国重点高校,但我所面对的,所谓学习过,掌握了C语言程序设计的学生,他们位操作没有学过;结构体、指针等概念和使用基本上是浆糊一桶;联合体不知道是什么东西...(你的第1个问题就是例证,简单的一个循环赋值过程就已经晕了)。所以我的重点还是放在嵌入式应用的基本思想和方法上,尽量用基本的、简单的C代码描述思路和算法。

2. 移动速度100ms对于某一帧的更换是个大概的时间,其误差对于一帧的更换讲,范围在100ms +/- 14ms之间。如果100ms到时,刚好扫描第2行,那么新的一帧更换会延时14ms,但下一帧的更换则是86ms,这个误差是不会积累的,14ms差别,人眼通常不会察觉,并且图像整体的平均移动速度还是保持在100ms.

   你也可以把line == 0 的条件去掉,100ms到马上更换一帧图像,总体显示的效果也是不错的,但仔细注意,会有“拖尾”现象存在。当帧率超过30后,人眼对时间的反应已经达到极限,但对亮暗的敏感度还是比较强的。

    动态扫描显示,其实是在“骗人”,因为代码实际上只是点亮一行,但到达一定的条件,你看上去就是8行整个都在亮了。“骗人”也要骗的完整些,完美些,尽量让人查觉不到为好。

    明白这些基本的思想后,如果想帧更换时间误差再小点,那么定时器的中断可以更改为1ms,此时其误差对于一帧的更换讲,范围在100ms +/- 7ms之间。

出0入0汤圆

发表于 2012-1-10 21:14:51 | 显示全部楼层
"更是无法理解他在说什么了。"

I told you so, :)

"我都明白,也会用,"

yeah, right, :)

here is a test to see if you truly understood it: implement a way to get the image to go backwards.

I already laid out the concept. all you need to do is to perform a boundary check.

if you really understood how to code, you should be able to do it in a minute or so.
头像被屏蔽

出0入0汤圆

发表于 2012-1-12 22:36:40 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入0汤圆

发表于 2012-1-13 01:14:32 | 显示全部楼层
学习了

出0入0汤圆

发表于 2012-1-13 01:23:24 | 显示全部楼层
"由于要执行if(++j>=12)那么当执行后j就变为1了 所以,当row=0的时候显示的是 j=1- 8, disbuff[]中是去掉前1行的箭头,后面补1行空行 ,以此类推,这个是我通过avrstudio调试也是这样的,具体的实验也是这样的,j直接从1变化到7,不知道我说的对不对!"

you are correct.

出0入0汤圆

发表于 2012-1-13 08:29:54 | 显示全部楼层
to make the code more generic, you should approach the problem, thinking of displaying a (moving) image of 8xM, and display it on a led matrix of 8xN (N<=M).

with that approach, your code will be much closer to a commercial piece.

出0入0汤圆

发表于 2012-1-13 09:43:07 | 显示全部楼层
学了
头像被屏蔽

出0入0汤圆

发表于 2012-1-13 12:12:02 | 显示全部楼层
提示: 作者被禁止或删除 内容自动屏蔽

出0入0汤圆

发表于 2012-1-13 12:54:03 | 显示全部楼层
mark! 受教了
多谢!!

出0入0汤圆

发表于 2012-1-13 21:12:37 | 显示全部楼层
the general design for those things is to drive the columns with shift registers and the rows either directly (for limited numbers of rows, like 8 or so), or via a shift register (>8 rows) buffered.

the use of shift registers allow for easy expansion of the design, to accommodate bigger displays without changing the code much.
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。【本论坛不允许直接上传手机拍摄图片,浪费大家下载带宽和论坛服务器空间,请压缩后(图片小于1兆)才上传。压缩方法可以在微信里面发给自己(不要勾选“原图),然后下载,就能得到压缩后的图片】。另外,手机版只能上传图片,要上传附件需要切换到电脑版(不需要使用电脑,手机上切换到电脑版就行,页面底部)。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2024-4-20 15:09

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

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