搜索
bottom↓
楼主: machao

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

[复制链接]
(336883816)

出0入0汤圆

发表于 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的架构和编译器的本事不够熟悉,实际性能改善不敢打包票。

不过有一点我敢保证,这段代码无论如何优化,查找表都能快上几倍。想要快就空间换时间,不在乎时间就时间换空间。
(336883487)

出0入0汤圆

发表于 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
        }
}
(336870321)

出0入12汤圆

发表于 2011-1-19 08:28:39 | 显示全部楼层
马老师这是何必呢。。   

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

回复【195楼】machao  
回复【192楼】twoperson  
话说这代码看着比machao副教授的参考答案可读性强  
-----------------------------------------------------------------------
哈哈,手下败将。还是回去面壁,再练上几年武功后,再做评价吧。北大的颜面被你丢够了,不要多说了,你的那个院士导师可能其它地方有能耐,但是做这个肯定不行,否则你也不会这个水平了。
-----------------------------------------------------------------------
如果不服,可以再次比试。你在北大找5个学生,组成一个小组。
用m16加一片lm324,设计一个读取sd卡上wave文件,并播放的wave播放器,看谁做的好。
给你们一个月的时间,下个学期开学我到北大找你,比试实物效果。
-----------------------------------------------------------------------
(336869793)

出0入0汤圆

发表于 2011-1-19 08:37:27 | 显示全部楼层
...
(336868936)

出0入0汤圆

发表于 2011-1-19 08:51:44 | 显示全部楼层
157L哪里去了
(336864847)

出0入0汤圆

发表于 2011-1-19 09:59:53 | 显示全部楼层
我也贴 一个 没有硬件测试 不知道 对不对 WINAVR的
#include<avr/io.h>
#include <avr/interrupt.h>  

unsigned char ms1_flag = 0,ms200_flag = 0;
unsigned char count = 0,state = 0;
unsigned char j = 0;






void ms200_flag_do(void)
{
        if(ms200_flag)
        {
                ms200_flag = 0;
       
                if (j<8)   
                {
                        state = 1;j++;return;
                }  

                if (j<15)  
                {
                        state = 2;j++;return;
                }  
                if( j< 22)  
                {
                        state = 3;j++;return;
                }  
                if (j< 28)  
                {
                        state = 4;j++;return;
                }
                else
                {
                        j=0;
                }  
         
               
        }
       


}
void TIMER2_Start(void)
{
    cli();
    TCCR2 = 0x00;   //disable the system clock
    ASSR  = 0x00;   //set async mode
    TCNT2 = 0x00;   //setup
    TIMSK |= 0x80;  //CTC interrupt
    OCR2  = 0x1f;
    sei();
    TCCR2 = 0x0E;//start  div Fsck/256
}



ISR(TIMER2_COMP_vect) //定时1MS
{

        ms1_flag = 1;
        count++;
        if(count==200)
        {
                ms200_flag = 1;       
                count = 0;
       
        }



}

int main(void)
{
       
        PORTA=0x00;
           DDRA=0xFF; // PA口设置为输出工作方式
           PORTC=0xFF;
           DDRC=0xFF; // PC口设置为输出工作方式
           TIMER2_Start();
        while(1)
        {
                if(ms1_flag)
                {
                        ms1_flag = 0;
                                PORTA = 0X18;
                        PORTC = 0X18;
                        PORTA = 0X00;
                                PORTC = 0XFF;
                                switch(state)
                        {
                                case 1:PORTC = 0XFE;PORTA = 0X80>>j;break;
                                case 2:PORTA = 0X01;PORTC = ~(0X02<<(j-8));break;
                                case 3:PORTC = 0X7F;PORTA = 0X02<<(j-15);break;
                                case 4:PORTA = 0X80;PORTC = ~(0X40>>(j-22));break;
                       
                       
                        }
               
                }
                ms200_flag_do();
               
       
       
        }

}
(336863275)

出0入0汤圆

发表于 2011-1-19 10:26:05 | 显示全部楼层
here is my code, running on PORTD / PF6..7.



(原文件名:usb1286_8x8_tmr1.PNG)


all you need to do is to update disp_buffer[] so that it displays a dynamic image.
(336863190)

出0入0汤圆

发表于 2011-1-19 10:27:30 | 显示全部楼层
马老师较真了!:) 其实,马老师可以单独开个制作wav播放器的帖子,估计很多人感兴趣的!
(336862734)

出0入0汤圆

 楼主| 发表于 2011-1-19 10:35:06 | 显示全部楼层
回复【197楼】the_exile
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......
-----------------------------------------------------------------------

我原用的if else,为了考试学生理解方便,全部采用了if结构。
至于用switch还是if好,本题不涉及此层度。更多的人是不理解的。我在题目中讲的效率,是指明显的降低效率的方法,就是指不要使用软件延时函数。

你还知道 switch还是if,要看编译器和指令系统。在市面上大量的“好”书中,甚至32位系统的书中代码,到处可见使用软件延时几十毫秒,UART轮循接收的代码。这个的浪费,你用100个IF OR SWITCH的选择也补不回来的。

就是在本贴中的代码,你看到DELAY吗?

189楼中的 :
#define delayms(ms) _delay_ms(ms)
#define delayus(us) _delay_us(us)

187楼中的:vtimer_set(timer,t);

178楼中的   _delay_ms(50); //proteus不响应快扫,故改慢,增大k的终值减少延时结果相同而且不闪

58楼的,还是32位系统,也出现delay_ms(100); 的语句。要知道STM32可以工作在72M,delay_ms(100);还谈什么效率高?
(336862365)

出0入0汤圆

 楼主| 发表于 2011-1-19 10:41:15 | 显示全部楼层
回复【198楼】millwood0
"这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 dri......
-----------------------------------------------------------------------

拿你的代码去考试,99%的学生挂掉了。
(336862293)

出0入0汤圆

 楼主| 发表于 2011-1-19 10:42:27 | 显示全部楼层
回复【199楼】tomzbj
马老师这是何必呢。。   
至于这个再次比试, 我这个外行也觉得就是一个人一周的工作量
回复【195楼】machao   
回复【192楼】twoperson   
话说这代码看着比machao副教授的参考答案可读性强   
-----------------------------------------------------------------------  
哈哈,手下败将。还是回去面壁,再练上几年武功后,再做评价吧。北大的颜面被你丢够了,不要多说了,你的那个院士导师可能其它地方有能耐,但是做这个肯定不行,否则你也不会这个水平了。
-----------------------------------------------------------------------
如果不服,可以再次比试。你在北大找5个学生,组成一个小组。
用m16加一片lm324,设计一个读......
-----------------------------------------------------------------------

外行您就看热闹吧,门道您是看不出的。
(336862080)

出0入0汤圆

 楼主| 发表于 2011-1-19 10:46:00 | 显示全部楼层
回复【203楼】millwood0
here is my code, running on portd / pf6..7.


(原文件名:usb1286_8x8_tmr1.png)
引用图片
all you need to do is to update disp_buffer[] so that it displays a dynamic image.
-----------------------------------------------------------------------

建议您看我的教材,里面有这样的例子的。上课讲过。至于学生是否去学,去掌握?20%吧。
(336861690)

出0入12汤圆

发表于 2011-1-19 10:52:30 | 显示全部楼层
原来马老师的意思是用标志位判断比用delay高明... 您早说啊.

回复【205楼】machao  
回复【197楼】the_exile
if (j&lt;8)  
{   line = 0; point = 0x80&gt;&gt;j;}
if (j&gt;=8 &amp;&amp; j&lt;14)
{   line++; point = 0x01;}
if(j&gt;=14 &amp;&amp; j&lt; 22)
{   line = 7; point = 0x01&lt;&lt;(j-14);}
if (j&gt;=22 &amp;&amp; j&lt; 28)
{   line-- ; point = 0x80;}
            
if(++j &gt; 27) j = 0;  
================================================================
这段代码优化程度可能不够理想。
本菜鸟......
-----------------------------------------------------------------------
(336861667)

出0入96汤圆

发表于 2011-1-19 10:52:53 | 显示全部楼层
收藏,今天的网络不好
(336861234)

出0入0汤圆

 楼主| 发表于 2011-1-19 11:00:06 | 显示全部楼层
问个研究生级别应该可以回答的问题吧。这里“应该”是指你具备了相应真正的能力了。不要给出本科生水平的答案。

在我的代码中使用了这样的定时和移动时间控制的方法:

void led8x8(void) {。。。 }  

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

main()
....
while(1)
{
.......
     if(t_xxms_ok )
     {
         t_xxms_ok = 0;
         ............
      }
}


其实也可以这样修改:
void led8x8(void) {。。。 }  

interrupt [TIM2_COMP] void timer2_comp_isr(void)  
{  
    led8x8();  
    if(time_count) time_count--;  
}  

main()
....
while(1)
{
.......
     if(!time_count )
     {
         time_count = 250;
         ............
      }
}

这两种方法哪种好?有什么区别?你推荐用哪个?
(336860677)

出0入0汤圆

 楼主| 发表于 2011-1-19 11:09:23 | 显示全部楼层
【209楼】 tomzbj
【198楼】 millwood0
【197楼】 the_exile
【192楼】 twoperson


能对211楼的问题给个参考解释吗?
(336860502)

出0入12汤圆

发表于 2011-1-19 11:12:18 | 显示全部楼层
我觉得全局变量不如标志位,在两个地方改全局变量影响程序结构。
外行随便说说, 各位老师见笑~
(336860450)

出0入0汤圆

发表于 2011-1-19 11:13:10 | 显示全部楼层
main:
    unsigned char i = 1,column = 0,show = 0x80;
  //时间到时:
    dis_buff[column] = dis_buff[column] & ~show;
    if(i <= 7){
       show >>= 1;
    }else if(i<=14){
        column++;
    }else if(i <= 21){
       show <<= 1
    }else{
        column--;
    }
    dis_buff[column] = dis_buff[column] | show;
    if(++i > 28){
       i = 1;
    }
      
  void led8x8(void) {
    static char i = 0;
    PORTC = 0xff;
    PORTA = dis_buff;
    PORTC = ~(1<<i);
    if(++i>7) i = 0;
}
(336860082)

出0入0汤圆

 楼主| 发表于 2011-1-19 11:19:18 | 显示全部楼层
回复【213楼】tomzbj
我觉得全局变量不如标志位,在两个地方改全局变量影响程序结构。
外行随便说说, 各位老师见笑~
-----------------------------------------------------------------------

不管学什么专业,您可是博士,(PKU化学本科, CAS物理博士,业余玩玩),您觉得您的帖子反映了您博士能力了?现在的博士是这样回答问题的?

全局变量不如标志位,为什么?举例说明

影响程序结构,如何影响的?
(336859833)

出0入0汤圆

 楼主| 发表于 2011-1-19 11:23:27 | 显示全部楼层
回复【214楼】banyai

再考虑考虑吧。
贴上实际效果自己看到的,并且是完整的代码。
(336859529)

出0入12汤圆

发表于 2011-1-19 11:28:31 | 显示全部楼层
我汗,博士应该怎么回答问题,有模板么

我就是觉得在多个地方修改全局变量的值加强了模块之间的耦合,不利于维护。
(336859154)

出0入0汤圆

发表于 2011-1-19 11:34:46 | 显示全部楼层
嘿嘿,其实马老师的办法,俺一直都是这么用的。

//1ms中断,用于AD、数码管扫描、按键检测
void Timer0Interrupt(void) interrupt 1
{
    TH0 = 0xFC;
    TL0 = 0x18;
        //add your code here!
        ms_100_con++;
        ms_1_con++;
        if(ms_1_con>1000) //产生秒

主程序中,查询相关变量。就几个程序在跑,对按钮的响应(状态机),是相当迅速,
   //读AD值,并滤波,推算温度值
   if(ms_100_con > 200)         
    {
     ms_100_con = 0;     
     AD_temp = GetADCResult(5);

200msAD采集一次,

各个任务跑起来没有磕磕绊绊,很流畅。
(336858138)

出0入0汤圆

 楼主| 发表于 2011-1-19 11:51:42 | 显示全部楼层
回复【217楼】tomzbj
我汗,博士应该怎么回答问题,有模板么
我就是觉得在多个地方修改全局变量的值加强了模块之间的耦合,不利于维护。
-----------------------------------------------------------------------

应该汗!

你明确说明哪个优了吗?

两段代码中,都在两个地方需要(必需的)对全局变量做修改,不同的是一个是t_xxms_ok,另外一个是time_count。在CVAVR中有BIT变量的概念,它也是全局的,在其它平台中,t_xxms_ok也只能定义成一个全局的char变量。

对不起了,因为您的博士,所以应该更加严谨的回答问题。您的回答在这个例子中不成立。
(336857927)

出0入0汤圆

发表于 2011-1-19 11:55:13 | 显示全部楼层
回复【212楼】machao  
-----------------------------------------------------------------------

第二种写法不如第一种健壮,特别是有其他中断的情况下。timecount = 0时被其他中断堵到下一次timecount--执行的话,结果会很难看
(336857822)

出0入0汤圆

 楼主| 发表于 2011-1-19 11:56:58 | 显示全部楼层
回复【219楼】erxun 老孟
嘿嘿,其实马老师的办法,俺一直都是这么用的。
//1ms中断,用于ad、数码管扫描、按键检测
void timer0interrupt(void) interrupt 1
{
    th0 = 0xfc;
    tl0 = 0x18;
//add your code here!
ms_100_con++;
ms_1_con++;
if(ms_1_con&gt;1000) //产生秒
主程序中,查询相关变量。就几个程序在跑,对按钮的响应(状态机),是相当迅速,
   //读ad值,并滤波,推算温度值
   if(ms_100_con &gt; 200)   
    {
     ms_100_con = 0;      
     ad_temp = getadcresult(5);
200msad采集一次,
各个任务跑起来没有磕磕绊绊,很流畅。
-----------------------------------------------------------------------

谢谢您的捧场。

不过如果是设计精度要求比较高的系统,建议您使用CTC方式。至于为什么,与211楼的问题是类似的,想一想为什么,道理明白后您在技术上又上了一个台阶。
(336857141)

出0入0汤圆

发表于 2011-1-19 12:08:19 | 显示全部楼层
好贴啊,看不懂。。。
(336856991)

出0入0汤圆

 楼主| 发表于 2011-1-19 12:10:49 | 显示全部楼层
回复【222楼】the_exile
回复【212楼】machao   
-----------------------------------------------------------------------
第二种写法不如第一种健壮,特别是有其他中断的情况下。timecount = 0时被其他中断堵到下一次timecount--执行的话,结果会很难看
-----------------------------------------------------------------------
timecount = 0 时被其他中断堵到下一次timecount--执行的话,问题不会太大的,因为中断中是: if(time_count) time_count--;  timecount = 0,是不会再减的。t_xxms_ok也有可能被堵,只是概率小。

有点深入了,但还没有涉及最重要的地方。
(336856448)

出0入0汤圆

 楼主| 发表于 2011-1-19 12:19:52 | 显示全部楼层
提醒或提示,211楼问题不涉及编译器、敏感变量,原子操作的问题,要从系统整体出发考虑问题。

222楼有点摸到边了。
(336856146)

出0入0汤圆

发表于 2011-1-19 12:24:54 | 显示全部楼层
回复【225楼】machao  
-----------------------------------------------------------------------
没注意if(timecount),失误。两种写法在这方面没太大区别。

另一个能直接看到的差异是,第二种写法在运行时改延迟时间是非常方便的,条件语句里给timecount设置不同的值就是了,加几行程序、不用调整流程就能解决后面的按键问题;而第一种就要麻烦一些。
(336855770)

出0入0汤圆

 楼主| 发表于 2011-1-19 12:31:10 | 显示全部楼层
回复【227楼】the_exile
回复【225楼】machao   
-----------------------------------------------------------------------
没注意if(timecount),失误。两种写法在这方面没太大区别。
另一个能直接看到的差异是,第二种写法在运行时改延迟时间是非常方便的,条件语句里给timecount设置不同的值就是了,加几行程序、不用调整流程就能解决后面的按键问题;而第一种就要麻烦一些。
-----------------------------------------------------------------------

又转远了。这个回答刚到本科水平,算入门初级。

请从你提到的“堵”深入下去。
(336855766)

出0入0汤圆

发表于 2011-1-19 12:31:14 | 显示全部楼层
测试了一下程序,没有什么问题,在下面,因为LED的行列不同,可能转圈的方向也会不一样,参考了马老师的程序,led8x8也可以不放中断中:
#include <avr/io.h>
#include <avr/interrupt.h>
unsigned char dis_buff[8] = {0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00};
unsigned char time_count;
volatile unsigned char t_xxms_ok;

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

SIGNAL(SIG_OUTPUT_COMPARE2)
{
    led8x8();
    if(++time_count >= 250)
    {time_count = 0; t_xxms_ok = 1;}
}

int main(void)
{
    unsigned char i = 1,column = 0,show = 0x1;
       
        PORTA=0x00;
    DDRA=0xFF;         // PA口设置为输出工作方式
    PORTC=0xFF;
    DDRC=0xFF;         // PC口设置为输出工作方式

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

    dis_buff[0] |= show;
    while(1)
    {       
            if(t_xxms_ok)
        {          
                    t_xxms_ok = 0;
            dis_buff[column] &= ~show;
            if(i <= 7){
               show <<= 1;
           }else if(i<=14){
               column++;
           }else if(i <= 21){
               show >>= 1;
           }else{
               column--;
           }  
           dis_buff[column] |= show;
           if(++i > 28){
               i = 1;
                   }
        }
    }
        //return 0;
}
(336855717)

出0入0汤圆

发表于 2011-1-19 12:32:03 | 显示全部楼层
回复【196楼】machao
    查了一下,发现twoperson是个优秀的学生,全国名牌中学师大二附中学生,中学就玩pic、进入北大电子系读本科,自己学习avr,有许多体会,本科设计:usb照相机,传感器用的是mt9m001,usb控制器用了cy7c68013。现在北大读研究生,不过到计算机系去了。
    奇怪的是怎么自己不敢上个代码?敢于接受挑战吗?做个wave播放器?春节回上海吗?我给你看我做的东西,然后提供你必要的硬件条件。
    其实硬件非常简单,atmega16一片、lm324四运放一片、sd卡座、耳机插座各一。其它就是电阻电容了。自备sd卡、带功放的小音箱。你可以自己决定采用的方案,也可以采用我的方案(在这个讨论组中有,年底刚发的,可能你看到过)。
    或者使用一片m051,32位的,可以实现播放44.1k,16位,双声道的wave。芯片、编程线我提供。
    先说明,我都做好了。程......
-----------------------------------------------------------------------

昨天一直在忙实验室的事情,就没顾得上看这帖,看样子马副教授在ourdev里浏览了下我发的帖啊。。。
有几个更正的地方,我本科在微电子系,多了个微字和电子系干的事情很不一样。。。本科设计是手指静脉识别技术中的图像采集,要是谁本科毕业设计敢单做个什么基于CY7C68013的USB照相机或者基于AVR的播放器再或者是基于8051的闪烁小灯这类东西就等着被拍吧。。。
这个挑战有什么意思?是能解决什么实际问题么?还是能促进科学进步?
马老师想挑战的话,不如去申个自然科学基金之类的,然后做点实际有用的东西出来?
(336855502)

出0入0汤圆

发表于 2011-1-19 12:35:38 | 显示全部楼层
回复【228楼】machao  
-----------------------------------------------------------------------

你是想说第二个写法里计时的周期可能因为timecount = 250被中断堵而改变么?

如果多个对精度要求高的任务都用它计时,那么这确实比较麻烦;但如果单任务、变时间延迟的话,还是第二种比较顺畅。至于到底哪种好,我觉得还是得看要求是什么。
(336855133)

出0入0汤圆

发表于 2011-1-19 12:41:47 | 显示全部楼层
学习,顶马老师,语重心长,诲人不倦啊
(336854520)

出0入0汤圆

 楼主| 发表于 2011-1-19 12:52:00 | 显示全部楼层
回复【230楼】twoperson
回复【196楼】machao  
   我本科在微电子系,多了个微字和电子系干的事情很不一样。。。本科设计是手指静脉识别技术中的图像采集,要是谁本科毕业设计敢单做个什么基于CY7C68013的USB照相机或者基于AVR的播放器再或者是基于8051的闪烁小灯这类东西就等着被拍吧。。。
这个挑战有什么意思?是能解决什么实际问题么?还是能促进科学进步?
马老师想挑战的话,不如去申个自然科学基金之类的,然后做点实际有用的东西出来?     


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

屁话!

你的微字,你做了什么,你们北大的微字做了什么,你的本科设计怎么不去设计一片芯片?北大微电子系设计出国产的MPU吗,AVR吗?MTM32吗?交大到是有一片“汉芯”,骗了国家几个亿,几十个院士、教授什么的。

高级的电子医疗设备你们北大设计出来几台?高级的汽车发动机控制系统你能做吗?你做过哪个实际有用的产品?用在哪个行业?促进科学进步?

没有技术上的真本事,拿出学术政客的嘴脸,这个是北大培养的高才生吗?跟你的导师学的?

作为教师的我,就是培养人才,你不是建议我到二附中去介绍AVR的应用吗?这个需要自然科学基金吗?教过你的老师都是自然科学基金的老师?

为国家培养真正有用的技术人才,不是实际有用的东西?不是促进科学进步?
(336854181)

出0入0汤圆

发表于 2011-1-19 12:57:39 | 显示全部楼层
回复【230楼】twoperson
-----------------------------------------------------------------------
你的回复,给我的感觉:一屋不扫,何以扫天下。

回复【223楼】machao
-----------------------------------------------------------------------
可惜51单片机定时器没有CTC。

我回答下211楼,
这两种办法,我都用过,
用定时器的“时间轮”来做任务刷新,一般都出现这两种方式。
第一种更好,
道理和“变速器的输出来驱动别的轮子”差不多。
(336853750)

出0入0汤圆

 楼主| 发表于 2011-1-19 13:04:50 | 显示全部楼层
回复【234楼】erxun 老孟
回复【230楼】twoperson  
-----------------------------------------------------------------------
你的回复,给我的感觉:一屋不扫,何以扫天下。
回复【223楼】machao  
-----------------------------------------------------------------------
可惜51单片机定时器没有ctc。
我回答下211楼,
这两种办法,我都用过,
用定时器的“时间轮”来做任务刷新,一般都出现这两种方式。
第一种更好,
道理和“变速器的输出来驱动别的轮子”差不多。
-----------------------------------------------------------------------

1。51的定时器有自动重装模式,同CTC模式。

2。第二种也可以“变速器的输出来驱动别的轮子”,例如:

    if(timer_c1) timer_c1--;
    if(timer_c2) timer_c2--;

没有体会到关键。

==================================================
不是卖关子,是有几个北大的,觉得自己不错,因此先不做解释,作为挑战。到是要看看北大学生的本事在什么地方。
(336852996)

出0入0汤圆

发表于 2011-1-19 13:17:24 | 显示全部楼层
现在的所谓名牌学校的学生都学傻了。张口闭口都是科研。。。
夜郎自大
马老师也是恨铁不成钢啊
(336852845)

出0入0汤圆

 楼主| 发表于 2011-1-19 13:19:55 | 显示全部楼层
本课的题目,认为是小儿科,可是连个代码都不敢贴上,怕被“挂”,还是做过USB照相机的。

给个研究生级别的问题,屁都不放。

挑战做个WAVE播放器,说对国家没贡献,不能促进科学进步。

要申请国家自然科学基金?问一下,你从无到有,设计出一个什么产品批量生产了吗?

不过有一点我肯定不如你了:我这个副教授的文章肯定没有你这个硕士生的多。

您还是唬别人去吧。不过如果你手上有我的AVR书的话,请把它烧掉。因为,这个根本不是自然科学基金级别的。拜托了,谢谢!
(336852106)

出0入0汤圆

 楼主| 发表于 2011-1-19 13:32:14 | 显示全部楼层
回复【236楼】kim5257
现在的所谓名牌学校的学生都学傻了。张口闭口都是科研。。。
夜郎自大
马老师也是恨铁不成钢啊

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

您高抬他们了。
他们连铁都不是。充其量是含铁量多一点的矿石。现在正在慢慢被练成渣滓,成不了铁,钢?就嘴硬。
(336851137)

出0入296汤圆

发表于 2011-1-19 13:48:23 | 显示全部楼层
to 【211楼】 machao
    马老师换汤不换药……还是在考定时的准确性……
    1、定时器都使用的是比较匹配模式,推测使用的是CTC(我没有看前面的初始化代码);
       定时器都是自动reload的,因此两段代码同时使用的是精确定时模式;
    2、两个代码使用的变量,无论是标志还是计数器都是一个字节长度,对8位AVR来说,属于
       默认的原子操作类型。因此不必在操作的完整性和原子性上纠结
    3、严格说两段代码作用不同,不应该放在一起比较。前者t_xxms_ok标志以固定的时间频率
       (当然要考虑扫描函数是否执行时间固定且严格)被置1;而第二个例子属于倒计时计数
       器,通常用于可变的系统延时,而并不常用于定时时间间隔的刷新类目的,因为reload
       是在主程序(另外一个任务平面上)中,因此从系统处理完所有中断处理程序到reload
       从而再次触发倒计时,这中间的时间是可变的,受到系统很多因素的干预……
    4、如果题设目的明确,那么显然第二种不符合要求,应该给一个勉强及格的分数,应为实效
       和精确性无法得到保证(只能说在某些情况下碰巧可以用);第一种符合题设的要求,因
       此可以给一个不错的分数(但如果缺乏对定时器模式的比较以及原子性的论述,肯定不能
       给满分)
    5、我本人比较喜欢第二种……因为我通常需要大量使用这种可变的倒计时计数器,在这种应
       用背景下,第一种已经不是好用不好用的问题了,是干脆就不适合。
(336851070)

出0入0汤圆

发表于 2011-1-19 13:49:30 | 显示全部楼层
刚看到马老师的帖子,这里给出我做的,不过是在WINAVR中写的,

我写软件一向讲究可移植性,扩展性,IO可调整性,相应会带来少许性能损失,
而且由于没有实际硬件,仅仅是编译通过,没有实际测试,不过很多模块是借用我以往的项目,应该不会有什么BUG,
万一有BUG也请见谅,必竟没下载到芯片中测试过

请马老师指教下,由于我写软件分功能模块写,所以文件众多,只贴少许主要函数,请下载查看完整工程

// Port.h
#ifndef _H_PORT
#define _H_PORT

#include "main.h"

#define Init_Port   Port_Init
extern  void Port_Init(void);

#define LED_COL0    PA7
#define LED_COL1    PA6
#define LED_COL2    PA5
#define LED_COL3    PA4
#define LED_COL4    PA3
#define LED_COL5    PA2
#define LED_COL6    PA1
#define LED_COL7    PA0

#define LED_ROW0    PC0
#define LED_ROW1    PC1
#define LED_ROW2    PC2
#define LED_ROW3    PC3
#define LED_ROW4    PC4
#define LED_ROW5    PC5
#define LED_ROW6    PC6
#define LED_ROW7    PC7

#define KEY_SW      PD0

#endif


// main.c
#include "main.h"

int main(void)
{
    Interrupt_ALL_Disabled();
    Init_WDG();
    Init_Port();    // 初始化输入输出端口
    Init_Timer();   // 初始化定时器
    Interrupt_ALL_Enabled();
    //
    for(;;)
    {
        WDG_CLR();
        if( f_Timer_Ovf )
        {
            f_Timer_Ovf = false;
            //
            //  固定主循环周期:1ms
            //
            Led_DoEvent();
            Key_DoEvent();
            Task_DoEvent();
            
            if( f_Timer_Ovf )
            {
                continue;
            }
        }
        SLEEP();    // 空闲休眠
    }
    return 0;
}

// Timer.c
#include "Timer.h"

TIMER_T        Timer;

void Timer_Init(void)
{
    //
    //  定时器2,中断周期:32/4MHz * 125 = 1ms
    //
    TCCR2 = FOC2_CLR | COM2_OFF | WGM2_CTC | CLK2_PRE32;
    TCNT2 = 0;
    OCR2  = 125-1;
    Interrupt_OC2_Enabled();
}

ISR(TIMER2_COMP_vect)
{
    f_Timer_Ovf = true;
}

// led.c
#include "Led.h"

LED_T Led;

const u8 bit[] = { BIT(0),BIT(1),BIT(2),BIT(3),BIT(4),BIT(5),BIT(6),BIT(7), };

void Led_Clear(void)
{
    Led.Buffer[0] = BIN(00000000);
    Led.Buffer[1] = BIN(00000000);
    Led.Buffer[2] = BIN(00000000);
    Led.Buffer[3] = BIN(00000000);
    Led.Buffer[4] = BIN(00000000);
    Led.Buffer[5] = BIN(00000000);
    Led.Buffer[6] = BIN(00000000);
    Led.Buffer[7] = BIN(00000000);
}

void Led_DrawPoint(u8 x, u8 y)
{
    Led.Buffer[y] |= bit[x];
}

void Led_ClearPoint(u8 x, u8 y)
{
    Led.Buffer[y] &=~bit[x];
}

void Led_DoEvent(void)
{
    u8 col;
    //   全部列输出0
    GPIO_OUT0(LED_COL0);
    GPIO_OUT0(LED_COL1);
    GPIO_OUT0(LED_COL2);
    GPIO_OUT0(LED_COL3);
    GPIO_OUT0(LED_COL4);
    GPIO_OUT0(LED_COL5);
    GPIO_OUT0(LED_COL6);
    GPIO_OUT0(LED_COL7);
    //
    Led.Row++;
    if( Led.Row>=ARRAY_SIZE(Led.Buffer) )
    {
        Led.Row = 0;
    }
    col = Led.Buffer[Led.Row];
    //
    if(col&BIT(0)) GPIO_OUT1(LED_COL0);
    if(col&BIT(1)) GPIO_OUT1(LED_COL1);
    if(col&BIT(2)) GPIO_OUT1(LED_COL2);
    if(col&BIT(3)) GPIO_OUT1(LED_COL3);
    if(col&BIT(4)) GPIO_OUT1(LED_COL4);
    if(col&BIT(5)) GPIO_OUT1(LED_COL5);
    if(col&BIT(6)) GPIO_OUT1(LED_COL6);
    if(col&BIT(7)) GPIO_OUT1(LED_COL7);
}

// Task.c
#include "Task.h"

TASK_T Task;

void Task_Init(void)
{
   
}

void Task_DoKey(void)
{
    if( KEY(SW).DoPress )
    {
        KEY(SW).DoPress = false;
        Task.Tick += MS(200);
        if( Task.Tick>MS(1000) )
        {
            Task.Tick = MS(200);
        }
    }
}

void Task_DoDraw(void)
{
    static u8 n;
    PT_INIT()
    PT_INIT_DELAY(u16)
    PT_BEGIN()
        Led_Clear();
        Led_DrawPoint(3,3);
        Led_DrawPoint(3,4);
        Led_DrawPoint(4,3);
        Led_DrawPoint(4,4);
        //
        Task.Tick = MS(200);
        for(;;)
        {
            for(n=0;n<8;n++)
            {
                Led_DrawPoint(n,0);
                PT_DELAY_TICK(Task.Tick);
                Led_ClearPoint(n,0);
            }
            for(n=0;n<8;n++)
            {
                Led_DrawPoint(7,n);
                PT_DELAY_TICK(Task.Tick);
                Led_ClearPoint(7,n);
            }
            for(n=7;(s8)n>=0;n--)
            {
                Led_DrawPoint(n,7);
                PT_DELAY_TICK(Task.Tick);
                Led_ClearPoint(n,7);
            }
            for(n=7;(s8)n>=0;n--)
            {
                Led_DrawPoint(0,n);
                PT_DELAY_TICK(Task.Tick);
                Led_ClearPoint(0,n);
            }
        }
    PT_END()
}

void Task_DoEvent(void)
{
    Task_DoKey();
    Task_DoDraw();
}

// Key.c
#include "Key.h"

KEY_T Keys[KEY_MAX_NUM];

void Key_Check(KEY_T* key)
{
    if( key->Status!=key->PIN )
    {
        key->Status = key->PIN;
        key->Tick = 0;
    }
    else
    {
        key->Tick++;
    }
    if( key->Status==KEY_PRESS )
    {
        //  短按
        if( key->IsPress==false && key->Tick>=MS(40) )
        {
            key->IsPress = true;
            key->DoPress = true;
        }
        //  长按
//        if( key->IsLong==false && key->Tick>=MS(1000) )
//        {
//            key->IsLong = true;
//            key->DoLong = true;
//        }
    }
    else
    {
        if( key->IsPress!=false && key->Tick>=MS(40) )
        {
            key->IsPress = false;
//            key->IsLong = false;
        }
    }
}

void Key_DoEvent(void)
{
    KEY(SW).PIN = GPIO_IN(KEY_SW);
    Key_Check(&KEY(SW));
}


点击此处下载 ourdev_612188R2TMD5.zip(文件大小:39K) (原文件名:led8x8.zip)
(336850855)

出0入0汤圆

发表于 2011-1-19 13:53:05 | 显示全部楼层
建议马老师可以加个加分题,加上灰度控制的部分,呵呵,这下可能有很多考生要骂我了。
(336850642)

出0入0汤圆

发表于 2011-1-19 13:56:38 | 显示全部楼层
呵呵,我也冒着被拍的危险,说两句,不对的地方,请轻拍。
个人觉得,两种方法各有优点。
第一种,可以称为健壮一些,不会有意外的事情出现。但效率相对会低一些。
第二种,效率高,因为很多片子的汇编都会有自减1指令,而有自加1指令的片子,好像不多。不针对CPU的话,第二种的效率,一般情况下都会比第一种高。并且中断中用尽量少的指令,也是提高效率的方法。但第二种的定时精度,相对第一种就会差很多,极限情况下,主函数判0的时候刚好为1,就得再等上一个轮回了。
(336850617)

出0入296汤圆

发表于 2011-1-19 13:57:03 | 显示全部楼层
to 【241楼】 sharpufo 风生水起月皎白
    我倒觉得,灰度控制可以去掉;但是假如一个——如何修改电路可以在不占用额外数字端口的情况下
实现8个按键的键盘扫描……
(336850418)

出0入0汤圆

发表于 2011-1-19 14:00:22 | 显示全部楼层
华师大
# include <mega16.h>

flash unsigned char led[8]={0x01,0x80,0x80,0x98,0x98,0x80,0x80,0x80};
flash unsigned char first[8]={0x00,0x00,0x00,0x18,0x18,0x00,0x00,0x00};
flash unsigned char position[8]={0xfe,0xfd,0xfb,0xf7,0xef,0xdf,0xbf,0x7f};
unsigned char dis_buff[8];
int time_count;
unsigned char read,now=0,move=0,flag=0,i=1;
int counter=1;
bit time_ok,time_readkey;

#define key_state_0 0
#define key_state_1 1
#define key_state_2 2

#define key_mask 0b00000001

void display(void)
{
        PORTC=0xFF;

        PORTA=dis_buff[now];


        PORTC=position[now];
        now++;
        if(now>=8)
        {
                now=0;
        }
}

interrupt [TIM0_COMP] void timer0_comp_isr(void)
{
        display();
        if(++time_count>=(counter*100))
        {
                time_count=0;
                time_ok=1;
        }
        if(++read>=5)
        {
                read=0;
                time_readkey=1;
        }
}

unsigned char read_key(void)
{  
   static unsigned char key_state=0;
   unsigned char key_press,key_return=0;
   key_press=key_mask&PINB;
   switch(key_state)
       {
           case key_state_0://状态0,无按键状态                 
           if(key_press==0x00)
                 {key_state=key_state_1;}
           break;

           case key_state_1://状态1,按键状态
           if(key_press==0x00){
                 key_return=1;
                 key_state=key_state_2;}
           else key_state=key_state_0;
           break;

           case key_state_2://状态2,等待释放状态
           if(key_press==key_mask)
                 key_state=key_state_0;
           break;               
       }
       return key_return;
   }

void main(void)
{
        int j;

        PORTA=0x00;
        DDRA=0xFF;
        PORTB=0x00;
        DDRB=0xFE;  //I/O端口初始化
        PORTC=0x00;
        DDRC=0xff;

        TCCR0=0x0B;
        TCNT0=0x00;
        OCR0=0x7C;
        TIMSK=0x02; //2ms中断一次
        #asm("sei")

        for(j=0;j<=7;j++)
        {
                dis_buff[j]=first[j];
        }
       
        while (1)
        {

                if(time_readkey==1)
                {
                        time_readkey=0;
                        if(read_key()==1)
                        {
                                counter++;
                                counter++;
                                if(counter==7)
                                {
                                counter=1;
                                }
                        }
                }
                if(time_ok==1)
                {
                        time_ok=0;
                        for(j=0;j<=7;j++)
                        {
                                dis_buff[j]=first[j];
                        }
                        switch(flag)
                        {
                        case 0:
                                dis_buff[0]=(led[0]<<move);
                                move++;
                                if(move==8)
                                {
                                        move=0;
                                        flag=1;
                                }
                                break;
                        case 1:
                                dis_buff=(led);
                                i++;
                                if(i>=7)
                                {
                                        i=6;
                                        flag=2;
                                }
                                break;
                        case 2:  
                                dis_buff[7]=(led[7]>>move);
                                move++;
                                if(move==8)
                                {
                                        move=0;
                                        flag=3;
                                }
                               
                                break;
                        case 3:
                                dis_buff=((led&0x7f)|0x01);
                                i--;
                                if(i==0)
                                {
                                        i=1;
                                        flag=0;
                                }
                                break;
                        }
                }
        }
}
(336850346)

出0入0汤圆

发表于 2011-1-19 14:01:34 | 显示全部楼层
两种都不是好的方法。一般简单的程序,比如这个有两个任务,规范一点来讲应该分开,显示任务和改变显示任务。而系统的滴答时钟一般都只有一种。这样,应该给每个任务都配一个递减计数器,在中断中只执行计数器的递减,特别紧急任务可以中断中直接执行。从而实现任务的分离。这样即使增加更多的任务,都能很好的运行。当然要保证在最长可能情况下,所以需要执行的任务完成时间小于系统的滴答一次的时钟,如果发现不行,就需要分离任务。可以参考:基于时间触发的程序设计。
(336850146)

出0入296汤圆

发表于 2011-1-19 14:04:54 | 显示全部楼层
to 【245楼】 banyai
     人家只是考题……嵌入式系统的核心就是专用……如果一个代码明确不会未来有
扩展——只是为了考试,这个大前提下(需求),做得越简单越好,越符合嵌入式的思想。
     千万别为了构架而构架……通用思想是嵌入式系统设计的毒品(注意,不是毒药)
(336850014)

出0入0汤圆

发表于 2011-1-19 14:07:06 | 显示全部楼层
回复【245楼】banyai  
两种都不是好的方法。一般简单的程序,比如这个有两个任务,规范一点来讲应该分开,显示任务和改变显示任务。而系统的滴答时钟一般都只有一种。这样,应该给每个任务都配一个递减计数器,在中断中只执行计数器的递减,特别紧急任务可以中断中直接执行。从而实现任务的分离。这样即使增加更多的任务,都能很好的运行。当然要保证在最长可能情况下,所以需要执行的任务完成时间小于系统的滴答一次的时钟,如果发现不行,就需要分离任务。可以参考:基于时间触发的程序设计。
-----------------------------------------------------------------------

我在240L用的正是和你说的类似的方法,有兴趣可以看下
(336848963)

出0入0汤圆

发表于 2011-1-19 14:24:37 | 显示全部楼层
回复【237楼】machao
-----------------------------------------------------------------------

我这个小本科感觉就是傻孩子说的意思,第二种方法中“time_count--”在中断中,time_count的重新赋值在main()中,那么当time_count = 0的时候,中断返回到主函数中,可能是任意一条语句的位置,而这个main函数的结构不知,是否有其他中断也不知道,那么这个重新赋值的时间就是不确定的。而第一种方法,肯定是等时间片式的,也就是说if(t_xxms_ok )这句语句不一定是精确地每250ms执行一次,但是定时器二所划分的时间片是相等的250ms。第二种方法,每次产生的时间片就可能被main函数中的其他语句“堵住”,本身就不准,再加上时间片划分的方式,执行起来的时候本来就有误差,所以第一种定时比较精确吧。
(336848242)

出0入0汤圆

 楼主| 发表于 2011-1-19 14:36:38 | 显示全部楼层
回复【239楼】Gorgon Meducer 傻孩子
to 【211楼】 machao
    马老师换汤不换药……还是在考定时的准确性……
    1、定时器都使用的是比较匹配模式,推测使用的是ctc(我没有看前面的初始化代码);
       定时器都是自动reload的,因此两段代码同时使用的是精确定时模式;
    2、两个代码使用的变量,无论是标志还是计数器都是一个字节长度,对8位avr来说,属于
       默认的原子操作类型。因此不必在操作的完整性和原子性上纠结
    3、严格说两段代码作用不同,不应该放在一起比较。前者t_xxms_ok标志以固定的时间频率
       (当然要考虑扫描函数是否执行时间固定且严格)被置1;而第二个例子属于倒计时计数
       器,通常用于可变的系统延时,而并不常用于定时时间间隔的刷新类目的,因为reload
       是在主程序(另外一个任务平面上)中,因此从系统......
-----------------------------------------------------------------------

我知道你是学软件的,回答的更深入些。但是由于没有做实际系统的经验积累,这些解释还是模糊的,或者说书面的,容易使人一头雾水,不知道你在说什么东西。

实际上,评价2种方式要从实际应用上看,再做出合适的评估。

首先,对于要求不高的系统,2者都可以使用,原子不原子,根本是多余的。

第2种方式还可以节省一个变量空间,减少代码量,实际还是有它的优点的。在有些时候,比如存储器紧张时,可能采用第2种方式更好些。

回答到这个点上,对于本科生,已经很优秀了,分数不是勉强及格,应该为90分。

那么更深的理解应该从什么方面考虑呢?(研究生层次)

通常没有OS的系统,主代码是WHILE的结构,在这个结构中,要做很多的事情,循环一次的时间是不确定的,增加一个功能,减掉一句语句是常有的事情。(非常可能出现前面提到的“堵”的问题)

拿个实际系统做例子,系统中定时中断,生成秒基时,此处我们且认为这个秒基时的产生时准确的没有延误。
系统还有个显示,显示时钟,然后做其它的事情 。。。。。

这里在系统中,需要根据秒基时来进行分、时的计算和调整,才能送出显示。这个大家都理解的。

当这个分、时的计算和调整处在主代码的WHILE中的话,此时就应该使用第1种方法了,使用第2种方法,你的表肯定要慢的,而且慢的规律是不容易分析的。

在实践考试的流水灯说,每次移动的时间设计为250ms

采用第一种方式,每次移动的时间实际是在250ms的左右,这个就看系统中还要做的其他事情了。但这个快慢是不会累计的,每转一圈的时间保证与28*250的时间基本同步的。

采用第二种方式,每次移动的时间实际都要比250ms长一点,这个误差是累计的。

采用第1种方法,每个小时,LED可以保证转的次数是相等的,比如100圈。而采用第2种方法,第1个小时99圈,第2个小时98圈...

这个是个简单的流水灯,无所谓了,多一圈,少一圈的。

如果你是控制导_弹的飞行呢?每小时少一圈?而且你还不知道,认为是100圈,飞到哪里了?

这里是举例子,当然导_弹还有制导系统调整。没看到网上做时钟的,提出的问题都是不准,慢,有几个说快的?除了系统时钟本身的问题,软件的设计上问题你能找到吗?如果不知道,系统时钟再准,也做不出准确的时钟。

接下来,才是考虑原子操作、多中断的“堵塞”问题。前面的不知道,考虑了100个原子操作,你的表还是不准,你的导_弹就是炸了自己的。
(336847813)

出0入0汤圆

发表于 2011-1-19 14:43:47 | 显示全部楼层
很有意思,马老师不愧是技高一筹,虽然知道两种方法定时都不准,但没有考虑到误差的累积。
学习了。
(336846837)

出0入0汤圆

发表于 2011-1-19 15:00:03 | 显示全部楼层
回复【249楼】machao
-----------------------------------------------------------------------

马老师说的好,这样的时间片的误差是累计的,针对这个程序可能没什么,但是对于时钟系统什么的,就很关键了,又学到新知识了。
(336846821)

出0入0汤圆

 楼主| 发表于 2011-1-19 15:00:19 | 显示全部楼层
各位朋友,代码就不要上了吧。

技术问题可以继续讨论。

1。看看能学到什么,那些地方自己没有考虑到,为将来自己能设计出最可靠的系统做准备。不是为“促进科学进步”努力,而是为自己能在社会中找到合适位置、工作岗位,有个稳定的生活努力学习吧。

2。评估名牌大学的学生的能力:拿者自然科学基金到这里抢饭?出风头?(请拿自然科学基金的朋友,从事微电子或半导体专业的名牌高校的大师们,设计一块真正的好MCU吧,我保证努力,配合写一本好的应用教材,只要你看的中我了)

3。继续朝马副教授头上拍砖  :)
(336846390)

出0入0汤圆

发表于 2011-1-19 15:07:30 | 显示全部楼层
第二种误差是累计的?为什么呢?只要在下一次中断来前,能够把250赋过去,就不会存在累计误差吧?误差始终会在一个中断时间间隔内,不会超过,和第一种是一样的。
(336846285)

出0入0汤圆

 楼主| 发表于 2011-1-19 15:09:15 | 显示全部楼层
回复【251楼】wangzheyu
回复【249楼】machao  
-----------------------------------------------------------------------
马老师说的好,这样的时间片的误差是累计的,针对这个程序可能没什么,但是对于时钟系统什么的,就很关键了,又学到新知识了。
-----------------------------------------------------------------------

如果我要单纯比拼代码小,就会采用第2种方式的,至少打字出考卷还少打一行呢。
但我使用的是第1种,道理让大家都非常明白做不到,但记住方法1更为好些。
(336846220)

出0入296汤圆

发表于 2011-1-19 15:10:20 | 显示全部楼层
to 【249楼】 machao
    马老师说的对。我觉得总结下来主要就是误差是否会积累的问题……另外,不太明白
为啥马老师要拍别人的砖头……因为我总觉得您只是想借此吸引眼球来让更多人看到这个
帖子,从而学习到这个细节的知识,出发点很好,做法我觉得不太适当。当然,这是你们
学术圈子里面的事情,我是一个局外人,觉得不适当也是正常的。
(336845806)

出0入0汤圆

发表于 2011-1-19 15:17:14 | 显示全部楼层
看这帧子之前,认为自己常用的第二种方法是最好的,现在知道错了,受益非浅。
(336845746)

出0入0汤圆

 楼主| 发表于 2011-1-19 15:18:14 | 显示全部楼层
回复【253楼】banyai
第二种误差是累计的?为什么呢?只要在下一次中断来前,能够把250赋过去,就不会存在累计误差吧?误差始终会在一个中断时间间隔内,不会超过,和第一种是一样的。
-----------------------------------------------------------------------

“堵”

主程序种还有其它的事情做,你能保证每次中断前都能够把250加入?后面是个读AD,或I2C读数据,还有人要加“delay_ms(50)”的。

有些是必须的“堵”。还有的更多的是自己给自己添“堵”,比如delay_ms(),这个在80%教科书中的"堵"药,毒害了很多人。

中断是1ms,
(336845631)

出0入0汤圆

发表于 2011-1-19 15:20:09 | 显示全部楼层
回复【248楼】wangzheyu  
回复【237楼】machao
-----------------------------------------------------------------------
我这个小本科感觉就是傻孩子说的意思,第二种方法中“time_count--”在中断中,time_count的重新赋值在main()中,那么当time_count = 0的时候,中断返回到主函数中,可能是任意一条语句的位置,而这个main函数的结构不知,是否有其他中断也不知道,那么这个重新赋值的时间就是不确定的。而第一种方法,肯定是等时间片式的,也就是说if(t_xxms_ok )这句语句不一定是精确地每250ms执行一次,但是定时器二所划分的时间片是相等的250ms。第二种方法,每次产生的时间片就可能被main函数中的其他语句“堵住”,本身就不准,再加上时间片划分的方式,执行起来的时候本来就有误差,所以第一种定时比较......
-----------------------------------------------------------------------
不认为是这样,如果把任务放在主循环里,两种都可能被堵,并且执行时间都是不一定的,因为中断发生是不一定的。
(336845607)

出0入12汤圆

发表于 2011-1-19 15:20:33 | 显示全部楼层
马老师,淡定淡定,在你自己的主题里爆粗是不是不太合适?您这么多学生还在这呢
北大做了什么实际有用的东西,可以说多了去了, 网上有的是资料,您自己可以去查嘛。
别的我不敢说, 您刚提到的高级的电子医疗设备, twoperson的一个师兄就正在做,而且做得很不错,两三年内上市。

回复【233楼】machao  
回复【230楼】twoperson
回复【196楼】machao  
   我本科在微电子系,多了个微字和电子系干的事情很不一样。。。本科设计是手指静脉识别技术中的图像采集,要是谁本科毕业设计敢单做个什么基于cy7c68013的usb照相机或者基于avr的播放器再或者是基于8051的闪烁小灯这类东西就等着被拍吧。。。
这个挑战有什么意思?是能解决什么实际问题么?还是能促进科学进步?
马老师想挑战的话,不如去申个自然科学基金之类的,然后做点实际有用的东西出来?     
-----------------------------------------------------------------------
屁话!
你的微字,你做了什么,你们北大的微字做了什么,你的本科设计怎么不去设计一片芯片?北大微电子系设计出国产的mpu吗,avr吗?mtm32吗?交大到是有一片“汉芯”,骗了国......
-----------------------------------------------------------------------
(336845551)

出0入0汤圆

发表于 2011-1-19 15:21:29 | 显示全部楼层
回复【249楼】machao  
-----------------------------------------------------------------------

然而第一种方法降低累积误差也不是没有代价;具体到流水灯上,表现就是如果任务过重,一圈下来可能有几个灯压根没亮过。在一些场合中时钟累积误差反倒不那么重要,信号时序出这种问题反而更要命。举个例子,比如用这个时钟去仿I2C总线,究竟是时钟比预期慢一点比较好,还是传过来的字节缺比特比较好?要是导_弹飞控计算机用会缺比特的通信程序去读传感器读数,这导_弹会飞到哪去?

我还是那个观点,好坏看的是与需求的吻合程度,特别是好与坏衡量指标多样化的情况下;用学院派的话说就是,多目标优化问题没有最优解,只有满意解。
(336845236)

出0入0汤圆

 楼主| 发表于 2011-1-19 15:26:44 | 显示全部楼层
回复【255楼】Gorgon Meducer 傻孩子
to 【249楼】 machao
    马老师说的对。我觉得总结下来主要就是误差是否会积累的问题……另外,不太明白
为啥马老师要拍别人的砖头……因为我总觉得您只是想借此吸引眼球来让更多人看到这个
帖子,从而学习到这个细节的知识,出发点很好,做法我觉得不太适当。当然,这是你们
学术圈子里面的事情,我是一个局外人,觉得不适当也是正常的。
-----------------------------------------------------------------------

本意没有这些节外的事情,只是让大家学习讨论一个简单的测试题。

可是看到口气大的朋友,不上代码,只是说些无用的废话,说北大的学生不用学,自己要用看看就会了,毕业都做的“大”东西,想摸摸底的。

现在不是挺好的吗。你以后碰到北大的,肯定不会心虚的,也就那样了。
(336844871)

出0入0汤圆

发表于 2011-1-19 15:32:49 | 显示全部楼层
请马老师帮忙看下240L我的代码,这是我目前的程序风格,
因为总是自己闭门造车摸索,
虽然自我感觉良好,但还是希望能给些指教和意建~
(336844613)

出0入0汤圆

发表于 2011-1-19 15:37:07 | 显示全部楼层
回复【257楼】machao  
回复【253楼】banyai
第二种误差是累计的?为什么呢?只要在下一次中断来前,能够把250赋过去,就不会存在累计误差吧?误差始终会在一个中断时间间隔内,不会超过,和第一种是一样的。
-----------------------------------------------------------------------
“堵”
主程序种还有其它的事情做,你能保证每次中断前都能够把250加入?后面是个读ad,或i2c读数据,还有人要加“delay_ms(50)”的。
有些是必须的“堵”。还有的更多的是自己给自己添“堵”,比如delay_ms(),这个在80%教科书中的"堵"药,毒害了很多人。
中断是1ms,

-----------------------------------------------------------------------
是的,如果说到堵的话,确实第二种方法会比第一种方法更好一些。但是一般我们确定定时时间的时候,都是以所有时间触发任务的最大公约数来计算的,这样可以减少中断。那么,在任务划分的时候,就一定确认了最长执行都会在一个时间间隔内完成。而如果不是这样,最小间隔的任务会丢失一次执行机会。
(336844588)

出0入296汤圆

发表于 2011-1-19 15:37:32 | 显示全部楼层
to 【261楼】 machao
    马老师……其实那个啥……用不着动气……一个积极讨论技术的帖子,
大家都在积极参与,那么一两个牛X哄哄,光打雷不下雨的帖子,不是您一
个人看着扎眼……应该说人人都会条件反射的嗤之以鼻……咱不理他们……
    现在在这个论坛,不动气我觉得最低碳……整天看看门户网站就已经把
人能气得没脉了……所以很多人就到这里来了……
(336844168)

出0入0汤圆

 楼主| 发表于 2011-1-19 15:44:32 | 显示全部楼层
回复【259楼】tomzbj
马老师,淡定淡定,在你自己的主题里爆粗是不是不太合适?您这么多学生还在这呢
北大做了什么实际有用的东西,可以说多了去了, 网上有的是资料,您自己可以去查嘛。
别的我不敢说, 您刚提到的高级的电子医疗设备, twoperson的一个师兄就正在做,而且做得很不错,两三年内上市。
回复【233楼】machao   
回复【230楼】twoperson  
回复【196楼】machao   
   我本科在微电子系,多了个微字和电子系干的事情很不一样。。。本科设计是手指静脉识别技术中的图像采集,要是谁本科毕业设计敢单做个什么基于cy7c68013的usb照相机或者基于avr的播放器再或者是基于8051的闪烁小灯这类东西就等着被拍吧。。。  
这个挑战有什么意思?是能解决什么实际问题么?还是能促进科学进步?  
马老师想挑战的话,不如去申个自然科学基金之类的,然后做点实际有用的东西出来?   ......
-----------------------------------------------------------------------

没什么啦,我的地盘我做主。

我不是贬低北大,北大能做很多“大”东西,推动技术发展。

但我开始是要个代码,这个在LZ位您看到了。你twoperson想贴就贴,不贴就看看算了。

“我表示我校绝大部分电子系3年级学生应该能在2小时内完成一和二”那么你保证了,3天了,代码呢?看不起别人?你北大的能,要拿实际的。不要显摆吧。

北大是厉害,但不代表北大的学生每个都厉害。北大还有卖肉的博士吧。

卖肉也没什么,但不要说屁话。

说屁话的北大学生估计也不会少到那里去的,twoperson就是一个。
(336843496)

出0入0汤圆

 楼主| 发表于 2011-1-19 15:55:44 | 显示全部楼层
回复【260楼】the_exile
回复【249楼】machao   
-----------------------------------------------------------------------
然而第一种方法降低累积误差也不是没有代价;具体到流水灯上,表现就是如果任务过重,一圈下来可能有几个灯压根没亮过。在一些场合中时钟累积误差反倒不那么重要,信号时序出这种问题反而更要命。举个例子,比如用这个时钟去仿i2c总线,究竟是时钟比预期慢一点比较好,还是传过来的字节缺比特比较好?要是导_弹飞控计算机用会缺比特的通信程序去读传感器读数,这导_弹会飞到哪去?
我还是那个观点,好坏看的是与需求的吻合程度,特别是好与坏衡量指标多样化的情况下;用学院派的话说就是,多目标优化问题没有最优解,只有满意解。
-----------------------------------------------------------------------

是的,我说过这个是研究生的答题,没有那个最好,要根据实际情况分析选择使用。

我的本意是,需要掌握到这个层次,知道里面的差别,说出道理来。否则你根本没有能力“根据实际情况分析选择使用”,“找到满意解”的。

不过你的例子举的不专业。访问i2c总线,现在都有硬件口,配合中断,时钟和数据是保证的。用1和2都行。我不会把它放在while中排队的。
(336842911)

出0入0汤圆

 楼主| 发表于 2011-1-19 16:05:29 | 显示全部楼层
回复【263楼】banyai  
是的,如果说到堵的话,确实第二种方法会比第一种方法更好一些。但是一般我们确定定时时间的时候,都是以所有时间触发任务的最大公约数来计算的,这样可以减少中断。那么,在任务划分的时候,就一定确认了最长执行都会在一个时间间隔内完成。而如果不是这样,最小间隔的任务会丢失一次执行机会。
--------------------------------------------------------------------
道理对的,做起来难,明白的人也不多。

看看本贴中的上的代码,有几个明确说明了8*8LED在1秒内扫描了几帧?
(336842892)

出0入0汤圆

发表于 2011-1-19 16:05:48 | 显示全部楼层
回复【266楼】machao  
-----------------------------------------------------------------------

我以前用什么都没有的古董2051弄过这个。而且一些片子的I2C口不够可靠(比如STM32),很多时候还是要软仿。
(336842588)

出0入0汤圆

 楼主| 发表于 2011-1-19 16:10:52 | 显示全部楼层
回复【264楼】Gorgon Meducer 傻孩子
to 【261楼】 machao  
    马老师……其实那个啥……用不着动气……一个积极讨论技术的帖子,
大家都在积极参与,那么一两个牛x哄哄,光打雷不下雨的帖子,不是您一
个人看着扎眼……应该说人人都会条件反射的嗤之以鼻……咱不理他们……
    现在在这个论坛,不动气我觉得最低碳……整天看看门户网站就已经把
人能气得没脉了……所以很多人就到这里来了……
-----------------------------------------------------------------------

你不了解我。偶才不会生气,是好玩,是休息。我还在与其他老师讨论评分标准,晚上要改考卷的。
(336842299)

出0入0汤圆

 楼主| 发表于 2011-1-19 16:15:41 | 显示全部楼层
回复【268楼】the_exile
-----------------------------------------------------------------------
我以前用什么都没有的古董2051弄过这个。而且一些片子的i2c口不够可靠(比如stm32),很多时候还是要软仿。
-----------------------------------------------------------------------
STM32的I2C不可靠?不太会吧。这个可是标准的东西。
(336842264)

出0入0汤圆

发表于 2011-1-19 16:16:16 | 显示全部楼层
马老师说的不过多的中断里边执行指令是很有道理的,中断里最好不要执行多的代码,实在不行就在中断里置个标志位,出来后在主程序里执行相应的任务,如果在中断里执行过多的语句,举个极端的例子,要是定时中断时间短,可能CPU大部分时间都在执行中断的指令,从而CPU得执行效率太低了,我只是一个中专生,这是我的拙见。
(336841866)

出0入0汤圆

发表于 2011-1-19 16:22:54 | 显示全部楼层
看来我回贴时候没选对,完全没人理我-_-
(336841257)

出0入0汤圆

 楼主| 发表于 2011-1-19 16:33:03 | 显示全部楼层
回复【260楼】the_exile
-----------------------------------------------------------------------
然而第一种方法降低累积误差也不是没有代价;具体到流水灯上,表现就是如果任务过重,一圈下来可能有几个灯压根没亮过。在一些场合中时钟累积误差反倒不那么重要,信号时序出这种问题反而更要命。举个例子,比如用这个时钟去仿i2c总线,究竟是时钟比预期慢一点比较好,还是传过来的字节缺比特比较好?要是导_弹飞控计算机用会缺比特的通信程序去读传感器读数,这导_弹会飞到哪去?
我还是那个观点,好坏看的是与需求的吻合程度,特别是好与坏衡量指标多样化的情况下;用学院派的话说就是,多目标优化问题没有最优解,只有满意解。
-----------------------------------------------------------------------

又仔细看过你的帖子。软件做I2C的话,更应该用方法1了。

1,你把时钟累积误差理解错了。这个误差是指计算时钟时、分的。不会用于作为I2C时钟。
2。这有两个时钟,一个是系统时钟(并由其产生T/C的时钟中断),它没有误差,用于操作I2C,应该使用系统时钟。
3。另一个时钟是基于系统时钟做的表,由于软件设计不合理,这个表有误差。方法1:这个表的误差不会积累,方法2:表误差会积累的,总使变慢。

4。还有一个地方有误差积累的情况。比如用T/C产生1ms的中断。用CTC模式(自动重装)没有积累,用普通溢出方式肯定产生误差积累。此时会使系统中所有的表变慢的。
(336840948)

出0入0汤圆

发表于 2011-1-19 16:38:12 | 显示全部楼层
看过了, 受教了.

本来delay是经常随便用的, 只要系统能跑, 以后尽量习惯用中断, 平时在公司给新人讲程序, 估计"害人不浅".
(336840766)

出0入0汤圆

发表于 2011-1-19 16:41:14 | 显示全部楼层
回复【270楼】machao  
-----------------------------------------------------------------------
STM32的I2C一直不大地道,光errata里列举的那几条莫名其妙的限制就足以逼迫很多人用软仿了。
(336839875)

出0入0汤圆

发表于 2011-1-19 16:56:05 | 显示全部楼层
回复【273楼】machao  
-----------------------------------------------------------------------

可能表达有问题,我说"时钟"时实际指的是你的基于系统时钟做表的俩方法。驱动I2C也只是个例子,这个例子可能不够完善(导_弹上也不会用流水灯转圈,不是吗?),但说明这两种方法各自的取舍倒是足够了:方法1引入的误差确实不会积累,但是如果while循环执行的不够快,就要放弃一部分东西来保证跟上;而方法2每次都要死命干完所有活儿,自然越拖越久。
(336839463)

出0入0汤圆

发表于 2011-1-19 17:02:57 | 显示全部楼层
回复【233楼】machao
回复【230楼】twoperson  
回复【196楼】machao   
   我本科在微电子系,多了个微字和电子系干的事情很不一样。。。本科设计是手指静脉识别技术中的图像采集,要是谁本科毕业设计敢单做个什么基于cy7c68013的usb照相机或者基于avr的播放器再或者是基于8051的闪烁小灯这类东西就等着被拍吧。。。  
这个挑战有什么意思?是能解决什么实际问题么?还是能促进科学进步?  
马老师想挑战的话,不如去申个自然科学基金之类的,然后做点实际有用的东西出来?      
-----------------------------------------------------------------------
屁话!
你的微字,你做了什么,你们北大的微字做了什么,你的本科设计怎么不去设计一片芯片?北大微电子系设计出国产的mpu吗,avr吗?mtm32吗?交大到是有一片“......
-----------------------------------------------------------------------

北大倒是有微处理器研发中心,属于计算机系,做众志系列微处理器,我有两个室友在那里,他们做的东西还行,不了解就不要随便说。。。。
你倒是有空在这里挑衅,可是别人没空陪你玩
(336837908)

出0入0汤圆

发表于 2011-1-19 17:28:52 | 显示全部楼层
83楼的想法就不错,简单明了!
(336836702)

出0入0汤圆

发表于 2011-1-19 17:48:58 | 显示全部楼层
好帖子!真希望多点这种帖子~~~能学到很多东西~
(336831129)

出0入0汤圆

发表于 2011-1-19 19:21:51 | 显示全部楼层
马老师157楼代码我认为不如查表法(83楼的思路,虽然好像还有错误,未细看)。
理由是在MCU的中断程序应该尽量缩短,以响应更重要的实时任务。这种显示方面的应该允许一些时间的误差。人眼其实没有那么高的分辨率的。把 LEd8*8这种函数放在中断程序内部去调用我感觉不是一个好方法。
(336828198)

出0入0汤圆

发表于 2011-1-19 20:10:42 | 显示全部楼层
回复【233楼】machao  
回复【230楼】twoperson
   我本科在微电子系,多了个微字和电子系干的事情很不一样。。。本科设计是手指静脉识别技术中的图像采集,要是谁本科毕业设计敢单做个什么基于cy7c68013的usb照相机或者基于avr的播放器再或者是基于8051的闪烁小灯这类东西就等着被拍吧。。。
这个挑战有什么意思?是能解决什么实际问题么?还是能促进科学进步?
马老师想挑战的话,不如去申个自然科学基金之类的,然后做点实际有用的东西出来?     
-----------------------------------------------------------------------
好意思说静脉识别?手指头有静脉么?
别拿来唬人,那东西又不是没见过,上次广东省少儿发明展有人用mini2440插个摄像头,边缘算子检测血管,非常好用。
解决实际问题可不是建立在信口开河的基础上的。现在我们就在尝试着提高别人的水平,这就是促进科学进步,而你却在制造麻烦。
国家自然基金更别提了,把那个Microkopter包装一下,连科技进步奖都能拿。大家都是中国混的,谁不知道里面的门门道道。
“在中国申请科研经费,主要是根据和某几个著名科学家的关系远近,来决定的。”----《科学》杂志


回复【258楼】banyai  
回复【248楼】wangzheyu  
回复【237楼】machao
-----------------------------------------------------------------------
我这个小本科感觉就是傻孩子说的意思,第二种方法中“time_count--”在中断中,time_count的重新赋值在main()中,那么当time_count = 0的时候,中断返回到主函数中,可能是任意一条语句的位置,而这个main函数的结构不知,是否有其他中断也不知道,那么这个重新赋值的时间就是不确定的。而第一种方法,肯定是等时间片式的,也就是说if(t_xxms_ok )这句语句不一定是精确地每250ms执行一次,但是定时器二所划分的时间片是相等的250ms。第二种方法,每次产生的时间片就可能被main函数中的其他语句“堵住”,本身就不准,再加上时间片划分的方式,执行起......
-----------------------------------------------------------------------
简单来说,假设中断1ms一次,定时时间250ms
第一种方法中:250ms后标志置位,而main可以在标志置位后250ms内清除标志,而不会出错。
第二种方法中:250ms后,count为0,main必须在1ms内把count设为250,否则count会变成255(假如count是uint8),导致计时出错。

第一种方法可以再优化一下,借鉴STM32的USB模块,可以增加一个覆盖标志ovf,如果在t_xxms_ok=1之前,t_xxms_ok没有清除,就设置ovf,提示程序时间可能出错。


回复【270楼】machao  
回复【268楼】the_exile
-----------------------------------------------------------------------
我以前用什么都没有的古董2051弄过这个。而且一些片子的i2c口不够可靠(比如stm32),很多时候还是要软仿。
-----------------------------------------------------------------------
stm32的i2c不可靠?不太会吧。这个可是标准的东西。

-----------------------------------------------------------------------
确实有问题,很多人都选择了软件模拟。话说Linux内核源码就有I2C模拟代码


回复【277楼】twoperson  
-------------------------------------------------
北大倒是有微处理器研发中心,属于计算机系,做众志系列微处理器,我有两个室友在那里,他们做的东西还行,不了解就不要随便说。。。。
你倒是有空在这里挑衅,可是别人没空陪你玩
-------------------------------------------------
不要再显摆了,众志是类似于2440的芯片,并不是MCU。
另外这东西到底能不能实际使用也很是问题。ZLG做的一个IO扩展芯片,商用勉强可以,一到工业环境就翘掉了。
做一个能演示的MCU甚至是PC并不难,UltrasparcIV的Verilog都公开了,还有什么抄不来的。
反正这是演示,稳定性,EMC等等凑合一下便能完工。
但真的要开卖,就没有这么容易了,如果只是知其然不知其所以然,肯定会完蛋。
相关资料:
http://www.pkunity.com/ReadNews.asp?NewsID=1224
http://www.laogu.com/wz_14359.htm

做人最要不得的就是死不认错。如果你不这么嚣张,没人会说你什么的,毕竟这个世界上没谁是全知全能的。
一个问题不会,只是一个知识点的问题。但如果态度有问题,其本质就完全不同了。

话说国产FPGA已经出来了,还内嵌了51核
http://www.agatelogic.com/gsjj/file3.html
根本和中科院清华北大中国科技大复旦华南理工什么的没关系~
(336827618)

出0入0汤圆

发表于 2011-1-19 20:20:22 | 显示全部楼层
马老师看漏了,我在 189楼 用的 vtimer_set(timer,t);不是软件延时,这是个完全通过中断驱动的虚拟定时器模块。
支持最多254个定时器,支持动态分配。这是我做一个比较复杂的项目(20多个模块)的时候写的。

//virtual timer
//vtimer.c

#include "../platform.h"
#include "vtimer.h"
#include "vtimer_conf.h"

//内部静态变量
static volatile uint32 vtimer_ovft[VTIMERS_NUM]      = {0};
static volatile uint8  vtimer_run[VTIMERS_NUM]       = {0};
static volatile uint8  vtimer_ovf_tag[VTIMERS_NUM] ;
static volatile uint8  vtimer_using[VTIMERS_NUM]   = {0};

//功能函数
void vtimer_init(void)
{
uint8 i;
for(i=0;i<VTIMERS_NUM;i++)
{
  vtimer_ovf_tag = 1;
  vtimer_run = 0;
  vtimer_using = 0;
}
}

void vtimer_service(void)
{
uint8 i;
for(i=0;i<VTIMERS_NUM;i++)
  {
   if(vtimer_run&&(vtimer_ovft==systick))
    {
        vtimer_ovf_tag = 1;
        vtimer_run = 0;
    }
  }
}

void vtimer_set(uint8 n,uint32 t)//开定时器,n为定时器号,t为时间(ms)
{
if(t==0)
{
vtimer_ovf_tag[n] = 1;
return;
}
vtimer_ovft[n]=systick+t;
vtimer_ovf_tag[n] = 0;
vtimer_run[n] = 1;
}

bool vtimer_ovf(uint8 n)
{
    return (bool)(vtimer_ovf_tag[n]);
}

uint8 vtimer_alloc(void)
{
  uint8 i;
  
  for(i=0;i<VTIMERS_NUM;i++)
  {
    if(!vtimer_using)
    {
      vtimer_using = 1;
      return i;
    }
  }
  return VTIMER_NULL;
}

void vtimer_free(uint8 n)
{
    vtimer_using[n] = 0;
    vtimer_ovf_tag[n] = 1;
    vtimer_run[n] = 0;
    vtimer_using[n] = 0;
}

//virtual timer configure file

//宏

//定时器个数
#define VTIMERS_NUM 4                  //最多255个

//虚拟定时器
#ifndef _VTIMER_H__
#define _VTIMER_H__

#define VTIMER_NULL       255           //无效定时器号

bool vtimer_ovf(uint8 n);               //是否时间到
void vtimer_service(void);              //服务进程函数,每个系统节拍中调用一次
void vtimer_init(void);                 //初始化虚拟定时器
void vtimer_set(uint8 n,uint32 t);      //n号定时器定时长度设定为t个系统节拍
uint8 vtimer_alloc(void);               //申请一个定时器,返回255表示没有多余定时器了
void vtimer_free(uint8 n);              //释放一个不需要用的定时器

#endif
(336822460)

出0入0汤圆

 楼主| 发表于 2011-1-19 21:46:20 | 显示全部楼层
回复【280楼】xingliu
马老师157楼代码我认为不如查表法(83楼的思路,虽然好像还有错误,未细看)。
理由是在mcu的中断程序应该尽量缩短,以响应更重要的实时任务。这种显示方面的应该允许一些时间的误差。人眼其实没有那么高的分辨率的。把 led8*8这种函数放在中断程序内部去调用我感觉不是一个好方法。
-----------------------------------------------------------------------

关于中断中的调用你说的对,我在188楼已经做了解释,我自己设计产品是放在主程序中调用的,中断中使用标志位。由于楼盖的太高了,你没注意到。

我提到的关于效率问题,不是指查表与循环的比较,主要针对新手喜欢使用软件DELAY的浪费。对于学了一个学期的学生,还做不到能体会查表快,还是 if 好的那样的层次。所以我不是指这个层次上的效率。

在上面贴上的代码中,还是有一些人使用delay函数的,这样肯定效率不高了。

效率是个综合的指标,代码短、空间占用少,完成某个功能需要的时间等,代码的可移植、可读性,甚至可靠行,都需要平衡的看待。查表执行时间是快点,但占的空间可能就多了,这个需要平衡的。

但如果使用delay_ms(),肯定是浪费效率了!你查看过目前市场上的教课书吗?80%的LED动态扫描、按键消抖的例子,都使用delay_ms(),甚至是32位的书!

有更多的人,仅仅学了点理论编程的知识,没有实践的经验,一讲效率,就是查表、使用if 还是switch。但看他的代码,到处是delay,你查100个表,提高的效率也不够一个delay浪费的。

如果单纯讲代码短,执行快,那么完成这个题目,肯定是使用汇编了。可是开发效率呢?
(336821967)

出0入0汤圆

发表于 2011-1-19 21:54:33 | 显示全部楼层
281楼的:
简单来说,假设中断1ms一次,定时时间250ms
第一种方法中:250ms后标志置位,而main可以在标志置位后250ms内清除标志,而不会出错。
第二种方法中:250ms后,count为0,main必须在1ms内把count设为250,否则count会变成255(假如count是uint8),导致计时出错。
好像不太对,不把count设置250,只是等待中,不会变成255的。
(336820632)

出0入0汤圆

 楼主| 发表于 2011-1-19 22:16:48 | 显示全部楼层
回复【282楼】warmonkey
-----------------------------------------------------------------------

我说句不好听的话,你不要生气,在此贴中贴这样的代码有点炫耀自己的感觉。

我LZ位已经写的很明白了:本科生实验测试题,2-3个小时完成。这些学生都是新手,不是天才,才学习一个学期,还有50%是不想学的(明确表示将来肯定不做这个),他们现在能看懂你的代码吗?

如果你一定要讨论,把它发到北大的BBS上吧,那里的高手多,都是做自然科学基金的大老。

我这里是小儿科,主要是打基础的。通过简单的例子来逐步掌握MCU的基本使用,掌握一些基本的、正确的方法和思路。

就是和某高手飚劲,也是在最简单的代码和结构上出的问题。可悲的是,没有几个人能全面的做出分析和解释。

至于效率,最好的结果就是看作成的东西达到什么效果。其实我提到用M16或M051做WAVE播放器,软件的编写是有一定难度的,读SD卡不难,打开文件也不难,放出声音也能做到。

但要流畅的播放音乐,比如44.1k、16位、双声道、PCM编码的WAVE,(不用D/A器件,用PWM做D/A)就比较困难了。我使用M16,现在也没有做到流畅的播放44.1k、16位、双声道的音乐,单声道非常好,双声道声音就变低了,音频数据跟不上了。

如果要练手,证明自己是软件能手,编写出的代码效率非常高,那么这个就是很好的题目。你愿意可以自己试一下。没有M16,用STM32也行。
(336820029)

出0入0汤圆

发表于 2011-1-19 22:26:51 | 显示全部楼层
"我这里是小儿科,主要是打基础的。"

where I work, if someone turned in your code to me, s/he would be fired on the spot, because any of the following issues with your code:

1) calling another routine from within isr: for an embedded programmer, that's just fundamental. a hidden bug (hidden in the sense that it works sometime of the times) like that is incredibly difficult to find in a large project. so it is incredibly costly and time consuming. anyone who doesn't know that should get a job somewhere.

2) writing non-portable code: rather than writing to a logic layer, you wrote to a physical layer at the highest level of your code.

3) not commenting on your code.

anyone who does any one of the above fundamentally doesn't understand embedded programming.
(336819853)

出0入0汤圆

发表于 2011-1-19 22:29:47 | 显示全部楼层
"那么这个重新赋值的时间就是不确定的。"

because of that, you should never "assign" a value to a timer counter register, after it has been initiated.

instead, you should increment or decrement the timer counter register to maintain timing accuracy.

"TCNT1 = tmr_offset;" for example is the wrong approach.

"TCNT1+= tmr_offset;" is the right approach.

that little "+" operator is incredibly important.
(336818799)

出0入0汤圆

 楼主| 发表于 2011-1-19 22:47:21 | 显示全部楼层
回复【282楼】warmonkey
      稍微看了187楼和282楼的代码,不知道是否有看漏掉的。问一句,实际运行过吗?我觉得8*8LED的扫描有问题。找不到LED的驱动显示代码,LED_array_set();除了定位,还有什么作用?
(336817578)

出0入0汤圆

发表于 2011-1-19 23:07:42 | 显示全部楼层
回复【284楼】xiaobendan  仲跻东
281楼的:
好像不太对,不把count设置250,只是等待中,不会变成255的。
-----------------------------------------------------------------------
count为0后,再count--,肯定会变成255;

回复【285楼】machao  
回复【282楼】warmonkey
-----------------------------------------------------------------------
我说句不好听的话,你不要生气,在此贴中贴这样的代码有点炫耀自己的感觉。
我lz位已经写的很明白了:本科生实验测试题,2-3个小时完成。这些学生都是新手,不是天才,才学习一个学期,还有50%是不想学的(明确表示将来肯定不做这个),他们现在能看懂你的代码吗?

-----------------------------------------------------------------------
汗,我只是觉得自己用得爽,也想给大家方便,就拿上来了。隐藏在一堆回复里面也谈不上炫耀。
我也没看清LZ位内容,只是因为好奇就写了个,没怎么注意马老师的要求就发上来了。

至于WAV播放器我会试着做的,嘿嘿。
(336816341)

出0入0汤圆

发表于 2011-1-19 23:28:19 | 显示全部楼层
回复【289楼】warmonkey  
-----------------------------------------------------------------------

中断里的代码是if(time_count) time_count--
到0就不会减了
(336815913)

出0入0汤圆

 楼主| 发表于 2011-1-19 23:35:27 | 显示全部楼层
回复【286楼】millwood0
"我这里是小儿科,主要是打基础的。"
where i work, if someone turned in your code to me, s/he would be fired on the spot, because any of the following issues with your code:
1) calling another routine from within isr: for an embedded programmer, that's just fundamental. a hidden bug (hidden in the sense that it works sometime of the times) like that is incredibly difficult to find in a large project. so it is inc......
-----------------------------------------------------------------------

非常正确,谢谢!

1。我在188楼解释说明过了

2。初始化代码中直接使用寄存器名并不是严重的问题。用CVAVR平台自动生成的代码,初始化部分全部使用的寄存器名。至于可移植行看做什么了。简单控制一个8*8的LED也要考虑可移植性,太过于教条了。

3。我把解释都写明了,还怎么考学生?初始化部分都有注解,其它部分是上课教的,本身就是让学生自己看后答题的。这是教学考试。
(336814844)

出0入0汤圆

 楼主| 发表于 2011-1-19 23:53:16 | 显示全部楼层
回复【290楼】the_exile
回复【289楼】warmonkey   
-----------------------------------------------------------------------
中断里的代码是if(time_count) time_count--
到0就不会减了
-----------------------------------------------------------------------

这个是一些程序员的通病,通常是不愿意仔细看别人的代码的。
偶是教师,工作的需要,不得不仔细看学生的代码,那怕再烂的,也希望从中找出点东西,给点分;另外实验中还要找出学生代码的BUG;苦啊!

不过正是这样的锻炼,所以还是可以很快发现问题,同时也从一些好的代码中学习到不少的好思路和方法。
(336813308)

出0入0汤圆

发表于 2011-1-20 00:18:52 | 显示全部楼层
这个8*8LED在这个例子里只需要扫描3*8,一次1mS,一个周期下来只需要3mS,不一定是8mS.
(336812672)

出0入0汤圆

 楼主| 发表于 2011-1-20 00:29:28 | 显示全部楼层
回复【293楼】hsztc
这个8*8led在这个例子里只需要扫描3*8,一次1ms,一个周期下来只需要3ms,不一定是8ms.
-----------------------------------------------------------------------

来了高手了。是可以的,兄弟贴个代码吧。是88扫描方便还是38扫描方便?这样做图什么?
(336812170)

出0入0汤圆

发表于 2011-1-20 00:37:50 | 显示全部楼层
【294楼】 machao

我的代码在191楼,没有M16实验板,所以代码还没测试过。


//////////////////////////////////////////显示程序
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++;
/////////////////////////////////////////////
(336812094)

出0入0汤圆

发表于 2011-1-20 00:39:06 | 显示全部楼层
补充:我不是高手 :(
(336810623)

出0入0汤圆

发表于 2011-1-20 01:03:37 | 显示全部楼层
马老师,虽然偶研究生也报了北大的,但是并不是因为北大好才报北大的,而是因为分数高想挑战一下自己,本人对您一直在教学方面的努力和辛劳表示敬佩,也觉得中国的年轻一代的培养,需要的是向您这样的认真尽责的老师,而不是那些把三分之二的时间用来搞行政的院士,也不是那些清华复旦毕业的来我校打酱油的老师十节课有八节课不去上的。虽然我们没有任何关系,但是对马老师绝对是高山仰止,景行行止,虽不能至,心向往之。坚守的三尺讲台是教师的最美丽的风景线。
(336809809)

出0入0汤圆

 楼主| 发表于 2011-1-20 01:17:11 | 显示全部楼层
回复【296楼】hsztc
补充:我不是高手 :(
-----------------------------------------------------------------------

你不要生气。要知道,这只是一道普通的考试测验题目,其目的是让学生掌握一些常用的编程方法和思路,不是编程技巧的比赛。

char dis_buff[8] = {};  

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

    对于开始学习者,首先是掌握标准的写法。一个8*8LED显示,只要真正掌握、理解和会用这样的一段代码就足够了,它可以实现任何的要在8*8LED上显示的图案和效果。---只要你把要显示的东西正确的填入显示缓冲区dis_buff[8]中就可以了。

这样写的另一层意义在于,低层和上层分开了,层次非常清楚。这个同使用点阵LCD屏相同的道理。

考试中增加了一点难度,在于如何方便的计算可控制移动的LED。只扫3行,我说过是可以的,但不是我考察学生的目的,也有点过了。

还是一句话,为什么要扫3行?使用这样的技巧你得到的什么,代码短了?容易阅读?容易理解?节省了编程时间?还是运行效率?如果你设计一个类似使用8*8LED的产品的话,你认为扫8行好,还是扫3行好?
回帖提示: 反政府言论将被立即封锁ID 在按“提交”前,请自问一下:我这样表达会给举报吗,会给自己惹麻烦吗? 另外:尽量不要使用Mark、顶等没有意义的回复。不得大量使用大字体和彩色字。
您需要登录后才可以回帖 登录 | 注册

本版积分规则

手机版|Archiver|amobbs.com 阿莫电子论坛 ( 公安交互式论坛备案:44190002001997 粤ICP备09047143号 )

GMT+8, 2021-9-22 07:34

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

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