amoBBS 阿莫电子论坛

 找回密码
 注册
搜索
bottom↓
楼主: machao

本次实践操作考核题,请有兴趣的给个简单代码—-- 157楼给出目前最佳的代码

[复制链接]
发表于 2011-1-18 10:27:16 | 显示全部楼层
回复【98楼】McDeggy 再见,列宁
-----------------------------------------------------------------------

取一次数据要多长时间?
发表于 2011-1-18 10:27:40 | 显示全部楼层
手机看帖,杯具
如果是二维数组显示矢量图形,我表示鸭梨很大
发表于 2011-1-18 10:29:39 | 显示全部楼层
1:可以开两个定时器对LED扫描,一个运动点,四个静态点。
2:28个运动点,按照T累加,按点计算位置,确定段码。
3:主函数扫描键盘,对进行累加,当然需要消抖延时。
不懂AVR,只是谈谈思路!
发表于 2011-1-18 10:30:01 | 显示全部楼层
mark!
发表于 2011-1-18 10:32:45 | 显示全部楼层
void scan(void)
{
    static unsigned char sRow=0;
    sRow =(--sRow)&0x07;
    PORTA = ~(1<<sRow);
    PORTB = (sRow==row)<<col;
    if ((~PORTA)&0x18) {
        PORTB |= 0x18;
    }
}

/*6.25ms*/
ISR(TIM0_OVF_vect)
{
    scan();
    //volatile global counter
    counter = (++counter)&0x1F; //%32
    if (!counter) {
        goNext();
    }
}
发表于 2011-1-18 10:40:40 | 显示全部楼层
回复【102楼】catvevs
-----------------------------------------------------------------------

没有必要!静态点是因为显示的数据有某几个位一直不变,还开定时器干吗?一个足够了,只需更新变化的数据,不变的位让它一直不变。
对于按键带来的延时可以用中断消除,即用定时器的中断来更新数据,而不是在主函数更新。

当年俺单片机考了60分,全年级99.9%的考了90分以上!我也是最后交卷的一个。普通二本,学校就不说了。
发表于 2011-1-18 10:42:42 | 显示全部楼层
这题目挺头疼的,怎么写都感觉不太优雅...不管是算点还是扫描,都觉得很别扭
发表于 2011-1-18 10:47:10 | 显示全部楼层
http://people.ece.cornell.edu/land/courses/ece4760/FinalProjects/
看看人家的大学,对比对比!我们大学生多半时间在网游里遨游了!
发表于 2011-1-18 10:51:04 | 显示全部楼层
先做个“字模”,然后全部查表解决。。。。
发表于 2011-1-18 10:52:50 | 显示全部楼层
u8 DisplayBuf[8]={0,0,0,0,0,0,0,0};

volotile u8 Index_Col=0;

定时器中断程序(每 200 / 8 =25 ms 中断一次):

void
init_timer0(void)
{
    TCCR0 = 0x00; //停止定时器

    TCNT0 = xx; //大概定时25ms

    TIMSK |=BIT(TOIE0); //溢出中断允许

    TCCR0 = BIT(CS02) | BIT(CS00); //启动定时器,普通模式,1024分频,不与OC0连接       
}


void
stop_timer0(void)
{
    TCCR0 = 0;
}

ISR(SIG_OVERFLOW0)
{
    PORTA = (1 << Index_Col);
    PORTC = ~(DisplayBuf[Index_Col]);

    Index_Col++;
    if(Index_Col>=8) Index_Col=0;
}

void SetXY(u8 x, u8 y)
{
   DisplayBuf[x] |= (1<<Y);
}

void ClrXY(u8 x, u8 y)
{
   DisplayBuf[x] &= ~(1<<Y) ;
}
发表于 2011-1-18 10:56:11 | 显示全部楼层
【105楼】 fei_yang
我的理解是:
1: 你这样点的话,你是同时在点亮动态点和静态点,这样会把其它的点点亮。
2: 我觉得这样会更简单,在定时器处理按键相对复杂,比如处理按键一直按下时只是加1,而不是一直加下去。

个人愚见!不知道是否正确!
发表于 2011-1-18 11:01:17 | 显示全部楼层
马老师要求严。我见过的工科毕业的本科的、研究生的,有连堆栈、编译链接过程等是什么都不知道也在做单片机开发,谁要是用了这样的设备我想想都怕。
发表于 2011-1-18 11:04:12 | 显示全部楼层
回复【110楼】catvevs
-----------------------------------------------------------------------

“1: 你这样点的话,你是同时在点亮动态点和静态点,这样会把其它的点点亮。”把单片机书抄十遍,特别是动态扫描数码管那一章节!
产品中按键一般是用中断。
发表于 2011-1-18 11:11:01 | 显示全部楼层
单片机考试我感觉就两道题即可:
1.请简述单片机的工作原理,为什么会有编译器、C语言?
2.我想自己做个单片机可以吗?如果可以,请简述思路。
发表于 2011-1-18 11:22:50 | 显示全部楼层
有个思路 板子焊好了 验证一下
发表于 2011-1-18 11:25:28 | 显示全部楼层
偶中专的,90级了,学校啊,早没有了,哈哈,凑个热闹而已,马老师莫怪哦!
说说我的理解吧
利用液晶取模的软件取得8*8点阵的数据共计28个状态,然后做一个硬件的驱动,利用定时器定时循环显示这28个相当于字符的东西,这样可能是最简单有效的了。
编辑原因,打错了,打成16*16了,哈哈
发表于 2011-1-18 11:26:45 | 显示全部楼层
【112楼】 fei_yang
确实是思路有问题,
1:要求按键用PC0口,复用点灯,所以要用定时器对按键扫描,按键要做得好比较困难,不过也能做。

2:扫描按键和点灯可以共用一个定时器,为了不相互干扰,可以在扫动态点和静态点之间加灯全灭灯语句。

多谢你的提醒!
发表于 2011-1-18 11:29:24 | 显示全部楼层
回复【109楼】knight_avr  
u8&#160;displaybuf[8]={0,0,0,0,0,0,0,0};

volotile&#160;u8&#160;index_col=0;

定时器中断程序(每&#160;200&#160;/&#160;8&#160;=25&#160;ms&#160;中断一次):

void
init_timer0(void)
{
&#160;&#160;&#160;&#160;tccr0&#160;=&#160;0x00;&#160;//停止定时器

&#160;&#160;&#160;&#160;tcnt0&#160;=&#160;xx;&#160;//大概定时25ms

&#160;&#160;&#160;&#160;timsk&#160;|=bit(toie0);&#160;//溢出中断允许

&#160;&#160;&#160;&#160;tccr0&#160;=&#160;bit(cs02)&#160;|&#160;bit(cs00);&#160;//启动定时器,普通模式,1024分频,不与oc0连接       
}


void&#160;
stop_timer0(void)
{
&#160;&#160;&#160;&#160;tccr0&#160;=&#160;0;
}

isr(sig_overflow0)
{
&#160;&#160;&#160;&#160;porta&#160;=&#160;(1&#160;&lt;&lt;&#160;index_......
-----------------------------------------------------------------------

200mS刷新一次,会闪烁吧???
发表于 2011-1-18 11:32:33 | 显示全部楼层
要是我做,还是用查表得笨办法,两个表格(每个表格28字节)分别决定PA,PC在某一时刻需要循环闪烁的那一位的状态,定时中断分为20ms和200ms(0.2s),20ms的那个定时中断用来切换常亮和需要循环亮的程序,200ms定时用来切换循环亮的状态位!
发表于 2011-1-18 11:38:59 | 显示全部楼层
PC0复用按键电路怎样接?一直按着的话是不是会影响显示?
如果开关另外一端接地,那么PC口是不是只能接LED的阴极?如果是阳极,就要强输出,那么一直按键会不会烧掉这个线?
抱歉我没有那个实验板,瞎想的,哈哈。

以下是8*8的数据表,一个一个做的,耗时7分钟,共计28行
0x01,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
0x00,0x01,0x00,0x18,0x18,0x00,0x00,0x00,
0x00,0x00,0x01,0x18,0x18,0x00,0x00,0x00,
0x00,0x00,0x00,0x19,0x18,0x00,0x00,0x00,
0x00,0x00,0x00,0x18,0x19,0x00,0x00,0x00,
0x00,0x00,0x00,0x18,0x18,0x01,0x00,0x00,
0x00,0x00,0x00,0x18,0x18,0x00,0x01,0x00,
0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x01,
0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x02,
0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x04,
0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x08,
0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x10,
0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x20,
0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x40,
0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x80,
0x00,0x00,0x00,0x18,0x18,0x00,0x80,0x00,
0x00,0x00,0x00,0x18,0x18,0x80,0x00,0x00,
0x00,0x00,0x00,0x18,0x98,0x00,0x00,0x00,
0x00,0x00,0x00,0x98,0x18,0x00,0x00,0x00,
0x00,0x00,0x80,0x18,0x18,0x00,0x00,0x00,
0x00,0x80,0x00,0x18,0x18,0x00,0x00,0x00,
0x80,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
0x40,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
0x20,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
0x10,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
0x08,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
0x04,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
0x02,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
发表于 2011-1-18 11:45:40 | 显示全部楼层
马老师,我打算挑战一下

不过我得用proteus仿真,以前没用过AVR,现弄,proteus也没学过……所以基本上是从头开始,proteus画图时间不算哈!
发表于 2011-1-18 11:47:10 | 显示全部楼层
回复【118楼】embeddev
  要是我做,还是用查表得笨办法,两个表格(每个表格28字节)分别决定pa,pc在某一时刻需要循环闪烁的那一位的状态,定时中断分为20ms和200ms(0.2s),20ms的那个定时中断用来切换常亮和需要循环亮的程序,200ms定时用来切换循环亮的状态位!
-----------------------------------------------------------------------

MCU无事可做,不如让它玩DELAY_nms(200),要改时间都不要重新计算TCNT,直接改DELAY_nms(?)不是更好简单?
发表于 2011-1-18 11:54:08 | 显示全部楼层
回复【119楼】xiaobendan 仲跻东

PC0复用按键电路怎样接?一直按着的话是不是会影响显示?
如果开关另外一端接地,那么PC口是不是只能接LED的阴极?如果是阳极,就要强输出,那么一直按键会不会烧掉这个线?
抱歉我没有那个实验板,瞎想的,哈哈。

-----------------------------------------------------------------------

我看pdf里面的意思是用PA和PB做LED屏扫描,PC0按键输入,不涉及按键复用的事儿;但是按照马老师给的图,确实是需要按键复用的。

复用的话,难度就大了很多,按键这里反而成了关键,这个我两小时肯定搞不定,看高手出马了。
发表于 2011-1-18 12:00:12 | 显示全部楼层
1小时 我想剩下两小时撰写文稿应该够了
五角场文秘职业技术学院

(原文件名:Capture.PNG)

点击此处下载 ourdev_611787MSJB13.rar(文件大小:52K) (原文件名:matrix_machao.rar)
发表于 2011-1-18 12:00:42 | 显示全部楼层
还有,跟马老师开个玩笑,我不会吸烟,该怎么办呢?要不,就吃15分钟零食好了。
发表于 2011-1-18 12:03:11 | 显示全部楼层
#define F_CPU 4000000UL // 4 MHz

#include <util/delay.h>
#include <avr/io.h>
#include <avr/interrupt.h>

unsigned char display[8]={0,0,0,0b00011000,0b00011000,0,0,0};
volatile unsigned char clock_count=0,clock_count_limit=4,count_flag=0,phase=0;
unsigned char calc_x,calc_y;

ISR(TIMER2_COMP_vect){ //50ms
        static key_history=0;
        clock_count++;
        if (clock_count>=clock_count_limit){
                clock_count=0;
                count_flag=1;
        }
        DDRC=0;
        if ((PINC&(1<<PC0))==0){
                if (key_history==0){
                        clock_count_limit+=2;
                        if (clock_count_limit>10) clock_count_limit=4;
                }
                key_history=1;
        }else{
                key_history=0;
        }
}

void calc_phase(void){
        if (phase<8) {
                calc_x=7-phase;
                calc_y=0;
        }else if(phase<8+6){
                calc_x=0;
                calc_y=phase-7;
        }else if(phase<8+6+8){
                calc_x=phase-14;
                calc_y=7;
        }else if(phase<8+6+8+6){
                calc_x=7;
                calc_y=28-phase;
        }
}

void rotate_matrix(void){
        unsigned char i,j;
        display[calc_y]&=~(1<<calc_x);
        phase++;
        if (phase>=8+6+8+6) phase=0;
        calc_phase();
        display[calc_y]|=(1<<calc_x);
}

int main (void){
        unsigned char i,j;
        DDRA=0xff;
        TCCR2=(1<<WGM21)|(0<<WGM20)|(0b111<<CS20);
        OCR2=194;
        TIMSK|=(1<<OCIE2);
        TIFR|=(1<<OCF2);
        sei();
        while(1){
                for (i=0;i<8;i++){
                        DDRC=1<<i;                       
                        PORTA=display;
                        _delay_ms(2);
                        PORTA=0;
                        if (count_flag){
                                count_flag=0;
                                rotate_matrix();
                        }
                }
                DDRC=0;
        }
}
发表于 2011-1-18 12:04:10 | 显示全部楼层
回复【124楼】xiaobendan 仲跻东
还有,跟马老师开个玩笑,我不会吸烟,该怎么办呢?要不,就吃15分钟零食好了。
-----------------------------------------------------------------------

楼上,你那个方法肯定不行的!仔细想想吧!没有考虑常亮的部分!
发表于 2011-1-18 12:09:24 | 显示全部楼层
回复【117楼】jackielau 九天
-----------------------------------------------------------------------
200ms一次确实会闪烁的

实际应该多少时间,在实际做的时候 调整一下就可以了,这只是一个估算而已,错了也没有关系,在定时器初始化的时候调整一下参数即可
发表于 2011-1-18 12:59:13 | 显示全部楼层
怎么会不行呢?把28个画面分别显示,不只是这种画面,任何一个画面都可以,同时亮2个灯,3个灯的流水灯都能做,随便什么画面好了,随便常亮多少个都行,总之随便显示什么内容都可以,只要更换那些数据表就可以了
不如这样,我把数据表做好,谁给做一个驱动程序吧,有条件的测试一下
发表于 2011-1-18 13:03:05 | 显示全部楼层
mark
发表于 2011-1-18 13:16:09 | 显示全部楼层
中间那四个点是不能同时亮的,只能扫描亮,当那个游动的点和中间四个点同列或者同行的时候就要出bug了:(
发表于 2011-1-18 13:32:44 | 显示全部楼层
我的方法效率比较低,但通用性比较好。

1.定时器1中断频率8kHz,ISR中,不断从显存读数据,扫描8列LED。

2.定时器1 ISR 每执行400次,就执行一次按键消抖程序,比较 上次按键状态 和 这次按键状态.
若同为按下,则修改时间间隔倍数X。( X=1表示时间间隔为0.2秒 )

3.定时器1 ISR 每执行1600X次,就执行一次扫描程序,根据上次的坐标,在显存中绘图。
需要个4个计数变量。加上显存等,总的内存占用最多30字节。

当然还可以用虚拟定时器,通过systick完成定时,这样就可以设定0.125ms的整数倍的时间间隔。
不过考虑到AVR处理能力有限,systick可能只能用1kHz,再高频率可能来不及处理。

话说AVR的汇编挺简单的,看两遍应该就能背下来,核心部分如果用汇编效率会高一些

代码晚上再传,下午要上课。
发表于 2011-1-18 13:46:22 | 显示全部楼层
马老师好~我也是大三的学生,这两天放假也没事做,看到这个题目就想试一试。上午十点看到题目,接着就准备搭板子,结果发现没有合适的电路,之前没用过点阵,就想用proteus模拟的,先是写了一段代码想熟悉一下点阵,结果发现模拟的时候延时太短会显示不正常,反复调试到十点四十左右吧,正式开始写代码,十二点半吃饭的时候题目一完成,一点继续把按键的功能加上了,一点半完成,中间边写边试耽误了一些时间,总时间也算是两个小时吧

以下是代码:

#include <intrins.h>
#include<STC12C5A.h>   

void Delay();
void Disp();

sbit Button=P1^7;

int x=0x80;
int y=0x80;       
int m=0;
int count=0;
int count2=2;
int mod=0;
int key=0;

void main()
{
    TMOD=0x01;
    TL0=0xCA;
    TH0=0x7D;
    TR0=1;
    ET0=1;
    EA=1;
    while(1)
    {
        while(1)
        {
            while(m==0)
            {
                Disp();
            }       
            m=0;
            x>>=1;       
            if(x==0)
            {       
                x=1;
                break;
            }
        }                       
       
        while(1)
        {
            y>>=1;          
            while(m==0)
            {
                Disp();           
            }
            m=0;
            if(y==1)break;
            }       
       
        while(1)
        {
            x<<=1;          
            while(m==0)
            {
                Disp();           
            }
            m=0;
            if(x==0x80)break;
        }       
       
        while(1)
        {
            y<<=1;          
            while(m==0)
            {
                 Disp();   
            }
            m=0;
            if(y==0x40)break;
        }       
        x=0x80;
        y=0x80;
    }
}

void tm0() interrupt 1 using 1
{
    TL0=0xCA;
    TH0=0x7D;
    if(count++==count2)
    {
        m=1;
        count=0;
    }
}

void Disp()
{
    P2=x;
    P3=~y;       
    Delay();
    P2=0x18;
    P3=~0x18;
    Delay();  
}
                         
void Delay10ms()
{
    int j,k;
    for(j=0;j<50;j++)
    {
        for(k=0;k<66;k++)
        {;}               
    }
}

void Delay()
{
    int a,b;
    for(a=0;a<100;a++)
    {
        for(b=0;b<8;b++)
        {
            if(key==0&&Button==0)
            {
                Delay10ms();
                if(Button==0)
                key=1;
            }
            else if(key==1&&Button==1)
                {
                    key=0;       
                    count2+=4;
                    if(count2>10)
                    {          
                        count=0;
                        count2=2;
                    }
                }
        }
    }
}


运行结果:(由于模拟的时候频率太高会显示不正常,没办法只好降低刷新频率,显示会有些闪,如果放到实际电路的话减小延时应该不会闪的)
论坛传gif格式的不能播放么...那就当附件传吧.......
点击此处下载 ourdev_611805KVVJGE.rar(文件大小:43K) (原文件名:clip0022.rar)
发表于 2011-1-18 13:48:21 | 显示全部楼层
废话少说,贴上程序才是王道
发表于 2011-1-18 13:50:58 | 显示全部楼层
const unsigned char table[] = {0x01,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
                                                                        0x00,0x01,0x00,0x18,0x18,0x00,0x00,0x00,
                                                                        0x00,0x00,0x01,0x18,0x18,0x00,0x00,0x00,
                                                                        0x00,0x00,0x00,0x19,0x18,0x00,0x00,0x00,
                                                                        0x00,0x00,0x00,0x18,0x19,0x00,0x00,0x00,
                                                                        0x00,0x00,0x00,0x18,0x18,0x01,0x00,0x00,
                                                                        0x00,0x00,0x00,0x18,0x18,0x00,0x01,0x00,
                                                                        0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x01,
                                                                        0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x02,
                                                                        0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x04,
                                                                        0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x08,
                                                                        0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x10,
                                                                        0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x20,
                                                                        0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x40,
                                                                        0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x80,
                                                                        0x00,0x00,0x00,0x18,0x18,0x00,0x80,0x00,
                                                                        0x00,0x00,0x00,0x18,0x18,0x80,0x00,0x00,
                                                                        0x00,0x00,0x00,0x18,0x98,0x00,0x00,0x00,
                                                                        0x00,0x00,0x00,0x98,0x18,0x00,0x00,0x00,
                                                                        0x00,0x00,0x80,0x18,0x18,0x00,0x00,0x00,
                                                                        0x00,0x80,0x00,0x18,0x18,0x00,0x00,0x00,
                                                                        0x80,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
                                                                        0x40,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
                                                                        0x20,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
                                                                        0x10,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
                                                                        0x08,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
                                                                        0x04,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
                                                                        0x02,0x00,0x00,0x18,0x18,0x00,0x00,0x00,
                                                                        };
const uchar dig[] = {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};
uchar hang,huamian;
uchar dsq;
int main(void){
while(1){
        SMCR |= (1<<SE);
        asm("sleep");
        }
}

ISR(SIG_OVERFLOW0){
SMCR &= ~(1<<SE);
TCNT0 = 0x06; //set count  自己修改看看多少数字更好
hang++;
if(hang > 7)hang = 0;
PORTC = 0xff;
PORTB = table[hang+huamian*8];
PORTC = dig[hang];
dsq++;
if(dsq>=100){//改变速度,就改变这个100吧
        dsq = 0;
        huamian++;
        if(huamian>27)huamian = 0;
        }
}
//修改原因:加入睡眠代码
发表于 2011-1-18 13:53:10 | 显示全部楼层
有硬件的试试上面的代码,要改一下端口,我好像打错了
发表于 2011-1-18 14:07:26 | 显示全部楼层
回复【135楼】xiaobendan 仲跻东
-----------------------------------------------------------------------

你好,你那个160*80的液晶是带触摸屏的吗?
发表于 2011-1-18 14:08:29 | 显示全部楼层
半夜丢块板砖这下出来好多白花花的石头啊!
发表于 2011-1-18 14:16:39 | 显示全部楼层
xiaobendan 仲跻东的思路不错
发表于 2011-1-18 14:21:16 | 显示全部楼层
这和学校有什么关系!
发表于 2011-1-18 14:23:12 | 显示全部楼层
回复【138楼】dz46316740
-----------------------------------------------------------------------

没有考虑按键!
发表于 2011-1-18 14:30:50 | 显示全部楼层
回复【楼主位】machao  
-----------------------------------------------------------------------

从没用过AVR,也来试一下。顺便请教一个问题,
发表于 2011-1-18 14:31:48 | 显示全部楼层
马老师的按键可能是个BUG
发表于 2011-1-18 14:40:08 | 显示全部楼层
回复【楼主位】machao  
-----------------------------------------------------------------------

从没用过AVR,我也来试一下,感觉和之前学的有点不一样。请教大家一个问题,就是我用的编译器是GCC,在这个编译器里面怎么定义一个标志位呢?谢谢!!
我是广东交通职业技术学院的学生,还没毕业。
以下是我写的代码,一个小时。思路很清晰,主要是查了一下GCC的一些相关知识,如数组。用数组感觉是最简单的,不过位操作应该是简洁很多。呵呵,大家别笑哦!

/***********************************
**************Atmega16**************
***********************************/
#include "avr/io.h"
#include "avr/interrupt.h"
#include "avr/pgmspace.h"
#define uchar unsigned char
#define uint  unsigned int
volatile uchar i,j,k,temp,flag,flag1,tempA,tempB,counter,time_counter;

const prog_uchar pa_table[]={0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80};
const prog_uchar pb_table[]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
void interrupt_init(void)
{
        cli();
        TCCR1B=0x00;
        TIMSK|=0x04;
        TCNT1=65536-4000;
        TCCR1B=0x01;
        sei();
}
void port_init(void)
{
        j=0;
        i=0;
        k=1;
        temp=0;
        tempA=8;
        tempB=0;
        flag=0;
        flag1=0;
        counter=0;
        time_counter=50;
        DDRB=0xff;
        DDRA=0xff;
        DDRC=0x00;
        PORTA=0xff;
        PORTB=0x00;
}
void display_1()
{
        PORTA=pgm_read_byte(&pa_table[3])|pgm_read_byte(&pa_table[4]);
        PORTB=pgm_read_byte(&pb_table[3])&pgm_read_byte(&pb_table[4]);
}

void display_2()
{       
        switch(counter)
                {
                        case 0:
                                if(flag==1)
                                        {
                                                flag=0;
                                                tempB=0;
                                                tempA--;
                                                if(tempA==0)
                                                        {
                                                                counter=1;
                                                        }
                                                break;                                       
                                        }
                                else break;
                        case 1:
                                if(flag==1)
                                        {
                                                flag=0;
                                                tempA=0;
                                                tempB++;
                                                if(tempB==7)
                                                        {
                                                                counter=2;
                                                        }
                                                break;
                                        }
                                else break;
                        case 2:
                                if(flag==1)
                                        {
                                                flag=0;
                                                tempB=7;
                                                tempA++;
                                                if(tempA==7)
                                                        {
                                                                counter=3;
                                                        }
                                                break;
                                        }
                                else break;
                        case 3:
                                if(flag==1)
                                        {
                                                flag=0;
                                                tempA=7;
                                                tempB--;
                                                if(tempB==0)
                                                        {
                                                                counter=0;
                                                        }
                                                break;
                                        }
                                else break;
                }
        PORTA=pgm_read_byte(&pa_table[tempA]);
        PORTB=pgm_read_byte(&pb_table[tempB]);
        if(tempA==3||tempA==4)
                {
                        PORTB=pgm_read_byte(&pb_table[tempA])&pgm_read_byte(&pb_table[3])&pgm_read_byte(&pb_table[4]);
                }
}
void key()
{
        if((PINC&0x01)==0x01)flag1=0;
        if(((PINC&0x01)==0)&&(flag1==0))
                {
                        i++;
                        if(i==20)
                                {
                                        i=0;
                                        flag1=1;
                                        k++;
                                        if(k==6)k=1;
                                        time_counter=50*k;
                                }
                }
        else
                {
                        i=0;
                }
}
int main(void)
{
        port_init();
        interrupt_init();
        while(1)
                {
                        key();
                        if(j%2)
                        display_1();
                        else display_2();
                }
}
ISR(TIMER1_OVF_vect)
{
        TCNT1=65536-4000;
        j++;
        if(j==time_counter)
                {
                        j=0;
                        flag=1;
                }
}

(原文件名:AVR_1.png)
发表于 2011-1-18 15:06:20 | 显示全部楼层
打个手印先   

AVR 不会

  但用C (51)写还是会点   

    有空写一个试试


  初步思路

  1.定时时间源 设定

  

  2.核心代码

      

        给外围 的  灯编码(8*2+(8-2)*2)= 28个 (直接IO 或整个IO组都行)

            灯编码组 1.2.3.4.5.6.7.8.9.................

              常亮的4个灯(写成一个函数 x4)

         变量: AAA =0
         时间要求每到 T   AAA++
         
          编写 比较语句  AAA 对应  (先清上2组IO)灯编码组 (跟上 常亮的4个灯)
           
          if (AAA > 28 );aaa=1


         穷法子~~~~ 呵呵
发表于 2011-1-18 15:15:01 | 显示全部楼层
回136楼,是带触摸的,四线电阻屏
我那些代码如果把表格放到PROG_MEM中,使用的RAM就小很多了
刚才用KEIL式了一下,RAM使用12字节,CODE是376字节(没有使用睡眠),应该是比较小的了,除去表格所占用的空间,实际的程序没多少了,而且CPU大部分都是睡眠的
发表于 2011-1-18 15:16:59 | 显示全部楼层
长期潜水,今天看到马老师出考题,出来冒个泡,这是我自己写的。拙作而已,希望起抛砖引玉的作用。电脑上没装IAR,只有KEIL C,那就用51做吧。反正用C语言的都差不多。现在贴上程序跟仿真。
程序代码:
#include <reg52.h>
#include "MyType.h"

#define PORTA P2
#define PORTB P3
#define PORTC P1
sbit         key = P1^7;        //按键
//全局变量

uint8 pattern;
uint8 keyflag ;//按键按下标志,按下:1, 释放:2,不按:0.
uint8 keydelay;
uint8 keyvalue;
//函数定义
void display(uint8 dat1,uint8 dat2);
void delay(void);
void keyscan(void);
void main(void)
{
        uint8 dispdat;

        TMOD = 0x01;
        pattern = 1;
        dispdat = 0x80;
        keyflag = 0;
        keyvalue = 2;
        while(1)
        {
                switch(pattern)
                {
                        case 1:
                                 display(dispdat,0x80);
                                dispdat>>=1;
                                if(dispdat==0)
                                {
                                        dispdat = 0x40;
                                        pattern = 2;
                                }
                                break;

                        case 2:
                                display(0x01,dispdat);
                                dispdat>>=1;
                                if(dispdat==0)
                                {
                                         dispdat = 0x02;
                                        pattern = 3;
                                }
                                break;
                        case 3:
                                display(dispdat,0x01);
                                dispdat<<=1;
                                if(dispdat==0x00)
                                {
                                        dispdat = 0x02;
                                        pattern = 4;
                                }
                                break;
                        case 4:
                                 display(0x80,dispdat);
                                dispdat<<=1;
                                if(dispdat==0x00)
                                {
                                        dispdat = 0x80;
                                        pattern = 1;
                                }
                                break;
                          default:
                                break;       
                 }
        }
}

/********************************************************************
* 显示子程序
*********************************************************************/
void display(uint8 dat1,uint8 dat2)
{
        uint16 count;
        for(count=0; count<25*keyvalue; count++)
        {
        PORTA = dat1;
        PORTB = ~dat2;
        delay();
        PORTB = 0xff;
        PORTA = 0x18;
        PORTB = ~0x18;
        delay();
        PORTB = 0xff;
        }
}

/********************************************************************
* 2mS延时
*********************************************************************/
void delay(void)
{
        TH0 = (65536-2000)/256;
        TL0 = (65536-2000)%256;
        TR0 = 1;
        while(TF0==0);
        TF0 = 0;
        TR0 = 0;
        keyscan();               
}

void keyscan(void)
{
        if(key==0 && keyflag==0)//按下
        {
                 keyflag = 1;
        }
        else if(key==0 && keyflag==1)
        {
                keydelay+=2;
        }
        if(keyflag==1 && key==1)//释放
        {
                keyflag = 2;
                keydelay = 0;
        }
   else if(keyflag==2 && key==1)
        {
                keydelay += 2;
                if(keydelay>20)
                        {
                                keyvalue += 4;
                                if(keyvalue>10)//大于1s
                                keyvalue = 2;
                                keyflag = 0;
                                PORTC = keyvalue|0x80;        //键值在P1口低8位显示
                        }
        }
}
仿真可以,实际硬件得增加驱动。
ps:本人是福州大学 自动化专业大四学生,母校不是很出名,每次出去参加比赛跟人家说是福州大学的,都会问你福州大学是哪里的,郁闷!呵呵:)
发表于 2011-1-18 15:19:01 | 显示全部楼层
附件:

仿真图 (原文件名:图1.JPG)

代码与仿真图ourdev_611835RIPJ7R.rar(文件大小:31K) (原文件名:代码及仿真.rar)
发表于 2011-1-18 15:20:10 | 显示全部楼层
mark and learn
 楼主| 发表于 2011-1-18 16:48:25 | 显示全部楼层
回复【122楼】McDeggy 再见,列宁
回复【119楼】xiaobendan 仲跻东
pc0复用按键电路怎样接?一直按着的话是不是会影响显示?  
如果开关另外一端接地,那么pc口是不是只能接led的阴极?如果是阳极,就要强输出,那么一直按键会不会烧掉这个线?  
抱歉我没有那个实验板,瞎想的,哈哈。
-----------------------------------------------------------------------
我看pdf里面的意思是用pa和pb做led屏扫描,pc0按键输入,不涉及按键复用的事儿;但是按照马老师给的图,确实是需要按键复用的。
复用的话,难度就大了很多,按键这里反而成了关键,这个我两小时肯定搞不定,看高手出马了。
-----------------------------------------------------------------------

原发的是一个草稿,现在已经换上新的,主要是配合上面贴图。

原来是:PA、PB控制8*8LED,要加按键,使用PORTC.0口

操作测试中,为了下载方便,改成:PA、PC控制8*8LED,与贴图相同。要加按键,使用PORTD.0口

我的挑战不要求做按键,只要做出第1部分。
发表于 2011-1-18 16:54:16 | 显示全部楼层
回复【150楼】machao  
-----------------------------------------------------------------------

复用也不是什么大问题,按键用一个大电阻连接到PC0即可.这样电平冲突不会导致过流.同时按键按下的电流足够小,led亮不了.
仿真的led还是不太靠谱.
发表于 2011-1-18 16:56:54 | 显示全部楼层
点击此处下载 ourdev_611883M4SPYK.rar(文件大小:42K) (原文件名:program_cvavr.rar)


(原文件名:US.jpg)

美刀

加了5x8点阵字体显示,不错,呵呵
发表于 2011-1-18 17:01:59 | 显示全部楼层
要是我做:一定会把扫描放在时间中断里的,频率200-400HZ左右。主程序负责修改显示表及其它事情
发表于 2011-1-18 17:04:24 | 显示全部楼层
。。。
发表于 2011-1-18 17:10:20 | 显示全部楼层
回复【153楼】adcr 老稻
-----------------------------------------------------------------------

做产品肯定是要放到中断里的,测试嘛就算了

不能象
Send("AT");
Delay(30);
Recv("xxx");
发表于 2011-1-18 17:23:54 | 显示全部楼层
接着顶
 楼主| 发表于 2011-1-18 17:24:40 | 显示全部楼层
考试12:30结束,下面是最后的两个考题,大家拍砖吧。在上面的所有代码,包括思路,都还有差距。

努力吧,希望能出现超越我的好代码。不要考虑什么OS、什么加汇编的,就是最基本的DD。

===========================================================================
四、英文阅读、翻译与理解(20分)
下面是M16数据手册中对T/C0工作模式给出的一段说明,请在阅读和理解的基础上,完成题目要求。

Reset and Interrupt Handling

-----
The AVR provides several different interrupt sources. These interrupts and the separate reset vector each have a separate program vector in the program memory space. All interrupts are assigned individual enable bits which must be written logic one together with the Global Interrupt Enable bit in the Status Register in order to enable the interrupt.
------

The lowest addresses in the program memory space are by default defined as the Reset and Interrupt Vectors. The complete list of vectors is shown in “Interrupts” on page 45. The list also determines the priority levels of the different interrupts. The lower the address the higher is the priority level. RESET has the highest priority, and next is INT0–the External Interrupt Request 0.
When an interrupt occurs, the Global Interrupt Enable I-bit is cleared and all interrupts are disabled. The user software can write logic one to the I-bit to enable nested interrupts. All enabled interrupts can then interrupt the current interrupt routine. The I-bit is automatically set when a Return from Interrupt instruction – RETI – is executed.
The interrupt execution response for all the enabled AVR interrupts is four clock cycles minimum. After four clock cycles the program vector address for the actual interrupt handling routine is executed. During this four clock cycle period, the Program Counter is pushed onto the Stack. The vector is normally a jump to the interrupt routine, and this jump takes three clock cycles.
A return from an interrupt handling routine takes four clock cycles. During these four clock cycles, the Program Counter (two bytes) is popped back from the Stack, the Stack Pointer is incremented by two, and the I-bit in SREG is set.

1.        将斜体部分整理成通顺易懂的中文解释说明(4分)(第一节)

2.        AVR中各个中断的优先级是如何排定的?中断优先级有何作用?(4分)

3.        AVR从响应一个中断开始,最少需要多少时间后,才能执行该中断服务程序的第一条指令?(4分)

4.        AVR在响应中断的过程中,硬件上自动做了那些处理?哪些寄存器以及内存单元将发生变化,发生什么变化?说明这些处理的作用。(4分)

5.        缺省情况下,AVR在执行某个中断服务时,会被比其优先级高的中断打断吗?为什么会或不会?说明原因。(4分)


五、简单系统的设计、调试与实现(40分)
    一系统中ATmage16的PORTA口和PORTC口与8*8 LED点阵的连接如左图所示。ATmage16的系统时钟频率为4MHz,配合下面的代码在8*8 LED点阵数码管上显示了一个环形流水灯图案,图案中流水灯为1个点亮的LED,沿着最外圈围绕中间常亮的4个点按顺时针方向运动,环形路径为右图中所示的灰色部分。
            
实现该功能的程序源代码如下:

#include <mega16.h>
char dis_buff[8] = {0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00};
char j=0,line=0,point;
char time_count;
bit t_xxms_ok;

void led8x8(void) {
    static char i = 0;
    PORTC = 0xff;
    PORTA = dis_buff;
    if (i = = line)  PORTA = dis_buff | point;
    PORTC = ~(1<<i);
    if(++i>7) i = 0;
}

interrupt [TIM2_COMP] void timer2_comp_isr(void)
{
    led8x8();
    if(++time_count >= 250)
    {time_count = 0; t_xxms_ok = 1;}
}


void main(void)
{
    PORTA=0x00;
    DDRA=0xFF;                        // PA口设置为输出工作方式
    PORTC=0xFF;
    DDRC=0xFF;                        // PC口设置为输出工作方式

    TCCR2=0x0B;                // T/C2工作在CTC方式,32分频       
    OCR2=0x7C;
    TIMSK=0x80;                        // 允许T/C2比较匹配中断
    #asm("sei")                        // 允许全局中断

    while (1)
    {        if(t_xxms_ok)
        {           t_xxms_ok = 0;
            
            if (j<8)
            {   line = 0; point = 0x80>>j;}
            if (j>=8 && j<14)
            {   line++; point = 0x01;}
            if(j>=14 && j< 22)
            {   line = 7; point = 0x01<<(j-14);}
            if (j>=22 && j< 28)
            {   line-- ; point = 0x80;}
            
            if(++j > 27) j = 0;
        }
    }
}

请根据连接图和代码回答下面的问题。

1.填空(10分)
    由于系统时钟为4M,T/C2工作在CTC方式,预分频系数为32,T/C2比较寄存器的值为0x7C,所以每隔(   )时间间隔就产生一次比较匹配中断。每执行一次中断服务将调用一次扫描显示函数,每执行一次扫描显示函数将点亮(   )(行 or 列)上的LED。因此8*8点阵全部扫描一遍,形成1帧图案需要(      )时间,由于在1秒内一共_产生了(     )帧图案,所以整个流水灯的效果(     )   (会 or 不会)闪烁。

2.回答下列问题(25分)
(1)根据现在的代码分析,系统上电后第1个点亮的流水灯是哪个位置的LED?(2分)

(2)流水灯每隔多少时间移动到下个位置?(2分)

(3)如果要调整流水灯的移动速度,比如将速度提高1倍,应该如何改动代码?(2分)

(4)如果需要将LED调整为1秒移动1次,又应该如何修改代码?(3分)

(5)扫描显示函数中的PORTC = 0xff;语句起到什么作用?并说明为什么能起到你所说的作用?(3分)

(6)分别说明代码中3个全局变量j、line、point的作用,它们之间的关系,以及如何与扫描显示函数配合,形成流水灯的。(4分)

(7)如果仅将T/C2的分频系数设置成256,代码其它部分不做任何改动,程序执行后将出现什么情况?(3分)

(8)仅需要改动led8x8函数代码段中的一句语句就能将流水灯的运动方向改成逆时钟方向运动,请分析原代码,并给出改动后的语句(2分)

(9)当然也可以改动多句语句,将流水灯的运动方向改成逆时钟方向运动,请分析原代码,并给出改动后的语句(4分)


3.对本题给出的代码不做任何改变,分析仅改变硬件连接后所产生的实际效果。
    将PA口与8*8LED点阵的8个连接线对调,(既原接PA0改接PA7、既原接PA1改接PA6、既原接PA2改接PA5、……)产生的实际效果有何变化?请具体说明。(5分)

点击此处下载PDF文档 ourdev_611902GT0A6H.pdf(文件大小:291K) (原文件名:文档 1.pdf)

==============================================================================
学生上课讲过8*8LED使用的例子,平时实验中有做8*8LED的练习。动手考试就是这个题(50%人做),最后笔试还是这个题。
发表于 2011-1-18 17:26:08 | 显示全部楼层
没意思,还是自己删掉。
发表于 2011-1-18 17:35:47 | 显示全部楼层
看到楼上的学生,我只有跳楼的份了,太强悍了
发表于 2011-1-18 17:45:40 | 显示全部楼层
希望马老师最后公布下自己的思路!
 楼主| 发表于 2011-1-18 17:50:22 | 显示全部楼层
要贴代码的,先看157楼内容

如果感觉可以过招的,再贴上。

或者就拍砖。

或者就做题,讨论吧。

单片机是死的东西,只要需要,可以精确的计算到一个CLK。这里我们采用一般的精度ms。如果这个概念都没有,让你设计一个导_弹控制系统?自己把自己炸了。

我考察和要求学生掌握的,都是最基本的方法和思路,如果大家没有建立,那么就做不出真正的好设计和产品。

如果你感到缺乏,需要学习,最重要的是喜欢,和将来准备从事这个行业的技术工作,那么不管你是学51,还是使用STM32,是北大的还是华师大的,建议你买我的教材,认真学习。不会上当的。

本学期,我的教材内容只是讲了一半,只是一个入门。

对,这个仅仅是评判是否入门的一道题目。
 楼主| 发表于 2011-1-18 17:53:00 | 显示全部楼层
回复【160楼】embeddev
  希望马老师最后公布下自己的思路!
-----------------------------------------------------------------------

157楼。是代码,不是思路,思路就是你做题后去体会。
发表于 2011-1-18 17:56:57 | 显示全部楼层
没注意代码都贴出来了 :)
 楼主| 发表于 2011-1-18 19:04:34 | 显示全部楼层
回复【29楼】double_kill
哈哈,看我的代码,高级短小
#include &lt;mega16.h&gt;
flash unsigned char led_7[28]={0x80,0x40,0x20,0x10,0x08,0x04,0x02,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,0x80,0x80,0x80,0x80,0x80,0x80};  
flash unsigned char position[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
flash unsigned char pos[28]={0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f,0x7......
-----------------------------------------------------------------------

对比过我的代码后,你对你的代码有新评估吗?
发表于 2011-1-18 19:07:08 | 显示全部楼层
刚刚看了下161楼。还是不找拍了
 楼主| 发表于 2011-1-18 19:08:09 | 显示全部楼层
回复【51楼】wajlh
最简单的办法是把28帧的数据算出来做成表格,然后程序里把每帧的数据显示你要求的时间就可以。什么坐标计算的都是浮云
-----------------------------------------------------------------------

你说的也是浮云。
发表于 2011-1-18 19:29:40 | 显示全部楼层
刚才把马老师的代码移植到GCC上面编译,长度只有378字节,比我那个短很多,的确是厉害!
 楼主| 发表于 2011-1-18 19:31:04 | 显示全部楼层
回复【165楼】homox17 碧海蓝天
#include"reg51.h"
#define uchar unsigned char  
#define uint unsigned int  
#define key_line p2^1
uchar timer1=0;
uchar motion_point=0;
uchar timer_max=20;
uchar key_state=0;
uchar disp_tab[]={0,1,2,3,4,5,6,7,15,23,31,39,47,55,63,62,61,60,59,58,57,56,48,40,32,24,16,8};
void set_bit(uchar m)
{
    p0|=0x01&lt;&lt;m;
}
void clr_bit(uchar m)
{
    p0&amp;=~(0x01&lt;&lt;m);
}
void stati......
-----------------------------------------------------------------------

这是我发现一些人的通病。


    每个班级或年级中都会有几个比较聪明的学生,的确他们的理解能力、反映能力、动手能力在班级中是数一数二的,甚至某些方面的能力超过其导师或教授(其实现在什么行业都有大量不合格的东西,或者假货,教师也不例外)。因此,总认为自己了不起,自己的思路或方法肯定好。不愿意先看看、仔细分析一下别人的东西是否有长处,是否比自己的好。非常固执。
   
    分析下来,可能他根本没有机会得到真正的导师,也就是说在这个领域或专业上的“高手”的指教。好马要用响鞭,响鼓是需要重捶敲的。本应可以学的更好,学的更快的料,就这样逐渐的费掉了。可惜。

    请你先看我157楼的代码,看懂后再做评价。
发表于 2011-1-18 19:36:45 | 显示全部楼层
答题——有错误的地方,欢迎马老师和其他朋友拍砖。

Reset and Interrupt Handling

-----
The AVR provides several different interrupt sources. These interrupts and the separate reset vector each have a separate program vector in the program memory space. All interrupts are assigned individual enable bits which must be written logic one together with the Global Interrupt Enable bit in the Status Register in order to enable the interrupt.
------

The lowest addresses in the program memory space are by default defined as the Reset and Interrupt Vectors. The complete list of vectors is shown in “Interrupts” on page 45. The list also determines the priority levels of the different interrupts. The lower the address the higher is the priority level. RESET has the highest priority, and next is INT0–the External Interrupt Request 0.
When an interrupt occurs, the Global Interrupt Enable I-bit is cleared and all interrupts are disabled. The user software can write logic one to the I-bit to enable nested interrupts. All enabled interrupts can then interrupt the current interrupt routine. The I-bit is automatically set when a Return from Interrupt instruction – RETI – is executed.
The interrupt execution response for all the enabled AVR interrupts is four clock cycles minimum. After four clock cycles the program vector address for the actual interrupt handling routine is executed. During this four clock cycle period, the Program Counter is pushed onto the Stack. The vector is normally a jump to the interrupt routine, and this jump takes three clock cycles.  
A return from an interrupt handling routine takes four clock cycles. During these four clock cycles, the Program Counter (two bytes) is popped back from the Stack, the Stack Pointer is incremented by two, and the I-bit in SREG is set.

复位及中断处理

AVR提供了多个不同的中断源。在程序存储空间中,这些中断及各自的复位向量都有独立的程序向量。AVR给所有的中断源分配了各自独立的使能位,要使用这些中断,必须对其对应的中断使能位写逻辑“1”,而且要置位AVR状态寄存器的全局中断使能位。

默认情况下,AVR程序存储器中的起始地址被定义为中断和复位向量。完整的向量表描述在“中断”一章(45页)。向量表也决定了不同中断的优先级。程序存储器中,中断向量所在地址越小,其中断优先级越高。复位中断拥有最高的优先级,其次是INT0——0号外部中断请求。
当中断产生时,全局中断使能I位被清零,因此,其他的所有中断将被禁止。用户可以通过软件方式将中断使能位I位置1以实现中断嵌套,这样一来,所有使能的中断都可以打破当前的中断程序。注意:当中断程序返回指令(RETI)产生时,中断使能位(I比特位)被自动置位。
对于所有AVR的中断而言,中断产生时的“响应时间”至少需要4个时钟周期。在4个时钟周期以后,程序指针才会跳转到中断向量的地址以执行中断程序。在这4个时钟周期里,程序计数器(PC)被压入堆栈,随后,程序指针会自动跳转到对应的中断向量程序空间,这个跳转时间约为3个时钟周期。
要从中断处理程序中返回,也需要4个时钟周期。在这4个时钟周期里,程序计数器的内容(两个字节)从堆栈中弹出,堆栈指针+2,同时,全局中断使能位I比特位被置位。

1. 将斜体部分整理成通顺易懂的中文解释说明(4分)(第一节)

见上文

2. AVR中各个中断的优先级是如何排定的?中断优先级有何作用?(4分)

AVR中各个中断的优先级是根据其中断向量在程序存储器中的地址大小所决定的——地址越小,优先级越高。中断优先级能保证重要的中断不会被其他中断打断,否则,很可能导致程序紊乱。

3. AVR从响应一个中断开始,最少需要多少时间后,才能执行该中断服务程序的第一条指令?(4分)

响应中断的时间至少需要4个时钟周期,程序指针跳转到中断向量程序空间需要约3个时钟周期,即总共需要7个时钟周期才能执行该中断服务程序的第一条指令。

4. AVR在响应中断的过程中,硬件上自动做了那些处理?哪些寄存器以及内存单元将发生变化,发生什么变化?说明这些处理的作用。(4分)

AVR在响应中断时,硬件上做的处理有:
a.全局中断使能标志位I位被清零(状态寄存器SREG发生变化——I位变为0,以禁止其他中断);
b.将当前程序计数器的内容压入堆栈(堆栈内存单元中的内容发生变化,堆栈指针SP-2,俗称现场保护,记忆当前正在处理的程序的位置);
c.程序指针指向中断向量所在的地址(程序计数器中的内容发生变化,以执行中断向量)

5. 缺省情况下,AVR在执行某个中断服务时,会被比其优先级高的中断打断吗?为什么会或不会?说明原因。(4分)

默认情况下,AVR在执行某个中断服务时,不会被比其优先级高的中断打断。因为在缺省设置下,AVR执行中断服务时,会将全局中断允许位——I位清零,此时AVR是不会响应其他中断的。
这里涉及到对优先级概念的理解:所谓中断优先级的“优先”是指优先响应,即多个中断产生时,AVR将首先响应优先级最高的中断请求。而不是指优先级高的中断可以打破优先级低的中断。
 楼主| 发表于 2011-1-18 19:38:01 | 显示全部楼层
回复【167楼】xiaobendan 仲跻东
刚才把马老师的代码移植到gcc上面编译,长度只有378字节,比我那个短很多,的确是厉害!
-----------------------------------------------------------------------

不仅仅是代码长度方面占优吧。
发表于 2011-1-18 20:05:23 | 显示全部楼层
晕。。没有看到161楼的留言。。。丢丑了,我也不是那么固执,没有准备要过招,发出来只是想请马老师指导我提高下,我个人还是很喜欢编程,不过一直都在做硬件方面的东西。。。
发表于 2011-1-18 20:14:19 | 显示全部楼层
再加一个按键吧,比如按一下就变逆时针,再按一下恢复顺时针
发表于 2011-1-18 20:30:34 | 显示全部楼层
终于看 明白马老师 的程序  一额汗
 楼主| 发表于 2011-1-18 20:56:15 | 显示全部楼层
回复【173楼】gdmfq 小马哥
终于看 明白马老师 的程序  一额汗
-----------------------------------------------------------------------

有话明讲,我等着被拍砖。要不您来个更好点的?
发表于 2011-1-18 20:57:33 | 显示全部楼层
菜鸟来学习····
发表于 2011-1-18 21:10:38 | 显示全部楼层
拍:
Program:     352 bytes (2.1% Full)
(.text + .data + .bootloader)

#define F_CPU 4000000UL // 4 MHz

#include <util/delay.h>
#include <avr/io.h>

volatile unsigned char k,pa,pc;
volatile unsigned char* port_pointer;
int main (void){
    unsigned char i,j;       
    DDRA=0xff;
    DDRC=0xff;
    PORTA=1;
    PORTC=~(1<<0);       
    while(1){               
        for (i=0;i<7;i++){
            if (j&1){
                port_pointer=&PORTA;
            }else{
                port_pointer=&PORTC;
            }               
            if ((j&2)==0){        //编译器未优化,此为循环移位
                *port_pointer=(*port_pointer>>(8-1))|(*port_pointer<<1);
            }else{
                *port_pointer=(*port_pointer>>1)|(*port_pointer<<(8-1));
            }                       
            pa=PORTA;        //使用push pop更佳
            pc=PORTC;
            for (k=0;k<4;k++){               
                PORTA=0;
                if (k&1){
                    PORTA=pa;
                    PORTC=pc;
                }else{
                    PORTA=0b00011000;
                    PORTC=~0b00011000;                       
                }       
                DDRA=0xff;
                _delay_ms(50);        //proteus不响应快扫,故改慢,增大k的终值减少延时结果相同而且不闪
                DDRA=0x00;
            }
        }
        j++;
   
    }
}


点击此处下载 ourdev_611980XEWLA4.txt(文件大小:912字节) (原文件名:matrix_machao - Copy.txt)
发表于 2011-1-18 21:14:09 | 显示全部楼层
上段不完整的……轮询的……看了老师的汗啊差距……我就写成这样就花了1个小时……贴出来希望不要吃加号……
#define ROW 8
int main()
{
  RCC_Config();//设置时钟到4M
  GPIO_Config();//端口设置
  uint32_t ms_base=4000/8;
  uint32_t CurTime;//当前时间
  uint32_t Scan_Row=0,Scan_Time=1,Scan_NextTime=0;//扫描列,扫描时间,
  uint32_t Disp=0,Disp_Time=200,Disp_NextTime=0;//0.2S
  uint32_t Key=0,Key_Time=10,Key_NextTime=0;
  SysTick_Config();//计时器4M/8,RELOAD=0xFFFFFF
  while(1)
  {
    CurTime=SysTick->Val;
    if(CurTime<Scan_NextTime)
    {
      Scan_Row  ;
      if(Scan_Row>=ROW)Scan_Row=0;
      disp(Scan_Row);//扫描
      Scan_NextTime=(CurTime-Scan_Time*ms_base)&0xFFFFFF;
    }
    if(CurTime<Key_NextTime)
    {
      if(readKey())
      {
        if(Key)Key=2;//键已按下,可以使用key  代表键按下的时间
        //if(key>100)//长按1秒
        else Key=1;//有键按下?
      }
      else
      {
        if(Key==2)//按下的键已放开
        {
          Disp_Time =400;
          if (Disp_Time>1000)Disp_Time=200;
        }
        Key=0;
      }
      Key_NextTime=(CurTime-Key_Time*ms_base)&0xFFFFFF;
    }
    if(CurTime<Disp_NextTime)
    {
      Disp  ;
      if(Disp>27)Disp=0;//一圈28点
      DispArr(Disp);//调整显示的数据
      Disp_NextTime=(CurTime-Disp_Time*ms_base)&0xFFFFFF;
    }
  }
}
发表于 2011-1-18 21:49:43 | 显示全部楼层
Program:     336 bytes (2.1% Full)
(.text + .data + .bootloader)

#define F_CPU 4000000UL // 4 MHz

#include <util/delay.h>
#include <avr/io.h>

volatile unsigned char k,pa,pc;
volatile unsigned char* port_pointer,port_xor;
int main (void){
    unsigned char i,j;       
    DDRA=0xff;
    DDRC=0xff;
    PORTA=1;
    PORTC=~(1<<0);       
    port_pointer=&PORTC;
    port_xor=(&PORTA);
    port_xor^=(unsigned char)port_pointer;
    while(1){               
        for (i=0;i<7;i++){       
            if ((j&2)==0){        //编译器未优化,此为循环移位
                *port_pointer=(*port_pointer>>(8-1))|(*port_pointer<<1);
            }else{
                *port_pointer=(*port_pointer>>1)|(*port_pointer<<(8-1));
            }                       
            pa=PORTA^0b00011000;
            pc=PORTC^(~0b00011000);
            for (k=0;k<4;k++){               
                PORTA^=pa;
                PORTC^=pc;       
                DDRA=0xff;
                _delay_ms(50);        //proteus不响应快扫,故改慢,增大k的终值减少延时结果相同而且不闪
                DDRA=0x00;
            }
        }
        j++;
        port_pointer=(unsigned char)(port_xor)^(unsigned char)port_pointer;
    }
}
 楼主| 发表于 2011-1-18 22:23:35 | 显示全部楼层
回复【178楼】iamseer
program:     336 bytes (2.1% full)
(.text + .data + .bootloader)
#define f_cpu 4000000ul // 4 mhz
#include &lt;util/delay.h&gt;
#include &lt;avr/io.h&gt;
volatile unsigned char k,pa,pc;
volatile unsigned char* port_pointer,port_xor;
int main (void){
    unsigned char i,j;
    ddra=0xff;
    ddrc=0xff;
    porta=1;
    portc=~(1&lt;&lt;0);
    port_pointer=&amp;portc;
    port_xor=(&a......
-----------------------------------------------------------------------

评判指标:
1。简单、短小,易懂,结构好,层次清楚
2。效率高(指仅化费做这点事情所使用的CPU效率,省出的时间可以睡觉,做其它事情)
3。非常容易调整移动的时间(指改变其中1句或2句代码,或某个变量,其实是为按键调整控制做准备的)
4。显示亮度均匀,没有闪烁、拖尾现象

以上你的代码达到哪个指标?如果要求走马灯的速度移动速度为1秒,或2秒(每隔1s or 2s移动到下一个位置),代码如何调整?

不是说代码短就是好的,你的代码还可以短,比如:port_xor^=(unsigned char)port_pointer;你直接查手册,得到地址,做好^,直接赋值,还可以少用一条指令。

你的代码如果再加上按键扫描,消抖,处理的话,显示就要出问题了。

先体会我的代码吧,你再这样钻下去,写不出好的系统代码的。
发表于 2011-1-18 22:29:14 | 显示全部楼层
马老师 误会我的意思了 我只是说自己能力差 看了很久才能理解 程序确实很妙
发表于 2011-1-18 22:42:05 | 显示全部楼层
a few issues with your code:

"interrupt [TIM2_COMP] void timer2_comp_isr(void)
{
    led8x8();"

you should try NOT to call other routines from within an isr, especially when the same routines are called outside of the isr.

in your case, you should just blend led8x8() into the isr.

"void main(void)
{
    PORTA=0x00;"

to make the code more portable, you shouldn't hardwire the ports in the code. instead, write to a logic layer: for example, set COL_PORT and ROW_PORT, etc.

hope it helps.
发表于 2011-1-18 22:42:46 | 显示全部楼层
回复【179楼】machao  
-----------------------------------------------------------------------

1. 执行效率和易读性我认为是矛盾的。通用写法易读但效率较低。
2. 由于不是用循环减少代码,指令少必然效率高
3. for (k=0;k<4;k++){  
4. delay改用中断flag传递,按键扫描,消抖,没有任何问题。
发表于 2011-1-18 22:57:46 | 显示全部楼层
回复【179楼】machao
-----------------------------------------------------------------------

感觉马老师的程序实在是很超越,要改的话肯定有得必有失,我们实验考的时候,有用这种思路的,但是写得肯定不是思路那么清楚,如果用for循环或者其他什么循环结构的话,看上去的长度是短了,但是从易读性和易修改性上来说,肯定就不如马老师的这个程序了。
 楼主| 发表于 2011-1-19 00:04:53 | 显示全部楼层
仔细、全面的分析和讲解偶的代码

#include <mega16.h>
char dis_buff[8] = {0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00};
char j=0,line=0,point;
char time_count;
bit t_xxms_ok;

void led8x8(void) {
    static char i = 0;
    PORTC = 0xff;
    PORTA = dis_buff;
    if (i = = line)  PORTA = dis_buff | point;
    PORTC = ~(1<<i);
    if(++i>7) i = 0;
}

interrupt [TIM2_COMP] void timer2_comp_isr(void)
{
    led8x8();
    if(++time_count >= 250)
    {time_count = 0; t_xxms_ok = 1;}
}


void main(void)
{
    PORTA=0x00;
    DDRA=0xFF; // PA口设置为输出工作方式
    PORTC=0xFF;
    DDRC=0xFF; // PC口设置为输出工作方式

    TCCR2=0x0B; // T/C2工作在CTC方式,32分频
    OCR2=0x7C;
    TIMSK=0x80; // 允许T/C2比较匹配中断
    #asm("sei") // 允许全局中断

    while (1)
    { if(t_xxms_ok)
        {    t_xxms_ok = 0;
            
            if (j<8)  
            {   line = 0; point = 0x80>>j;}
            if (j>=8 && j<14)
            {   line++; point = 0x01;}
            if(j>=14 && j< 22)
            {   line = 7; point = 0x01<<(j-14);}
            if (j>=22 && j< 28)
            {   line-- ; point = 0x80;}
            
            if(++j > 27) j = 0;
        }
    }
}

整个代码层次非常清楚,分成3个部分,每个部分完成自己的部分,基本是独立的,调试非常方便。

首先看LED扫描和中断:
void led8x8(void) {
    static char i = 0;
    PORTC = 0xff;
    PORTA = dis_buff;
    if (i = = line)  PORTA = dis_buff | point;
    PORTC = ~(1<<i);
    if(++i>7) i = 0;
}

interrupt [TIM2_COMP] void timer2_comp_isr(void)
{
    led8x8();
    if(++time_count >= 250)
    {time_count = 0; t_xxms_ok = 1;}
}

中断是每1ms一次。每执行一次中断服务将调用一次扫描显示函数,每执行一次扫描显示函数将点亮1行上的LED。因此8*8点阵全部扫描一遍(8行),形成1帧图案需要8ms时间。那么在1秒内一共_产生了125帧图案,大大超过了25帧/秒人眼的滞留数,所以整个流水灯的效果不会闪烁。

另外,每行点亮的时间非常平均,没有那一行多,那行少,行扫,帧扫都是均等的,所以保证了整个图案的效果亮度均匀,不会出现有的点亮,有的点暗的现象(这里当然认为所有LED的特性是相同的)。

定时中断配合led8x8(void),实际构成了8*8LED的底层驱动,那么到底要显示什么,就是数组dis_buff[8]中的内容了。这个通常应该在上层代码中决定和填入的。

这个是8段LED数码管扫描驱动的模板!

下面再仔细看LED的扫描函数:
void led8x8(void) {
    static char i = 0;
    PORTC = 0xff;
    PORTA = dis_buff;
    if (i = = line)  PORTA = dis_buff | point;
    PORTC = ~(1<<i);
    if(++i>7) i = 0;
}

在此题目中,中间4个LED是不动常亮的,所以将数组dis_buff[8]中填入了固定的图象。如果把    if (i = = line)  PORTA = dis_buff | point;注销掉运行的话,显示的就是中心的4个点。因此省下的只要考虑那个单独的、运动的LED了。

PORTC = 0xff;的作用是临时关闭所有的LED,功能是消除拖尾现象的,这个不想多罗嗦了。
如果把PORTC = ~(1<<i)  (从上到下一行一行点亮);改成PORTC = ~(0x80 >> i);(从下到上一行一行点亮),那么LED点就是从左下角开始逆时针移动了。当然不改变代码,改变PORTC或PORTA的硬件连接顺序,运动方向也就反了。

分析到此,省下的就是如何显示运动的点了。那就是: if (i = = line)  PORTA = dis_buff | point;作用了。

这里的变量line是指出当前点应该在那个行上显示,而变量point就是具体的点位(列)了。至于具体的值,就交给上层决定吧。

中断服务中的后两行,是控制移动速度的,这里为250,因此知道运动点每250ms移动到下一个位置,如果需要快,减少,需要慢,增加。
    if(++time_count >= 250)
    {time_count = 0; t_xxms_ok = 1;}

题目中给出调整到1s,那么就是 if(++time_count >= 1000);但此时还要把变量time_count改成int变量!

如果加按键调整运动速度的话,那么if(++time_count >= speed);在按键处理中,赋给speed变量不同的值,移动的速度也就不同。

但是,不管你如何调整移动速度,都不会影响和破坏8*8LED的扫描显示过程,两者之间是独立的,互不干扰。

最后就是主程序了。前面是初始化,不用多讲了。

  while (1)
    { if(t_xxms_ok)
        {    t_xxms_ok = 0;
            
            if (j<8)  
            {   line = 0; point = 0x80>>j;}
            if (j>=8 && j<14)
            {   line++; point = 0x01;}
            if(j>=14 && j< 22)
            {   line = 7; point = 0x01<<(j-14);}
            if (j>=22 && j< 28)
            {   line-- ; point = 0x80;}
            
            if(++j > 27) j = 0;
        }
    }

里面就是一段计算LED点的位置的代码,一旦移动时间到了,算出下个点的位置。
点移动的位置一共28个(0-27),这个大家都知道了,分布在8行上(line:0-7),第0行8个,右移;然后1/2/3/4/5/6,都在最右边,接下来第7行8个,左移,然后6/5/4/3/2/1,最左边.......

显示、移动速度调整、算点的位置,都是相对独立,互不影响的。调试可以一个功能一个功能的分别调试。

有人提出螺旋移动,也没问题吗,其它部分不用变化,就是考虑改掉
            if (j<8)  
            {   line = 0; point = 0x80>>j;}
            if (j>=8 && j<14)
            {   line++; point = 0x01;}
            if(j>=14 && j< 22)
            {   line = 7; point = 0x01<<(j-14);}
            if (j>=22 && j< 28)
            {   line-- ; point = 0x80;}
            
            if(++j > 27) j = 0;
这段代码。学软件编程的人,发挥你的长处,给个算法,填入line和point就可以了。

整个软件的结构是非常重要的,学习过程中编写的代码结构不好,代码短是没有意义的。只有在特殊情况下,比如代码空间不够了,才会放弃对结构的要求。

另外就是完成整个功能所占CPU的时间。偶代码中,完成整个功能没有任何的软件延时浪费,MCU有大量的时间做其它的事情。

如果你的代码中有DELAY类似的代码,就不要贴上来了,这个是坑人的东西。没讲过T/C可以用一下,方便说明问题。实际应用中尽量不用或少用软件delay!
发表于 2011-1-19 00:04:57 | 显示全部楼层
mark,精彩。
发表于 2011-1-19 00:06:31 | 显示全部楼层
学习
发表于 2011-1-19 00:09:07 | 显示全部楼层
//LED animation achievement

#include "../platform.h"
#include "../hardware.h"

#include "LED_animation.h"

uint16 t;//t is time in ms
enum {top,bottom,left,right}side; //边
//转角处LED属于顺时针方向遇到的边,例如左上角属于上边
uint8 pos;                        //位置

bool key_lock = 0;  //用于等待按键松开

uint8 timer;

static void make_a_block(void)
{
    LED_array_set(4,5,TRUE);
    LED_array_set(5,4,TRUE);
    LED_array_set(5,5,TRUE);
    LED_array_set(4,4,TRUE);
}

void APP_LED_animation(void)
{
    pos = 0;
    side = top;
    t = 200;
    key_lock = FALSE;
    timer = vtimer_alloc();
   
    make_a_block();
   
    if(timer == VTIMER_NULL)
    {
        while(1);
    }
   
    while(1)
    {
        if(!key_lock)
        {
            if(key_pressed(KEY_TPLUS))
            {
                t+= 200;
                if(t > 1000) t = 200;
                key_lock = TRUE;
            }
        }
        else
                {
                    if(key_released(KEY_TPLUS))
                         key_lock = FALSE;
                }
                  
        if(vtimer_ovf(timer))   
        {
            switch(side)
            {
                case top:
                    vtimer_set(timer,t);
                    LED_array_set(pos,0,FALSE);
                    pos++;
                    if(pos >= 8)
                    {
                        pos = 0;
                        side = right;
                        break;
                    }
                    LED_array_set(pos,0,TRUE);
                    break;
                    
                case bottom:
                    vtimer_set(timer,t);
                    LED_array_set(7,7-pos,FALSE);
                    pos++;
                    if(pos >= 8)
                    {
                        pos = 0;
                        side = left;
                        break;
                    }
                    LED_array_set(7,7-pos,TRUE);
                    break;
               
                case left:
                    vtimer_set(timer,t);
                    LED_array_set(7-pos,0,FALSE);
                    pos++;
                    if(pos >= 8)
                    {
                        pos = 0;
                        side = top;
                        break;
                    }
                    LED_array_set(7-pos,0,TRUE);
                    break;
               
                case right:
                    vtimer_set(timer,t);
                    LED_array_set(7,pos,FALSE);
                    pos++;
                    if(pos >= 8)
                    {
                        pos = 0;
                        side = bottom;
                        break;
                    }
                    LED_array_set(7,pos,TRUE);
                    break;
                    
                default:side = top;     
            }//switch(side)
            
        }//if(vtimer_ovf(timer))

    }//while(1)

}//main()

没实验板也没proteus,暂不测试了。等装好proteus再说。
这个代码的优势在于结构清晰,而且可以复用。
点击此处下载 ourdev_611990NSF1WC.rar(文件大小:68K) (原文件名:machao_LED_array.rar)
 楼主| 发表于 2011-1-19 00:25:59 | 显示全部楼层
回复【181楼】millwood0
a few issues with your code:
"interrupt [tim2_comp] void timer2_comp_isr(void)
{
    led8x8();"
you should try not to call other routines from within an isr, especially when the same routines are called outside of the isr.
in your case, you should just blend led8x8() into the isr.
"void main(void)
{
    porta=0x00;"
to make the code more portable, you shouldn't hardwire the ports in the c......
-----------------------------------------------------------------------

这2点是更高层次的要求了。考试主要是为了学生能好理解,层次清楚。

1。led8x8() 可以放入中断中,但层次结构不好。
   我做产品设计过程中,led8x8() 不会在中断中调用,也不放在中断中。而是采用这样的结构:

   led8x8() {}

   interrupt [TIM2_COMP] void timer2_comp_isr(void)  
   {  
      led_s_time_ok = 1;  
   }

   main()
   {
    ......
    while{
        ......
        if (led_s_time_ok)
        {   led_s_time_ok = 0; led8x8();}

    }
    我给学生编写中断的原则之一是:中断服务尽量短;以及能不使用中断嵌套就不要使用中断嵌套的结构。

2。这个是编程规范的问题。说实话,我的课程不能解决全部的问题。如果我这样写代码的话,不及格的人更多。

说句难听的话,您写的E文贴,就有很多的学生是看不明白您在讲什么。
发表于 2011-1-19 00:30:46 | 显示全部楼层
打个酱油
PKU化学本科, CAS物理博士,业余玩玩
从开始写程序算起用了39分钟,没精确定时,没考虑中间四个灯闪烁的问题
手头没M16, 只有M48, 所以用PB0和PB1代替PC6和PC7

#include <stdlib.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/signal.h>
#include <avr/pgmspace.h>
#include "util/delay.h"
#include "avr/sleep.h"
#include "avr/wdt.h"

#ifndef cbi
        #define cbi(x, i) ((x) &= ~( 1<<(i) ))
#endif
#ifndef sbi
        #define sbi(x, i) ((x) |= ( 1<<(i) ))
#endif

#ifndef bit_set
        #define bit_set(x, i) ((x) & ( 1<<(i) ) )
#endif

#define delayms(ms) _delay_ms(ms)
#define delayus(us) _delay_us(us)
       
static unsigned char        data2[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
static unsigned char        data[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };

SIGNAL        (SIG_OVERFLOW0);

SIGNAL        (SIG_OVERFLOW0) {

        int        x[] = { 0, 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7, 7, 6,
                5, 4, 3, 2, 1, 0, 0, 0, 0, 0, 0, 0 };
        int        y[] = { 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 7,
                7, 7, 7, 7, 7, 7, 6, 5, 4, 3, 2, 1 };
       
        int        size = sizeof(x) / sizeof(int);

        static int count = 0;
        static int i = 0;
        count++;
        if( count > 30 ) {
                count -= 30;
                memset( data2, 0, sizeof(data2) );
       
                data2[ y[i % size ] ] = 1 << x[ i % size ];
                data2[3] |= 1<<3;
                data2[3] |= 1<<4;
                data2[4] |= 1<<3;
                data2[4] |= 1<<4;

                memcpy( data, data2, sizeof(data2) );
                i++;
        }
}

int        DISPLAY();
int        DISPLAY() {

        int        i;

        { PORTC = ~0; sbi( PORTB, 0 ); sbi( PORTB, 1 ); }
        for ( i = 0; i < 8; i++ ) {
               
                PORTD = data;
                if ( i < 6 ) {
                        cbi ( PORTC, i );
                }
                else {
                        cbi ( PORTB, i-6 );
                }

                delayms(1);
                { PORTC = ~0; sbi( PORTB, 0 ); sbi( PORTB, 1 ); }
        }
        return 0;
}

int main() {

        TCCR0B = 0b010;
        sbi( TIMSK0, TOIE0);

        DDRD = ~0;
        DDRC = ~0;
        DDRB = ~0;       
        PORTD = ~0;
        PORTC = 0;
        PORTB = ~0;
        cbi(PORTB, 0);
        cbi(PORTB, 1);

        sei();

        while(1) {
                DISPLAY();
        }
}
发表于 2011-1-19 01:10:28 | 显示全部楼层
为什么我写的代码这么丑长?

ICCAVR写的,没用过CVAVR。

#include<iccioavr.h>
#include<macros.h>

#define KEY1 (PINC&(1<<0))  //按键 PC0
#define LED_H PORTA
#define LED_L PORTB

unsigned char xian_zt=0,xian_wx=0,xian_data=0x80,key_zt=0,delay=0;
unsigned int timer2_t=200,timer2_js=0;


void timer2_init(void)  //产生1mS定时中断
{
CLI();
TCCR2=0;
TCNT2=0;
OCR2=124;
TCCR2=(1<<WGM21)|(1<<CS21)|(1<<CS20);  //4000000/32/125=1000
TIMSK=(1<<OCIE2);
SEI();
}

void io_init(void)
{
DDRA=DDRB=0xff;
PORTB=PORTC=PORTD=0xff;
}

void key_fun(void)
{
   if(KEY1==0 && delay==0 && key_zt==0) delay=10; //按键程序
   if(delay>15)  //去斗动
   {
    delay=0;
    if(KEY1==0 && key_zt==0)  //判断按键是否按下
    {
     key_zt=1;
     timer2_t+=400;  //加0.4S
         timer2_js=0;
         if(timer2_t>1000) timer2_t=200;  //大于1S
    }
   }
   if(KEY1==1 && key_zt==1) key_zt=0;  //按键是否弹起
}


void main(void)
{

io_init();

timer2_init();


while(1)
{
  key_fun();  
}
}


#pragma interrupt_handler timer2_fun:iv_TIMER2_COMP

void timer2_fun(void)  //中断处理
{

//////////////////////////////////////////显示程序
if((xian_zt>=0 && xian_zt<=6) || (xian_zt>=14 && xian_zt<=20))
{
  LED_L=0xFF;  //消影
  if(xian_wx==0)
  {
   LED_H=xian_data;
   if(xian_zt<=6)
   LED_L=~(1<<0);
   else
   LED_L=~(1<<7);
  }
  else if(xian_wx==1)
  {
   LED_H=0x18;
   LED_L=~(1<<3);
  }
  else
  {
   LED_H=0x18;
   LED_L=~(1<<4);
  }
}
else
{
  LED_H=0;
  if(xian_wx==0)
  {  
   LED_L=~xian_data;
   if(xian_zt<=13)
   LED_H=(1<<0);
   else
   LED_H=(1<<7);
  }
  else if(xian_wx==1)
  {
   LED_L=~0x18;
   LED_H=(1<<3);
  }
  else
  {
   LED_L=~0x18;
   LED_H=(1<<4);
  }
}

if(xian_wx>=2)  xian_wx=0; else xian_wx++;
/////////////////////////////////////////////

timer2_js++;  //定时
if(timer2_js>=timer2_t)
{
  timer2_js=0;
  
  xian_zt++; if(xian_zt>27) xian_zt=0;
  
  if((xian_zt==0) || (xian_zt==21))
  xian_data=0x80;
  else if((xian_zt>=1 && xian_zt<=6) || ((xian_zt>=22 && xian_zt<=27)))
  xian_data>>=1;
  else if((xian_zt==7) || (xian_zt==14))
  xian_data=1;
  else if((xian_zt>=8 && xian_zt<=13) || ((xian_zt>=15 && xian_zt<=20)))
  xian_data<<=1;
  else
  xian_zt=0;  

}

if(delay>=10) delay++;  //定时,用于按键消斗

}
发表于 2011-1-19 01:29:25 | 显示全部楼层
回复【189楼】tomzbj
打个酱油
pku化学本科, cas物理博士,业余玩玩
从开始写程序算起用了39分钟,没精确定时,没考虑中间四个灯闪烁的问题
手头没m16, 只有m48, 所以用pb0和pb1代替pc6和pc7
#include &lt;stdlib.h&gt;
#include &lt;avr/io.h&gt;
#include &lt;avr/interrupt.h&gt;
#include &lt;avr/signal.h&gt;
#include &lt;avr/pgmspace.h&gt;
#include "util/delay.h"
#include "avr/sleep.h"
#include "avr/wdt.h"
#ifndef cbi
#define cbi(x, i) ((x) &amp;= ~( 1&lt;&lt;(i) ))
#endif
#ifnd......
-----------------------------------------------------------------------

话说这代码看着比machao副教授的参考答案可读性强
 楼主| 发表于 2011-1-19 01:56:54 | 显示全部楼层
考题的标准答案:
1.填空(10分)
    由于系统时钟为4M,T/C2工作在CTC方式,预分频系数为32,T/C2比较寄存器的值为0x7C,所以每隔( 1ms  )时间间隔就产生一次比较匹配中断。每执行一次中断服务将调用一次扫描显示函数,每执行一次扫描显示函数将点亮( 1行  )(行 or 列)上的LED。因此8*8点阵全部扫描一遍,形成1帧图案需要(  8ms    )时间,由于在1秒内一共_产生了( 125 )帧图案,所以整个流水灯的效果( 不会 )   (会 or 不会)闪烁。

2.回答下列问题(25分)
(1)根据现在的代码分析,系统上电后第1个点亮的流水灯是哪个位置的LED?(2分)
        第1行最左边(注问的是 流水灯 第1个位置)

(2)流水灯每隔多少时间移动到下个位置?(2分)
        250ms

(3)如果要调整流水灯的移动速度,比如将速度提高1倍,应该如何改动代码?(2分)
        if(++time_count >= 125)

(4)如果需要将LED调整为1秒移动1次,又应该如何修改代码?(3分)
         int time_count;
         if(++time_count >= 1000)

(5)扫描显示函数中的PORTC = 0xff;语句起到什么作用?并说明为什么能起到你所说的作用?(3分)
         消除拖尾现象。

(6)分别说明代码中3个全局变量j、line、point的作用,它们之间的关系,以及如何与扫描显示函数配合,形成流水灯的。(4分)
        见184楼解释说明

(7)如果仅将T/C2的分频系数设置成256,代码其它部分不做任何改动,程序执行后将出现什么情况?(3分)

        此时中断间隔成为8ms,扫描一帧图象的时间为64ms,1秒内扫描的帧数为15.625帧,显示的图象就会闪烁跳动了。

(8)仅需要改动led8x8函数代码段中的一句语句就能将流水灯的运动方向改成逆时钟方向运动,请分析原代码,并给出改动后的语句(2分)
        PORTC = ~(0x80 >>i);  注:此时从左下角开始逆时钟方向运动,题目没有规定必须从左上角开始
   
(9)当然也可以改动多句语句,将流水灯的运动方向改成逆时钟方向运动,请分析原代码,并给出改动后的语句(4分)
        if (j<8)   
        {   line = 0; point = 0x01<<j;}  
        if (j>=8 && j<14)  
        {   line++; point = 0x80;}  
        if(j>=14 && j< 22)  
        {   line = 7; point = 0x80 >> (j-14);}  
        if (j>=22 && j< 28)  
        {   line-- ; point = 0x01;}  
        
         从右上角开始。答案之一。      


3.对本题给出的代码不做任何改变,分析仅改变硬件连接后所产生的实际效果。
    将PA口与8*8LED点阵的8个连接线对调,(既原接PA0改接PA7、既原接PA1改接PA6、既原接PA2改接PA5、……)产生的实际效果有何变化?请具体说明。(5分)

    从右上角开始逆时钟方向运动。
 楼主| 发表于 2011-1-19 02:01:18 | 显示全部楼层
本帖最后由 machao 于 2013-6-12 17:03 编辑

回复【193楼】twoperson

话说这代码看着比machao副教授的参考答案可读性强

-----------------------------------------------------------------------

哈哈,手下败将,一个本科生实验测试题的代码都不敢贴出来,还有本事做评价。

还是回去面壁,练上几年武功后,再做评价吧。北大的颜面被你丢够了,不要多说了。你的那个院士导师可能其它地方有能耐,但是做这个肯定不行,否则你也不会这个水平了。再多说,连你导师的颜面也要丢尽了。

顺便问一句,您是北大的吗,不会是冒牌吧。
 楼主| 发表于 2011-1-19 02:26:02 | 显示全部楼层
本帖最后由 machao 于 2013-6-12 17:04 编辑

回复【193楼】twoperson  
话说这代码看着比machao副教授的参考答案可读性强  
-----------------------------------------------------------------------
哈哈,手下败将。还是回去面壁,再练上几年武功后,再做评价吧。北大的颜面被你丢够了,不要多说了,你的那个院士导师可能其它地方有能耐,但是做这个肯定不行,否则你也不会这个水平了。
-----------------------------------------------------------------------

如果不服,可以再次比试。你在北大找5个学生,组成一个小组。

用M16加一片LM324,设计一个读取SD卡上WAVE文件,并播放的WAVE播放器,看谁做的好。

给你们一个月的时间,下个学期开学我到北大找你,比试实物效果。
 楼主| 发表于 2011-1-19 03:31:48 | 显示全部楼层
查了一下,发现twoperson是个优秀的学生,全国名牌中学师大二附中学生,中学就玩PIC、进入北大电子系读本科,自己学习AVR,有许多体会,本科设计:USB照相机,传感器用的是mt9m001,usb控制器用了cy7c68013。现在北大读研究生,不过到计算机系去了。

    奇怪的是怎么自己不敢上个代码?敢于接受挑战吗?做个WAVE播放器?春节回上海吗?我给你看我做的东西,然后提供你必要的硬件条件。

    其实硬件非常简单,ATmega16一片、LM324四运放一片、SD卡座、耳机插座各一。其它就是电阻电容了。自备SD卡、带功放的小音箱。你可以自己决定采用的方案,也可以采用我的方案(在这个讨论组中有,年底刚发的,可能你看到过)。

    或者使用一片M051,32位的,可以实现播放44.1k,16位,双声道的WAVE。芯片、编程线我提供。

    先说明,我都做好了。程序是我自己写的,绝不是我的研究生代写的。他们做的,效果和性能都不能与我的相比。

 
发表于 2011-1-19 04:43:44 | 显示全部楼层
if (j<8)
{   line = 0; point = 0x80>>j;}
if (j>=8 && j<14)
{   line++; point = 0x01;}
if(j>=14 && j< 22)
{   line = 7; point = 0x01<<(j-14);}
if (j>=22 && j< 28)
{   line-- ; point = 0x80;}
            
if(++j > 27) j = 0;
================================================================
这段代码优化程度可能不够理想。
本菜鸟第一反应是可以写成这样:
if (j<8)
{   line = 0; point = 0x80>>j;}
else if (j<14)
{   line++; point = 0x01;}
else if(j< 22)
{   line = 7; point = 0x01<<(j-14);}
else if (j< 28)
{   line-- ; point = 0x80;}
            
if(++j > 27) j = 0;

再改一改的话:
unsigned char state = 0;
……
……
switch(j)
{
    case 7 : state = 1; break;
    case 14: state = 2; break;
    case 21: state = 3; break;
    case 28: state = 4; break;
}
switch(state)
{
    case 0: line = 0; point = 0x80>>j; j++; break;
    case 1: line = j-7;   point = 0x01;    j++; break;
    case 2: line = 7; point = 0x01<<(j-14); j++; break;
    case 3: line = 28-j ;  point = 0x80; j++; break;   
    case 4: line = 0; point = 0x80; j=1; state = 0;break;   
}

这样,比较操作也省掉了;至于会不会引入额外跳转,很大程度上要看编译器的功力了。当然,如果优化编译器能对下面的情况做优化,那么这么做的性能提升就要少一些了。
if (j<8)
……
if (j>=8 && j<14)
手边没有硬件和仿真器,上述代码不保证100%正确,不过思路大致如此。鉴于本菜鸟对AVR的架构和编译器的本事不够熟悉,实际性能改善不敢打包票。

不过有一点我敢保证,这段代码无论如何优化,查找表都能快上几倍。想要快就空间换时间,不在乎时间就时间换空间。
发表于 2011-1-19 04:49:13 | 显示全部楼层
"这2点是更高层次的要求了。"

putting a routine inside of an isr is just fundamentally flaw.

your code has the right structure - updating the display through a display buffer but you made it unnecessarily complicated.

here is mine - originally written for 8051. It uses an overflow timer so to maintain portability (to other mcus).

the rows are driven via a  hc164, making the design idea to be expanded to drive a message board, by chaining hc164 (or a hc595).

============code==========
//#include <regx51.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include "gpio.h"
//#include "delay.h"

//hardware configuration
#define COL_PORT                PORTA                        //columns are driven by P2
#define COL_DDR                        DDRA
#define COL_PINs                0xff                //all pins on p2
#define COL_DLY                        1000                        //delay cycles for each col

#define HC164_PORT                PORTB
#define HC164_DDR                DDRB
#define HC164_CLK                (1<<1)                //hc164 clock
#define HC164_SDA                (1<<2)                //hc164 data out
#define HC164_CLR(pin)        IO_CLR(HC164_PORT, pin)        //clear a hc164 pin
#define HC164_SET(pin)        IO_SET(HC164_PORT, pin)        //set a hc164pin
//hardware configuration

//strobe pins on rising edge
#define HC164_STROBE(pins)        {IO_CLR(HC164_PORT, pins); IO_SET(HC164_PORT, pins);}

//tmr1 offset
unsigned short _tmr1_ticks=0x0000;                //display interval for each column


#define MSB(word_t)                ((word_t) >> 8)        //most significant byte
#define LSB(word_t)                ((word_t) & 0x00ff)        //least significant byte

//driving mechanism: col low, + hc164 high to turn on a dot on the matrix (common cathode)

const unsigned char font_8x8[3][8] = {        //asci font
{0x1f,        0x28,        0x48,        0x88,        0x48,        0x28,        0x1f,        0x00},        //'A'
{0xff,        0x91,        0x91,        0x91,        0x91,        0xaa,        0x44,        0x00},        //'B'
{0x7e,        0x81,        0x81,        0x81,        0x81,        0x81,        0x42,        0x00}        //'C'
};

unsigned char disp_buffer[8] = {        //each column is a byte. 0=right most col
        //0x1f,        0x28,        0x48,        0x88,        0x48,        0x28,        0x1f,        0x00
        //0xff,        0x91,        0x91,        0x91,        0x91,        0xaa,        0x44,        0x00
        0x7e,        0x81,        0x81,        0x81,        0x81,        0x81,        0x42,        0x00
        //0xff, 0xf0, 0xf0, 0x0f, 0xff, 0xff, 0x55, 0x22
        };        //start with a blank space

//unsigned char disp_str[]="ABCBABBAC";        //ascii string to be displayed
//unsigned char *disp_str_ptr=disp_str;                        //display col index


void _8x8_init(void) {
        IO_CLR(COL_PORT, COL_PINs);                //all col low
        IO_OUT(COL_DDR, COL_PINs);                //all col as output

        IO_CLR(HC164_PORT, HC164_CLK | HC164_SDA);        //_clk / _sda low
        IO_OUT(HC164_DDR, HC164_CLK | HC164_SDA);        //_clk / _sda as output
}

void tmr1_init(unsigned short ticks) {        //set tmr1 to trip once every ticks
        _tmr1_ticks = -ticks;

        //EA = 0;                                                                //disable interrupt
        cli();

        //TR0=0;                                                                //turn off tmr0
        TCCR1B = 0x00;                                                        //stop the timer
        //TMOD = (TMOD & 0xf0) | 0x01;                //tmr0: not gated, timer, mode 1 (16 bit tmr)
        TCCR1A =        (0 << COM1A1) | (0 << COM1A0) |                //com0a1..0 = 0b00
                                (0 << COM1B1) | (0 << COM1B0) |                //com0b1..0 = 0b00
                                (0 << WGM11) | (0 << WGM10)                 //wgm2..0 = 0b000 -> normal operation
                                ;
        TCCR1B =        (0 << FOC1A) |                                                //force output channe a disabled
                                (0 << FOC1B) |                                                //force output channel b disabled
                                (0 << WGM12) |                                                //wgm12..0 = 0b000 -> normal operation
                                (0 << CS12) | (0 << CS11) | (1 << CS10)                        //cs12..0 = 0b001 -> 1:1 prescaler
                                ;
        //TH0 = MSB(_tmr0_ticks);                                //load up the offset
        //TL0 = LSB(_tmr0_ticks);
        TCNT1 = _tmr1_ticks;                                                        //8-bit timer/counter
        //TR0 = 1;                                                        //turn on tmr0
        //ET0 = 1;                                                        //turn on tmr0 interrupt
        TIMSK1 =         (0 << OCIE1B) |                                //output compare match interrupt on B disabled
                                (0 << OCIE1A) |                                //output compare match interrupt on a disabled
                                (1 << TOIE1)                                //tmr0 interrupt enabled
                                ;
        //EA = 1;                                                                //turn on global interrupt
}

//void tmr0_isr(void) interrupt TF0_VECTOR {
ISR(TIMER1_OVF_vect) {
        static unsigned current_col=0;                //current display

        //TH0 += MSB(_tmr0_ticks);                        //update the offset
        //TL0 += LSB(_tmr0_ticks);
        TCNT1 += _tmr1_ticks;                                //advance tmr1 counter

        //send 0 on current_col = 0; 1 otherwise
        if (current_col) IO_CLR(HC164_PORT, HC164_SDA);
        else IO_SET(HC164_PORT, HC164_SDA);
        HC164_STROBE(HC164_CLK);                        //strobe out the data on sda

        //COL_PORT = 0x00;                                        //turn off all columns
        COL_PORT = disp_buffer[current_col];        //display current buffer

        current_col = (current_col + 1) % 8;        //update current_col;
}

void mcu_init(void) {
}

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

        mcu_init();                                                //reset the mcu
        _8x8_init();                                        //initialize the matrix
        tmr1_init(COL_DLY);                                //set tmr0 to trip every 1000 ticks (=1ms)  = 1ms per column
        sei();                                                        //turn on global interrupt

        while (1) {
                //i++;
                //_8x8_disp();                                //display the current buffer on the leds
        }
}
发表于 2011-1-19 08:28:39 | 显示全部楼层
马老师这是何必呢。。   

至于这个再次比试, 我这个外行也觉得就是一个人一周的工作量

回复【195楼】machao  
回复【192楼】twoperson  
话说这代码看着比machao副教授的参考答案可读性强  
-----------------------------------------------------------------------
哈哈,手下败将。还是回去面壁,再练上几年武功后,再做评价吧。北大的颜面被你丢够了,不要多说了,你的那个院士导师可能其它地方有能耐,但是做这个肯定不行,否则你也不会这个水平了。
-----------------------------------------------------------------------
如果不服,可以再次比试。你在北大找5个学生,组成一个小组。
用m16加一片lm324,设计一个读取sd卡上wave文件,并播放的wave播放器,看谁做的好。
给你们一个月的时间,下个学期开学我到北大找你,比试实物效果。
-----------------------------------------------------------------------
友情提示:标题不合格、重复发帖,将会被封锁ID。详情请参考:论坛通告:封锁ID、获得注册邀请码、恢复被封ID、投诉必读
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|阿莫电子论坛(原ourAVR/ourDEV) ( 公安备案:44190002001997(交互式论坛) 工信部备案:粤ICP备09047143号 )

GMT+8, 2019-9-19 13:07

阿莫电子论坛, 原"中国电子开发网"

© 2004-2018 www.amobbs.com, 原www.ourdev.cn, 原www.ouravr.com

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