amoBBS 阿莫电子论坛

 找回密码
 注册
搜索
bottom↓
查看: 5528|回复: 58

给马老师交卷:avr sd waveplayer

 关闭 [复制链接]
发表于 2013-5-20 22:42:10 | 显示全部楼层 |阅读模式
本帖最后由 tomzbj 于 2013-5-21 11:39 编辑

马老师在我blog上的原文:
“用m16加一片lm324,设计一个读取sd卡上wave文件,并播放的wave播放器,看谁做的好
尝试过就知道了,关键还是要做的好。参考一下我编写教程第2版的第19章,有什么心得可以到我的讨论组交流。”

我用mega8,16.9344M, 14位r-2r实现了流畅播放16位 44.1kbps单声道wav。 双声道没试,估计超点频应该差不多了。

这次用了两晚上焊电路,周末两天加今天晚上调试。如果再加上3月份用8位pwm做的一次实验,那就是一共用了五个晚上和四个全天时间,hehe

电路图见附件
主程序如下,其他初始化、uart、sd卡操作、petit fs之类就不贴了
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

#include "init.h"
#include "uart.h"
#include "sd.h"
//#include "fifo.h"
#include "pttfs/pff.h"
#include "pttfs/diskio.h"

#define FIFOSIZE 384

volatile static enum { NOTREADY, READY }  play_ready;

volatile uint16_t *rear, *front;
uint16_t    fifo_buf[FIFOSIZE];
FATFS   fs;

/* 全部fifo操作改用宏实现 */
#define fifo_init() do { front = rear = fifo_buf; } while(0)
#define fifo_is_empty() ( front == rear )
#define fifo_is_full() ( rear+1 == front || rear+1 == front+FIFOSIZE )
#define fifo_del(e) do { e = *front; front++; if( front == fifo_buf + FIFOSIZE ) front = fifo_buf; } while(0)

/*inline int8_t  fifo_del( uint16_t *e ) {

    if( fifo_is_empty() )
        return -1;

    *e = *front;
    front++;
    if( front == fifo_buf + FIFOSIZE )
        front = fifo_buf;
    return 0;
}*/
/*
int8_t  fifo_insert( uint16_t e ) {
    if( fifo_is_full() )
        return -1;
    *rear = e;
    rear++;
    if ( rear == fifo_buf + FIFOSIZE )
        rear = fifo_buf;

    return 0;
}*/
ISR( TIMER0_OVF_vect ) {

    uint16_t    data;

    TCNT0 += (256-48); // 16934400 / 8 / 48 = 44100
    if( play_ready == NOTREADY )
        return;
    if( !fifo_is_empty() )
        fifo_del( data );
    else
        data = 0x0;

    data += 0x8000;

/*    PORTD = ( PORTD & 0x3 ) | ( (data >> 8 ) & 0xfc );
*/
     /* 用14个io拼r-2r, 比用pwm da的方式灵活些。如果用m16就容易多了 */
    PORTB = ( PORTB & 0xfc ) | ( data >> 14 );
    PORTD = ( PORTD & 0x3 ) | ( (data >> 6 ) & 0xfc );
    PORTC = ( data >> 2 ) & 0xff;
}

uint16_t    get_avail_mem(void) {
    uint8_t *p;
    uint16_t    i;
    for( i = 1; i < 65534; i++ ) {
        p = malloc(i);
        if( !p )
            break;
        free(p);
    }
    return i-1; /* 最后一次分配成功的栈内存大小 */
}

void init() {

    play_ready = NOTREADY;
    init_devices();

    fifo_init();

    disk_initialize();
    pf_mount(&fs);

}

void    listfile( char *path ) {

    DIR     dir;
    FILINFO    finfo;

    int n = 256;
    pf_opendir( &dir, path );
    while ( pf_readdir( &dir, &finfo ) == FR_OK && n > 0 ) {
        n--;
        if( finfo.fname[0] == '\0' )
            continue;        
        if( strstr( finfo.fname, ".WAV" ) ) {
            uart_puts( finfo.fname );
            uart_puts( "\r\n" );
            play_back( finfo.fname );
        }
    }
}


void    skip_wav_header() {

    WORD n;
    uint8_t i,ret;
    do {
        ret = pf_read( (void *)fifo_buf, 4, &n );
    } while( strncasecmp( (char *)fifo_buf, "fmt ", 4 ) );
    {
        DWORD size;
        struct WAVE_FORMAT {
            WORD wFormatTag;
            WORD wChannels;
            DWORD dwSamplesPerSec;
            DWORD dwAvgBytesPerSec;
            WORD wBlockAlign;
            WORD wBitsPerSample;
        } fmt_chunk;

        ret = pf_read( &size, 4, &n );
        ret = pf_read( &fmt_chunk, sizeof(fmt_chunk), &n );
        /*        sprintf( (char *)fifo_buf, "%u channel, %lubps, %ubits",
                  fmt_chunk.wChannels,
                  fmt_chunk.dwSamplesPerSec,
                  fmt_chunk.wBitsPerSample );
                  uart_puts( (char *)fifo_buf );*/ /* 显示wave文件头信息 */
        i = size - sizeof(fmt_chunk);
        while( i>0 ) {
            pf_read( fifo_buf, 1, &n );
            i--;
        }
    }
    pf_read( fifo_buf, 4, &n );
    //    uart_write( (char *)fifo_buf, 4 );
    /* 实际可能还需要跳过fact等trunk,不过无所谓了,不影响播放 */
}

void    play_back( char *path ) {

    register uint16_t i;
    register uint16_t    *ptr;
    int8_t ret;
    WORD n;
    register WORD nn = 0;

    ret = pf_open( path );   
    skip_wav_header();

    do {
        /*        pf_read( buf, sizeof(buf), &n );
                  for( i = 0; i < n; i+=2 ) {
                  do {
                  ret = fifo_insert( *(uint16_t *)(&buf) );
                  } while( ret == -1 );
                  }
                  continue;*/   /* 以上实现逐个数据插入fifo,22.05kbps时可以,44.1kbps就不行了  */

        if( fifo_is_full() )
            continue;
        cli();
        ptr = rear;

        if( front <= rear ) {

            if ( front == fifo_buf ) {
                nn = fifo_buf + FIFOSIZE - rear - 1;
                nn *= sizeof( fifo_buf[0]);
                rear = fifo_buf + FIFOSIZE - 1;
            }
            else {
                nn = fifo_buf + FIFOSIZE - rear;
                nn *= sizeof( fifo_buf[0]);
                rear = fifo_buf;
            }
        }
        else if (front > rear ) {
            nn = front - rear - 1;
            nn *= sizeof( fifo_buf[0]);
            rear = front - 1;
        }   
        sei();
        pf_read( ptr, nn, &n );
        play_ready = READY;

        if( ! ( PIND & _BV(0) ) ) {
            while( ! (PIND & _BV(0) ) );
            break;
        }
    } while( nn == n );
}

int main() {

    init();

    { sprintf( fifo_buf, "free mem: %d\r\n", get_avail_mem() ); uart_puts(fifo_buf); }

    listfile( "/" );

    while(1);

    return 0;
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
发表于 2013-5-21 08:31:40 | 显示全部楼层
顶楼主   
发表于 2013-5-21 08:32:57 | 显示全部楼层
lgg88 发表于 2013-5-21 08:31
顶楼主

+1                    
发表于 2013-5-21 11:06:58 来自手机 | 显示全部楼层
顶楼主。   
发表于 2013-5-21 11:28:54 | 显示全部楼层
马老师 快来审阅审阅呢
发表于 2013-5-21 12:26:00 来自手机 | 显示全部楼层
Mark…
来自:amoBBS 阿莫电子论坛 Windows Phone 7 客户端
发表于 2013-5-22 16:16:32 | 显示全部楼层
mark     
 楼主| 发表于 2013-5-26 00:04:35 | 显示全部楼层
马老师最近有点忙啊?学生这几天答辩呢?
发表于 2013-6-8 00:37:25 | 显示全部楼层
"14位r-2r实现了流畅播放16位",意味着丢掉了最后的2位,尽管人的耳朵可能已经分辨不出了。但是如果放双声道,需要使用大量的I/O口。使用M16的话,一共32个I/O口,14+14 = 28,还有4个I/O,给SD卡,要加几个控制按键都没有办法。

我使用M16,可以流畅播放44.1K,8位双声道, 44.1K,16位单声道。对于44.1K,16位双声道的WAV,能放,但不是非常流畅,主要是RAM限制,缓冲不够大。最佳的缓冲是4*256字节。选取256长度,是为了提高数组下标指针的处理运算速度(下标只要加1,不需要判断,加到256自动回0)。

2个8位定时器,产生2个8位PWM,高、低各8位PWM,合成16位PWM位一路,1个16位定时器也是产生2路8位的PWM,合成另外1路16位PWM。输出接一个轨到轨的4运放,做2路加权低通滤波,实现两路16位DAC。

4路PWM输出,只用掉4个I/O口,还可以加LED指示,按键,已经USART通信控制,这样才可能用到实际的产品中。


软件和硬件的合理搭配,最高的发挥MCU的效率是关键,不是一般人能做好的。

我在培训工程师和给研究生上课时,摸底的问题就是使用一片8位MCU,产生一个1M的方波(我在坛里发过这个题目)。至少到今天,没有一个工程师或学生一开始就能给出正确的答案。尽管他们中有许多人已经有着5-6年的硬件工程师经历。顺便说一下,这个题目用标准架构的51是做不到的,需要用改进的兼容51的MCU才行。
发表于 2013-6-8 00:53:04 | 显示全部楼层
前几年,我们专业成绩最好的一名男生,直升研究生。他原一心一意要跟着我。后来有推荐到外校的机会,他选择了北大。我说,北大的名气比我的名气大多了,你去北大当然可以镀上厚厚的黄金,但不一定能学到你想学的,你喜欢的东西。结果他去了北大,但是现在如同我分析的,并不顺心,原因就不说了。

在给一道高手练手的设计题:

设计题六,设计一个简易的频率计
1,        使用8个LED数码管显示测试结果
2,        使用8个LED指示不同的频率量程,比如MHz、KHz、Hz
3,        给出你的频率计测试频率所采用的方法、能够测试频率的范围,不同量程测试结果的误差、以及测量频度(每秒几次)

条件如下:

1、使用MCU:51架构建议使用STC12C5A60S2;AVR架构使用M16
2、系统时钟4~12M(理论上认为100%准确,不考虑误差),尽量少用外围器件
3、主要为硬件电路设计和与之配合的系统软件设计,系统软件不使用OS
发表于 2013-6-8 14:47:18 | 显示全部楼层
忘了给打分了。
在学校环境体制下,优秀!
在我这里,只能算70分。
我在这里出题的目的,不是培训驾驶员的,是寻找和训练F1车手的。
 楼主| 发表于 2013-6-8 21:28:45 | 显示全部楼层
本帖最后由 tomzbj 于 2013-6-8 21:30 编辑
machao 发表于 2013-6-8 00:53
前几年,我们专业成绩最好的一名男生,直升研究生。他原一心一意要跟着我。后来有推荐到外校的机会,他选择 ...


不好意思,频率计我09年就做过了,不记得用了几天了,顶多不超过一周

信号进来,2SK246缓冲,HC04整形,74HC393和Mega8的T1计数器一起计数,Mega8用异步时钟+32.768k晶振产生1s信号。显示是1602LCD。
理论能测到2^24=16.777216MHz,实测到10M左右。
后来买了74AC393,应该能测到100M左右了,一直没想起来再试。
发表于 2013-6-8 22:06:00 | 显示全部楼层
tomzbj 发表于 2013-6-8 21:28
不好意思,频率计我09年就做过了,不记得用了几天了,顶多不超过一周

信号进来,2SK246缓冲,HC04整形, ...

单用M8,系统16M,测8M没有问题,精度可以非常高。你没有给出你所使用的方法理论上的误差是多少。另外如果使用8位LED数码管,动态扫描,你的系统能达到要求吗?
 楼主| 发表于 2013-6-8 22:39:30 | 显示全部楼层
machao 发表于 2013-6-8 22:06
单用M8,系统16M,测8M没有问题,精度可以非常高。你没有给出你所使用的方法理论上的误差是多少。另外如 ...

为啥要用8位数码管动态扫描?放着max7219、tm1640之类驱动ic不用,把mcu浪费在这种事上?
再说了,8个数码管要用多少电,还干不干正事了?手持频率计啊,装电池用的,整机耗电量也就几个mA
您的wave播放器能放44.1k的wave,硬件不改,能放32k或者48k的么?把n个各种采样率各种位数的wave放在一起,能让它们按顺序用正确的参数播放么?
我给您打个分,不好意思,也给70吧
 楼主| 发表于 2013-6-8 22:46:47 | 显示全部楼层
本帖最后由 tomzbj 于 2013-6-8 22:52 编辑
machao 发表于 2013-6-8 00:37
"14位r-2r实现了流畅播放16位",意味着丢掉了最后的2位,尽管人的耳朵可能已经分辨不出了。但是如果放双声道 ...


至于io够不够的问题,这太容易了,上4片不值钱的hc373,只用8+4=12个io口就够放16位双声道了,我就是懒得焊一堆飞线而已。用mega8是因为我以前囤了几个mega8,以及做了几块mega8的开发板,正好用上。 就这东西来说mega16只是多了几个io,ram不多,频率一样,没太大优势
发表于 2013-6-8 23:03:20 | 显示全部楼层
马老师的题目是在最大限度内使用单片机的资源,尽量少用或者不用外部器件。这种题目类似于11年的电子设计大赛的综合评测那道题。
发表于 2013-6-8 23:34:00 | 显示全部楼层
我上大学时做过类似的,用的STC12C5A单片机,PWM 8bit立体声,效果还凑合,22.1184M晶振压力不大。

当时很热血(当时做东西都是自己研究明白自己写,不喜欢搬来人家的代码就用),自己找了几个FAT16的文章啃,然后计算各种偏移,格式化一个内存卡,对着WinHex各种比对,最后实现了读取功能以及FAT16根目录文件检索。
而且FAT文件系统中特别地对簇链接使用了缓存,一次读FAT表,可能的话,多缓存几个簇的链接,这样不必频繁查FAT表。之所以这样做也是因为我手头恰好有一个16M的卡,测试时发现读16M卡特别慢,后来才发现16M卡上一个扇区就是一个簇,读完一个扇区就要读一次FAT表,当然没效率了,使用这个缓存后,假使缓存8个簇,也才使用16个字节的内存,却可以最多减少7次读操作,很划算。

还记得一个事,磁盘0扇区上存放主分区记录,有4个位置,结果几乎网上所有的代码都认为第一个记录就是第一个分区,然后判断它是不是FAT16,若不是,则显示错误。结果我用pq分区,人家分出来的第一个是空记录,甚至第三个才是一个主分区记录。正确的做法应该是扫描4个记录进行判断。真不知道网上的代码怎么都这么自信。

STC这个单片机有1K的外部RAM,用做两个扇区缓存。播放前先把缓存都填满。然后从第一个开始播放,播放完一个扇区后,立马开始读取一个扇区,填充到刚播放完的缓冲空间中,播放进程则自动进入另一部分缓冲空间。这样可以保证播放进程不被打断,跑起来很爽。我之前了解些windows平台上音频设备是怎么操作的,虽然没真正做过win平台的,但这种做法很值得学习。

当时的代码还可以下载:http://static.zhujinliang.com/blog/uploads/2012/06/wav_play.rar
现在看不论代码实用程度还是美观程度,都有很大改进空间,不过仅作为个兴趣爱好而已,玩过了,足够了。
现在想起还有个问题,产生的44100Hz时钟不准,221184晶振不能精确分频到44100(常见晶振都不能),肯定歌曲播放速度变化了。貌似USB声卡啥的是重采样到48000来做的。
入口在test.c,启动后自动搜索卡上根目录合适的wav文件,自动下一曲下一曲。。。串口有输出。貌似是这样的,记不清了。

大学玩了两年,都是把代码当作艺术,根据自己的想法让代码优美些,现在当个半拉子程序员,开始制造垃圾代码了,项目进度催啊。怀念大学无忧无虑的时光,可以不用上课天天窝宿舍鼓捣自己喜欢的。
发表于 2013-6-9 03:20:31 | 显示全部楼层
tomzbj 发表于 2013-6-8 22:39
为啥要用8位数码管动态扫描?放着max7219、tm1640之类驱动ic不用,把mcu浪费在这种事上?
再说了,8个数 ...

70分少了,按你的打分标准,至少应该85分。

“您的wave播放器能放44.1k的wave,硬件不改,能放32k或者48k的么?把n个各种采样率各种位数的wave放在一起,能让它们按顺序用正确的参数播放么?
我给您打个分,不好意思,也给70吧”

就是“把n个各种采样率各种位数的wave放在一起,能让它们按顺序用正确的参数播放么?”这一点扣分啦?

好吧,贴上我书中的代码(第二版的光盘里有,买书不带光盘,本站和网上有免费下载)看一下:



         if(dest == 0)                                                                /* 如果指针被赋值为0, 直接处理音频数据 */      
        {
                if (channel_flag == 0x12)                                // 16bit 2ch
                {
                        do                                                                /* 接收指定的字节数 */
                        {
                    m_data_0 = rcv_spi();
                                if (read_step == 1)                                        // 左声道高8位
                                {
                                        if(m_data_0 <= 0x80)
                                                pre_data = m_data_0 + 0x80;
                                        else
                                                pre_data = m_data_0 + 0x81;

                                }
                                else if (read_step == 3)                                        // 右声道高8位
                                {
                                        if (m_data_0 <= 0x80)
                                                m_data_0 = (m_data_0 + 0x80);
                                        else
                                                m_data_0 = (m_data_0 + 0x81);
                                        m_data_0 = (pre_data + m_data_0) >> 1;        // (左+右)/2
                                       
                                 while(FifoCt == FULL_bsize);               /* 如果缓冲区满,则等待 */
                                         Buff[FifoWi] = m_data_0;               /* 一旦缓冲区有空位,则接收音频数据填入 */
                                 #asm("cli")                            /* 修改音频数据的个数之前首先关闭中断 */
                                 FifoCt ++;                             /* 修改音频数据 */
                                 #asm("sei")                            /* 修改完成后打开中断 */
                                        ++FifoWi;
                                }
                                if (++read_step > 3) read_step = 0;                               
                        }while(--cnt);
                }
                else if(channel_flag == 0x0A)                                // 8bit 2ch
                {
                        do                                                                /* 接收指定的字节数 */
                        {
                    m_data_0 = rcv_spi();
                                if (read_step_bit)                                        // 右声道高8位
                                {
                                        if (m_data_0 <= 0x80)
                                                m_data_0 = (m_data_0 + 0x80);
                                        else
                                                m_data_0 = (m_data_0 + 0x81);
                                        m_data_0 = (pre_data + m_data_0) >> 1;        // (左+右)/2
                                       
                                 while(FifoCt == FULL_bsize);               /* 如果缓冲区满,则等待 */
                                         Buff[FifoWi] = m_data_0;               /* 一旦缓冲区有空位,则接收音频数据填入 */
                                 #asm("cli")                            /* 修改音频数据的个数之前首先关闭中断 */
                                 FifoCt ++;                             /* 修改音频数据 */
                                 #asm("sei")                            /* 修改完成后打开中断 */
                                        ++FifoWi;
                                }
                                else                                         // 左声道高8位
                                {
                                        if(m_data_0 <= 0x80)
                                                pre_data = m_data_0 + 0x80;
                                        else
                                                pre_data = m_data_0 + 0x81;
                                }
                                read_step_bit = !read_step_bit;                               
                        }while(--cnt);
                }
                else if (channel_flag == 0x11)                        // 16 bit  1ch
                {
                        do                                                        /* 接收指定的字节数 */
                        {
                    m_data_0 = rcv_spi();
                                if (read_step_bit)                                        // 只读取高8位
                                {
                                        if (m_data_0 <= 0x80)
                                                m_data_0 = m_data_0 + 0x80;
                                        else
                                                m_data_0 = m_data_0 + 0x81;
                                 while(FifoCt == FULL_bsize);               /* 如果缓冲区满,则等待 */
                                         Buff[FifoWi] = m_data_0;               /* 一旦缓冲区有空位,则接收音频数据填入 */
                                 #asm("cli")                            /* 修改音频数据的个数之前首先关闭中断 */
                                 FifoCt ++;                             /* 修改音频数据 */
                                 #asm("sei")                            /* 修改完成后打开中断 */
                                        ++FifoWi;
                                }
                                read_step_bit = !read_step_bit;
                        }while(--cnt);
        }
                else                                                                // 8bit 1ch
                {
                        do                        /* 接收指定的字节数 */        
                        {
                                m_data_0 = rcv_spi();
                         while(FifoCt == FULL_bsize);/* 如果缓冲区满,则等待 */
                         Buff[FifoWi] = m_data_0;        /* 一旦缓冲区有空位,则接收音频数据填入 */
                         #asm("cli")                                        /* 修改音频数据的个数之前首先关闭中断 */
                         FifoCt ++;                                        /* 修改音频数据 */
                         #asm("sei")                                        /* 修改完成后打开中断 */
                                ++FifoWi;
                      }while(--cnt);
                }       
        }

以上是单双通道,8、16位音频数据的处理代码。由于是教程的简单例子,所以硬件简化了,只是使用了一个8位PWM输出加简单的电阻电容的低通(见我的教程538页原理图),所以把数据转换成8位单通道播放的。所以各种位数和单双声道没有问题。


case FCC('f','m','t',' ') :                                                /* 'fmt ' chunk */
                                if (sz > 100 || sz < 16) return 0;                        /* Check chunk size */
                                pf_read(Buff, sz, &rb);                                                /* Get content */
                                if (rb != sz) return 0;
                                if (Buff[0] != 1) return 0;                                        /* Check coding type (1) */
                                if (Buff[2] != 1 && Buff[2] != 2) return 0;        /* Check channels (1/2) */
                                if (Buff[14]!= 8 && Buff[14]!= 16) return 0;/* Check resolution (8/16) */
                                channel_flag = Buff[2] | Buff[14];
       
                                if (LD_WORD(&Buff[4]) == 8000)                        /* Sampling freq */
                                {
                                        OCR1AH=0x05;
                                        OCR1AL=0x65;
                                }       
                                else if (LD_WORD(&Buff[4]) == 22050)
                                {
                                        OCR1AH=0x01;
                                        OCR1AL=0xFF;
                                }       
                                else if (LD_WORD(&Buff[4]) == 44100)
                                {
                                        OCR1AH=0x00;
                                        OCR1AL=0xFF;
                                }
                                else
                                {
                                        return 0;       
                                }
                                //OCR0A = (BYTE)(F_CPU/8/LD_WORD(&Buff[4]))-1;        /* Sampling freq */
                                break;

                        case FCC('d','a','t','a') :       

以上是处理标准的 8K 22.05K 44.1K的处理,OCR1AH,和OCR1A值就是PWM的频率,根据WAV频率不同改变调整。
考虑设计标准适合44.1K,22.05k和8k,8位定时器产生44.1K的PWM,时钟最佳选择是:11.2896M\22.5792M\33.8688M (44100*256 = 11289600)这3个都是标称晶体,市场上有卖的。换句话讲,就是配合44.1用的,就如11.0592M就是配合UART使用的一样的道理。

书上使用的是11.2896M系统时钟(受到AVR最高时钟16M限制),播放44.1、22.05、8K没有问题。播放32K的音质会有影响,播放48K的效果会很差。

2个要求,一个没有问题,另一个有问题,所以扣掉的30分,至少应还给我15分。85分
===============================================================

我编写教程第2版的第19章题目是“公交车语音报站器”,播放44.1、22.05、8K足够了,加上个运放做低通、功放、喇叭,基本成本也就20元吧(TF卡不算),带4个按键,2个指示LED,一个串口。

实际的产品是语音电梯楼层报站器,使用STC芯片(5元),比M16便宜。系统时钟33.8688M,也是播放44.1、22.05、8K,单双,8/16位都能处理,实际是2路8位PWM输出,组成1个单通道的16位音频输出。

做的最好的是用一片M051芯片(9元),32位的MCU,晶体是8M,设置内部倍频系数,实际系统时钟为45.1584M。播放44.1、22.05、8K,单双,8/16位都能处理,4路8位PWM输出,构成2通道16位音频输出。音质刚刚的。用于申请专利,去年拿到发明专利的授权。有图为证,再加5分吧,怎么也得混个90分,要不就丢脸啦。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
发表于 2013-6-9 03:24:51 | 显示全部楼层
放着max7219、tm1640之类驱动ic不用,


max7219 works well under 5v.

At 3.3v, sometimes it cannot be initiated reliably.
发表于 2013-6-9 08:57:47 | 显示全部楼层
machao 发表于 2013-6-9 03:20
70分少了,按你的打分标准,至少应该85分。

“您的wave播放器能放44.1k的wave,硬件不改,能放32k或者48 ...

既然是技术探讨,放着有技术内涵的不去思考,却在这种皮毛的东西上计较。读个WAV文件头算个定时器参数跟解决一个缓冲问题哪个更有意思?LZ不是程序员啊。

劝LZ别跟着课本跑了,课本上有啥跟实际生产相关的东西,端口扩展都用intel的什么8开头的40脚芯片(芯片型号记不得了),真当硬件不要钱了(不说还得用XDATA操作,还得算地址了)。放着MCU剩余计算能力不用,再加一个芯片,怕单片机累坏了?

再话说14位r-2r?你的电阻精度够么?就算买到0.1%精度的电阻,跟14位精度还差着10倍呢。

不小心说多了。。。
 楼主| 发表于 2013-6-9 10:01:29 | 显示全部楼层
zhujinliang2124 发表于 2013-6-9 08:57
既然是技术探讨,放着有技术内涵的不去思考,却在这种皮毛的东西上计较。读个WAV文件头算个定时器参数跟 ...

不好意思,我在北大本科是学化学的,研究生改物理,现在做的东西也跟电子没太大关系,纯粹业余爱好。
我在北大最多就是个中下水平。您说的课本是哪一本,对不起,我没上过这方面的课。
我只是让马老师看看,北大学生有没有弱到要五个人花一个月时间来搞这玩意儿的程度。
发表于 2013-6-9 15:47:00 | 显示全部楼层
使用一片8位MCU,产生一个1M的方波
用过stm8的时钟输出功能,
今天看到个帖子,发现stc也有时钟输出功能了,至于stc能否输出1M方波就不知道了
发表于 2013-6-9 16:45:31 | 显示全部楼层
标准8051配6M晶振,ALE口自动输出1MHZ方波,搞定
等等,会不会占空比不是50%,哈哈,还要学习
发表于 2013-6-9 19:36:20 | 显示全部楼层
tomzbj 发表于 2013-6-9 10:01
不好意思,我在北大本科是学化学的,研究生改物理,现在做的东西也跟电子没太大关系,纯粹业余爱好。
我 ...

噢,北大高材生。原来深层次的含义是为几年前的师哥打抱不平来了。

奇怪的是北大正牌专业学生到没有出面,是否你只是前哨探路的?

我说过的,现在也坚持:一个月,全部自己玩,文件系统也要自己写,北大高材生嘛,难度当然要稍微大一点。用实际的东西说话。请正规军出山吧。

最后表扬你一下,尽管你是杂牌的,但你的水平确实应该比许多正规军还要高些。
发表于 2013-6-9 19:38:25 | 显示全部楼层
zhugean 发表于 2013-6-9 16:45
标准8051配6M晶振,ALE口自动输出1MHZ方波,搞定
等等,会不会占空比不是50%,哈哈,还要学习 ...

如果需要一个10M的波怎么办?
 楼主| 发表于 2013-6-9 19:45:39 | 显示全部楼层
machao 发表于 2013-6-9 19:36
噢,北大高材生。原来深层次的含义是为几年前的师哥打抱不平来了。

奇怪的是北大正牌专业学生到没有出面 ...

马老师,我3月份的时候已经先用自己写的文件系统试过了,当时只试了用8位pwm播放,没继续试。这次才改用petit fs的。
我在主贴里说了,上次用了两晚上加两白天,这次是三晚上加两白天。

之前那位不是我师兄,算是师弟吧。
 楼主| 发表于 2013-6-9 20:04:27 | 显示全部楼层
tomzbj 发表于 2013-6-9 19:45
马老师,我3月份的时候已经先用自己写的文件系统试过了,当时只试了用8位pwm播放,没继续试。这次才改用p ...

我这个杂牌军也只敢跟这里的正规军凑合比比了,在北大啥都不是
师弟们的水平我是知道的,不靠谱的当然也有,靠谱的搞这东西,估计就是一下午,顶多两晚上的事吧
 楼主| 发表于 2013-6-9 20:11:51 | 显示全部楼层
zhugean 发表于 2013-6-9 16:45
标准8051配6M晶振,ALE口自动输出1MHZ方波,搞定
等等,会不会占空比不是50%,哈哈,还要学习 ...

我猜马老师的意思大概是说,至少得一条XOR、一条OUT或者MOV之类输出到端口、一条JMP之类跳转,加起来估计得四五个周期,标准51要乘以12,得48M或60M主频才行了
最高主频按24M算的话,至少得换6个周期或者更短的加强版51才行。
发表于 2013-6-9 20:20:22 | 显示全部楼层
tomzbj 发表于 2013-6-9 20:11
我猜马老师的意思大概是说,至少得一条XOR、一条OUT或者MOV之类输出到端口、一条JMP之类跳转,加起来估计 ...


询问22楼的朋友
 楼主| 发表于 2013-6-9 20:39:37 | 显示全部楼层
machao 发表于 2013-6-9 20:20
询问22楼的朋友

stm8我玩过几天,不过时钟输出没试过。按手册上的说法,相当强大,输出HSE/HSI/LSI都行,还可以按FCPU分频,比mega48的输出主时钟或者8分频方便。
不过工具链比gcc差远了,所以后来还是回到玩avr了。
发表于 2013-6-9 21:33:34 | 显示全部楼层
tomzbj 发表于 2013-6-9 20:39
stm8我玩过几天,不过时钟输出没试过。按手册上的说法,相当强大,输出HSE/HSI/LSI都行,还可以按FCPU分 ...

哦,忘了,你是用AVR的M8做播放器的。那么手册看过几遍?AVR每个定时器的比较匹配输出功能有什么实际的用途?不太像北大的学生,手册上现成的东西,不应该问的。
 楼主| 发表于 2013-6-9 21:46:47 | 显示全部楼层
machao 发表于 2013-6-9 21:33
哦,忘了,你是用AVR的M8做播放器的。那么手册看过几遍?AVR每个定时器的比较匹配输出功能有什么实际的用 ...

手册又不是小说,哪个功能我要用到了才会看。AVR每个定时器的比较匹配输出功能有什么实际的用途?我不需要用干嘛要知道,你直接问回字有几种写法吧
发表于 2013-6-9 21:50:29 | 显示全部楼层
看擂台赛了。
北大研究生挑战华中(华南?)老师傅。
 楼主| 发表于 2013-6-9 22:09:32 | 显示全部楼层
tiger5 发表于 2013-6-9 21:50
看擂台赛了。
北大研究生挑战华中(华南?)老师傅。

华东吧?
我非常尊敬马老师,只是觉得他之前说这东西要五个北大学生弄一个月,有点不爽而已~~

我这个纯外行初二才学C语言,我那几个NB师弟有的上小学时就上汇编crack学校机房了,有的不会写字的时候就会拿烙铁了,
现在有在Google干了两年然后跳槽现在在微软总部的,有在航天五院搞载人你们看电视的时候人家的设备在电视里工作的,让他们五个人弄一个月做个wave播放器?开什么国际玩笑?
发表于 2013-6-9 22:58:43 | 显示全部楼层
马老师的擂台是一个拼技巧和熟练程度的小竞赛,虽然新颖夺目,但是在MCU飞速发展的今天,总用一个AVR来发挥,难免感到有些单调

有种XMOS的多核MCU很好玩,除了高速IO以外没有外设,但有几个并行的CPU核,使用CPU核来模拟各种外设,有一点类似FPGA的开发方式,发挥的空间很大


发表于 2013-6-10 00:19:34 | 显示全部楼层
tomzbj 发表于 2013-6-9 21:46
手册又不是小说,哪个功能我要用到了才会看。AVR每个定时器的比较匹配输出功能有什么实际的用途?我不需 ...

你不是问如何产生1M的时钟方波吗,我给的就是答案呀。明白一件事,教北大的学生和我自己学校的学生一样。大部分是要把饭送到口内才会吃的,告诉饭在哪里,自己也不愿意去拿的。

使用该功能,10M系统时钟,可以输出最高5M的方波,精确程度仅仅依赖系统时钟的精准程度。
只需要几句初始化代码,以后就是就是硬件自己做了,MCU去做其他事情就可以了。
 楼主| 发表于 2013-6-10 00:48:50 | 显示全部楼层
machao 发表于 2013-6-10 00:19
你不是问如何产生1M的时钟方波吗,我给的就是答案呀。明白一件事,教北大的学生和我自己学校的学生一样。 ...

...您是这个意思?我可没问,不过我确实这么用过。

问题在于,如果只是要1MHz这样的整数值之类,我用8M晶振,熔丝设置PB0输出8分频不就行了,程序里完全不用管。前段时间做的某测量仪表就是用M48输出1.5M时钟给AD7705的。

如果是要零碎、可以随时调整的值,比如123.456kHz这样,用PWM这样能达到的精度就太差了,倒不如用avr搭dds了,在几十到几百kHz的频率上比用ad9850方便,虽然mcu满载运转,加几个按钮调整频率还是可以的。
我前段时间做了一个这样的信号发生器,用M8超频到24M,可以输出最高到1MHz左右的正弦波、方波、三角波,100k以下波形很好,最小频率间隔可以到1Hz,在示波器上可以准确分辨出68.500kHz和68.501kHz。

基本思想是这两位的,http://www.myplace.nu/avr/minidds/  http://codeandlife.com/2012/03/13/fast-dds-with-atmega88/
稍微修改了一下
loop:
    add     r28, r22             ; 1 clock
    adc     r29, r23             ; 1 clock
    adc     r30, r24             ; 1 clock
    lpm                         ; 3 clocks
    out     _SFR_IO_ADDR(PORTD), r0  ; 1 clock
    sbic    _SFR_IO_ADDR(PINB), 0   ; 1 clock
    rjmp    loop                    ; 2 clocks
    ret

每个按键连一个二极管下拉PB0,跳出dds循环后再判断按的是哪个键。
发表于 2013-6-10 01:19:19 | 显示全部楼层
tomzbj 发表于 2013-6-9 22:09
华东吧?
我非常尊敬马老师,只是觉得他之前说这东西要五个北大学生弄一个月,有点不爽而已~~

不要老是拿别人的荣耀给自己贴金。
北大是中国一流高校,招收的学生也是一流的,国家投入更多的经费,所以比其他高校多培养出一些优秀精英那是应该的。北大也不会因为我说北大的学生不行就失去了他光环。

但北大的学生也不会个个都是优秀分子,平庸之人肯定有。这些人给北大的脸上抹黑你知道吗。

我的这个专栏不是讨论航天飞机,咱也没那个本事。所以老老实实讨论一些简单的MCU应用设计的方法。其实很多也不是我创造的方法,就是因为编写教材,多看了几遍器件手册,稍微有点心得。聪明的人自己看看数据手册就都会知道的。

我根本也不知道你是北大的,你的WAVE作业我说过,在学校环境下给打了优秀。我自己专业上课的学生,一个学期下来没几个优秀的(当然及格的一大批,这个原因地球人都知道)。对于到这个论坛里点名让我看的,只要不是要什么代码,我都感到高兴,并同时激励他要深入的学下去。因此我给了第2个标准分数70分。因为在设计上确实存在一些问题,可以改进,同时我感到尽管DD做出来了,可能还是有些基本概念和方法不是掌握的非常清楚和透彻(这个在你后面的帖子里面证实了)。

在此之前,与北大无关。

然而后面的跟帖让我吃惊,你把北大搬了出来,还想用你这个DD为几年前的事情证明些什么。

北大的学生应该是全中国最聪明的一部分,是不需要到这个如此技术含量低的坛子里来证明什么,来这里品头论足的,甚至是来这里所谓的“讨教学习”的。聪明的学生对于简单的问题自己看书就会了,就是不会,难道北大的教授水平比我还差吗?

所以北大学生来这里的,一定是不聪明的学生,与我自己学校中普通学生没有什么差别。我挑战的就是这一类的学生。这种类型的5个学生,花一个月的时间,从零开始不一定做的比我好。

如何证明?你就是例子。看你提的问题吧,不同频率、不同采样率一起播放?这个能会是难点做不到吗(或许你自己做不到,认为是难的)?更可笑的是问如何产生1M的方波,用什么XOR指令驱动I/O产生,当我已经告诉你答案(尽管不是直接的),你还要求直接告诉你。才用过M8,手上手册应该有吧,花10分钟看一下,就能明白的。为什么要直接的答案?北大的学生就这水平?给北大丢脸你不觉的?

如果我再说狠点,你可能已经马上就看过了,但就是不明白,所以才又问的。如果真是如此,你真应该后悔自己暴露了是北大的学生。

北大是,也是应该有好学生的。不过作为你这个北大学生,可能将来和我一样,只能在电视里看他们的风采。

真不明白,北大的精英和你有什么关系?把他们搬出来证明你有能耐?


 楼主| 发表于 2013-6-10 07:37:35 | 显示全部楼层
马老师您这是什么理解能力?我说用XOR驱动端口是回答您之前问为啥用标准51出不了1M方波的问题的,您说的PWM匹配输出的方法我早就用过,不止一次了。北大当然有差的,有垫底的,不过您从北大找5个垫底的证明他们不如你,有什么意义呢?
 楼主| 发表于 2013-6-10 08:43:56 | 显示全部楼层
machao 发表于 2013-6-10 01:19
不要老是拿别人的荣耀给自己贴金。
北大是中国一流高校,招收的学生也是一流的,国家投入更多的经费,所 ...

说说我最近想做或者正在做的几个东西吧,都是纯做着玩的,跟本职工作无关。
我是外行,看看马老师有什么高见,给指导指导?

1. 可调开关恒流源
原理也简单,用LM2596之类电源管理ic,负载串个小电阻,用运放放大电阻上的电压反馈到电源管理ic的FB脚。调节电流可以有两种方法,一是用数字电位器调整运放的增益,二是用DAC生成一个控制电压引入到反馈环,两种方式我都已经试过了。
之前已经实现了用LM2596出3A恒流,现在在某哥们的产品里长期稳定运行中;
用AOZ1014做到5A,用APW7120+两只2SK3918做到15A。
这次准备用技嘉主板常用的vcore pwm ic, ISL6312,三相同步整流,目标是100A。 电流取样用小电阻估计很难做到了,得换霍尔电流传感器,反馈环路稳定性还得仔细计算。

2. 可调电子负载
配合前面的东西,要求就是100A电流,100W以上功率,可实现按恒阻、恒压、恒流方式工作。打算先做个恒阻的,简单点,用mcu控制8~10路mos管,每个mos管控制一个0.1欧 25W铝壳电阻的通断。电阻装到大散热片上,散热片泡水里。
之后再考虑实现恒压和恒流方式。

3. dds+pll试验
PLL一般是固定基准频率,VCO输出频率经过可变分频器以后跟固定基准频率进鉴相器,MCU控制分频比。主要问题是可变分频器即使是16位的,输出30M左右频率时,最小步进也在30M/65536, 几百Hz的量级;分频比再高的话,鉴相器不好办。现在我打算试试固定分频比,基准频率用avrdds输出,看能不能把最小步进做到1Hz级别。avrdds的相位噪声不知道是不是大问题,锁相速度慢点倒无所谓。纯粹做着玩的,别问这东西做出来有啥用。

4. 功率电感的饱和电流测试器,elm-chan那个,http://elm-chan.org/works/lchk/report.html,估计马老师您也看过
这东西需要一个大电流电源,至少60A吧,如果不用蓄电池比较麻烦。试试能不能用几个大电容放电来实现,99%的时间充电,1%或者更短的时间放电,这样对供电的要求就降低了。elm-chan是靠示波器上的显示来找饱和电流的,我打算试试用两路adc来采集电压、电流数据,如果精度足够,从I-t曲线的二阶导数曲线上找极值点应该就能判定电感的饱和点。

5. 电波钟,以前在淘宝上买过几个JJY和BPC的接收模块,实测都是在阳台上有信号,拿到屋里书桌上就没信号了,可能我住的这屋屏蔽太好,或者干扰太严重。GPS的方案估计好不到哪去,我的手机在阳台上都不容易定位;GPRS之类还得办个卡,也麻烦。wifi+网络对时倒是可以,就是感觉有点不值。还是得从JJY或BPC下手。
看到一个全分立的方案,打算试试,然后看看怎么改进。http://wenku.baidu.com/view/a7128b8984868762caaed568

6. elm-chan的频谱分析仪,http://elm-chan.org/works/rsm/report_e.html, 这个我一直感兴趣,不过用AVR来做FFT似乎勉为其难了点,用STM32估计能做得更好一些。
这东西再加强加强,能实现的功能不少,以后有机会慢慢试。

还有一些没啥技术含量,纯练手的,就不说了。
发表于 2013-6-10 09:31:25 | 显示全部楼层
mark 北大
发表于 2013-6-10 09:38:48 | 显示全部楼层
tomzbj 发表于 2013-6-9 22:09
华东吧?
我非常尊敬马老师,只是觉得他之前说这东西要五个北大学生弄一个月,有点不爽而已~~

北大还是牛人多。

  
发表于 2013-6-10 19:21:18 来自手机 | 显示全部楼层
"用LM2596之类电源管理ic,负载串个小电阻,用运放放大电阻上的电压反馈到电源管理ic的FB脚。调节电流可以有两种方法,一是用数字电位器调整运放的增益,"

Another alternative: adc the voltage on the resistor (directly or via a current sense amplifier if the current is high). And use the mcu to generate a voltage on the fb pin.

It is a simple hardware solution but very difficult software solution.
发表于 2013-6-10 19:24:15 来自手机 | 显示全部楼层
"3. dds+pll试验"

Look into clock synthesizers (cdce9xxx or fs7xxx). They have all the functions you desire, but on one chip.

"别问这东西做出来有啥用。"

Signal / clock generator or tuner.
 楼主| 发表于 2013-6-10 20:28:21 | 显示全部楼层
millwood0 发表于 2013-6-10 19:21
"用LM2596之类电源管理ic,负载串个小电阻,用运放放大电阻上的电压反馈到电源管理ic的FB脚。调节电流可以 ...

re, mcu太慢了,现在随便哪个电源管理ic都是逐周期控制,频率低的几百kHz,高的上MHz. mcu如果不是专门为这种应用设计的,光ad+da用的时间就慢到不知道哪去了。
 楼主| 发表于 2013-6-10 20:35:29 | 显示全部楼层
millwood0 发表于 2013-6-10 19:24
"3. dds+pll试验"

Look into clock synthesizers (cdce9xxx or fs7xxx). They have all the functions you ...

嗯,不过用这种东西不就没有折腾的乐趣了?查了一下淘宝,有货没成交量。我还是喜欢用手里现成的和容易搞到的器件做着玩。 你的中文输入法坏了么?
发表于 2013-6-10 20:49:39 | 显示全部楼层
马老师的题目是在最大限度内使用单片机的软硬资源, 8位数码管动态扫描加宽范围高精度频率测量,的确对作者的软件功力是有一定要求得。不是随便写个能跑通的程序能达到要求的。不过术业有专攻,也没必要太较真。
发表于 2013-6-10 20:52:30 | 显示全部楼层
不过楼主直接用溢出中断来做那个WAVE的程序,的确也没发挥出M16的长处。不能怪马老师给70分。
 楼主| 发表于 2013-6-10 21:14:10 | 显示全部楼层
spy2008 发表于 2013-6-10 20:52
不过楼主直接用溢出中断来做那个WAVE的程序,的确也没发挥出M16的长处。不能怪马老师给70分。 ...


我学一下马老师的口气:您看贴不认真,我用的是M8不是M16。再仔细看看,马老师自己也说了放32k的音质会有影响,48k的不行,为什么?
以及,如果不是44.1k的整数分之一,象32k、48k或者40k之类,用马老师的方法放出来,音量能保持一致么? 如果你觉得这不算个问题,就当我没说好了。
发表于 2013-6-11 02:11:34 | 显示全部楼层
spy2008 发表于 2013-6-10 20:52
不过楼主直接用溢出中断来做那个WAVE的程序,的确也没发挥出M16的长处。不能怪马老师给70分。 ...

用14位r-2r实现DA并不是给70分的主要原因。其实用r-2r和PWM实现DAC各有自己的优点,看你如何选择。

用R-2R的优点,是可以应付各种扫描频率的WAV,比如32K 48K,可能做不到完全相同,但可以非常接近,不会对声音有太多的影响,通常一般人不会感觉到,普通应用场合没问题。缺点就是占用I/O口太多,如果播放双声道的话,40pin的MCU还不能用。
用PWM做DAC,的优缺点与上面相反。
这里不讨论哪个DAC能真正做到16位,只要差不多到12位,一般应用足够。

这个小DD还是有实用价值的,当然不是做专业的HIFI播放器,而是面对一些日常的需要语音提示的电子系统,如公交车的报站器,电梯楼层报站器,带语音提示的冰箱、空调.....这些应用语音需要专门做的,制作时采用22.05k、44.1k就可以了,在pc上非常容易做转换。但这些应用中通常还需要其它的外围,如LED显示、按键,或通信控制等,所以能有更多的I/O方便些。

所以从我的观点看,用PWM比R-2R好。

主要的原因还在下面,从LZ帖的代码上,我发现一些问题,感觉LZ对MCU的使用和代码优化还不够专业,这个从中断服务代码中可以看出:

ISR( TIMER0_OVF_vect ) {

    uint16_t    data;

    TCNT0 += (256-48); // 16934400 / 8 / 48 = 44100       ==》这里反映出对定时器的理解还在初级阶段,如果使用比较清零方式,这句代码就可以省掉
    if( play_ready == NOTREADY )
        return;
    if( !fifo_is_empty() )                                                    ==》这里最好优化掉,用一个判断。
        fifo_del( data );
    else
        data = 0x0;                                                           ==》此时为什么等于0?

    data += 0x8000;                           ==》我这个处理是在读数据处理了,而且不用16位的,高8位,低8位分开,其实在WAVE文件中,本身就是字节的,只要高8位加0x80,少多少指令?

/*    PORTD = ( PORTD & 0x3 ) | ( (data >> 8 ) & 0xfc );
*/
     /* 用14个io拼r-2r, 比用pwm da的方式灵活些。如果用m16就容易多了 */
    PORTB = ( PORTB & 0xfc ) | ( data >> 14 );            
    PORTD = ( PORTD & 0x3 ) | ( (data >> 6 ) & 0xfc );
    PORTC = ( data >> 2 ) & 0xff;                                    
}

从这个中断代码中,可以看出代码并不是优化的,这个中断服务是44.1K频率连续不断的,周期是22.7us一次。如果按系统时钟10M计算,可以执行227条指令(AVR基本可以做到1MIPS/1M)。估算一下中断服务要多少条指令,用227减一下,省下的才留给读取TF卡数据等其他的操作使用。注意的是,读TF卡一般使用SPI接口,需要一位一位的串入的。

我在教程中讲过,中断服务程序的编写原则,应该是越短越好(主要指执行时间)。做到这点也是不容易的,需要经验和积累。可是首先要有概念,在编写整个系统代码时就要考虑如何实现。另外像我第1个指出的问题就应该可以避免,属于低级问题。使用比较清零方式,直接就少一条指令。在这个应用中,有时一条指令效果就明显不同。


下面是我的代码:
教材例程中的,简化成8位单通道输出,44.1K、22.05K、8K 的双通数据都做处理(总是要读的),由于音频数据处理上也做啦简化,低8位直接扔掉了,省下一点时间,所以使用11.2896M晶振就可以了。

interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
        if(FifoCt)
        {                                                                /* 一旦缓冲区中有数据 */
                OCR2 = Buff[FifoRi];                                /* 开始播放 */
                ++FifoRi;
                FifoCt --;                                                /* 播放完一个数据后,音频数据个数-1 */
        }
}


下面这段是比较完整的代码,系统时钟使用22.5792M(超频),16位双声道输出,只是播放44.1K双声道,由于缓冲区的问题,不能播放流畅。

M16只有1K的RAM,而我4个256就没有了,试过把缓冲变成4个128(见代码中痕迹:FifoRi &= 0x7f;)但由于在读入数据处理也要加上相应的指令,就是这多的几条指令,使得播放速度慢了下来,所以只能使用3个256缓冲。播放44.1K双声道的效果不好。

// Timer2 output compare interrupt service routine
interrupt [TIM2_COMP] void timer2_comp_isr(void)
{
                if(FifoCt)                                                //一旦缓冲区中有数据开始播放
                  {
                    OCR1BL = Buff_l_h[FifoRi];        //高8位
                       OCR1AL = Buff_l_l[FifoRi];
                    OCR2 = Buff_r_h[FifoRi];        //低8位       
//                OCR0 = Buff_r_l[FifoRi];
                OCR0 = Buff_l_l[FifoRi];       
                    ++FifoRi;
//                        FifoRi &= 0x7f;
                    --FifoCt;                                        // 播放完一个数据后,音频数据个数-1 */
//                        count = 0;
                }
}

这个例子,我给自己打90分是有道理的,因为还是有缺陷和不足。应该还可以优化:可以尝试的方法有:

1、关键的地方用汇编
2、提高SPI硬件接口的时钟。TF卡可以工作在20M的时钟,但AVR的SPI的时钟最高是系统时钟的1/2. 那就提高系统时钟,可是M16最高为16M,我用22M已经超多了,再超就不稳定了。
3、采用SPI中断的方式读取TF卡数据。我是使用轮询方式读的,这里浪费了不少时间。但是用中断方式处理程序上处理相对复杂。LZ用什么方式?
4、采用SDIO模式读TF卡?AVR没有SDIO接口,要用I/O配合定时器。我现在定时器没有了。

LZ的作业没有总结报告。

后面2个原因是扣分的主要点

=================================
做一样东西,通常做出来花20%的时间,而优化、找问题要花80%的时间。而且,你做的DD播放效果我没有听到,附加的必要功能有没有?比如TF卡热插拔,前一首、后一首、选择固定音乐(放广告用),连续播放等基本的操作.....,放音转换过程中是否有卡卡的杂音?这些都是要评估的。

之所以我敢于挑战,是我自己做过,有体会。当5个人没有任何相关的基础概念,从头开始的话,首先要看很多的资料。你评估一下,仅看懂文件系统,从底层开始建立编写文件系统的代码需要多少时间?
还有是我自己赌的:北大的学生个个优秀,但相对可能的是团队精神差,一个人1个月可以做好,5个人说不定没戏。
最后一点是我赌赢的。现在的人,名利看的重,北大学生也不例外。想想看,5个北大学生,没事跑到上海,泡一个月,跟一个糟老头子挑战比试,能得到什么?赢了是应该的。而且赢了又怎样,又不是国际比赛,没有钱也没有名。万一输了,那可不得了。

现在算了,一切都挑明了,我知道的那点技巧也说了,这个所谓的“挑战”我自己就认输了,不比试了。

 楼主| 发表于 2013-6-11 10:54:45 | 显示全部楼层
machao 发表于 2013-6-11 02:11
用14位r-2r实现DA并不是给70分的主要原因。其实用r-2r和PWM实现DAC各有自己的优点,看你如何选择。

用R ...

回马老师:
1. 既然您也知道r-2r强在哪里,pwm什么地方不行,我就不多说了。io口不够用根本不是个问题,几毛钱一片的hc373,我只是懒得焊大堆飞线。不过从别的贴子里看到您似乎不喜欢外加74之类,那没办法了。
2. TCNT0这里您要揪住不放,那您胜利了,呵呵。
3.     if( !fifo_is_empty() )   ==》这里最好优化掉,用一个判断。       -------不知道马老师啥意思,我的fifo操作都已经用宏代替过了
4.     data = 0x0;  ==》此时为什么等于0?  ------似乎直接return返回更好些。总之执行到这里时,音质肯定是受影响了,就看怎么做影响小些呗
5.  data += 0x8000这里,编译出来就是        subi r20,lo8(-(-32768))               sbci r21,hi8(-(-32768),两条单周期指令。我试了用    *((uint8_t *)((&data)+1)) += 0x80, 结果编译出汇编指令反倒多了几条。至于为啥不在读数的时候处理,您再仔细看一下我读数的部分就知道。 ps. 您读数的做法就算效率高点,可读性都给破坏了,我觉得得不偿失。
6.   sd读取我用的就是elm-chan自带的方式。这里还可以优化,不过对avr要求不要太高了,再优化也就这样了。
7.  按键换后一首的功能我做了。就两三天时间,您说的那么多功能有必要一一实现么?音质么,可以说放16位 44.1k没一点杂音,我是用LM4871推动4欧音箱,耳朵贴音箱上听的,要不要我用麦克风再录一段下来?
8. 我之前已经说了,3月份我试过一次8位pwm的,两晚上加两白天,fat系统操作和sd卡操作都是这段时间里我自己实现的。
9.  40楼我准备做和在做的几个东西,马老师再给点意见?
发表于 2013-6-11 14:13:03 | 显示全部楼层
machao 发表于 2013-6-11 02:11
用14位r-2r实现DA并不是给70分的主要原因。其实用r-2r和PWM实现DAC各有自己的优点,看你如何选择。

用R ...

  受教。很佩服马老师做事的态度。产品开发需要这样的态度。
发表于 2013-6-11 17:11:44 | 显示全部楼层
tomzbj 发表于 2013-6-11 10:54
回马老师:
1. 既然您也知道r-2r强在哪里,pwm什么地方不行,我就不多说了。io口不够用根本不是个问题, ...

不是因为你是北大的学生,而是看你喜欢玩这些东西(爱才,不爱北大),就多说点吧,希望你有收获。

1. 既然您也知道r-2r强在哪里,pwm什么地方不行,我就不多说了。io口不够用根本不是个问题,几毛钱一片的hc373,我只是懒得焊大堆飞线。不过从别的贴子里看到您似乎不喜欢外加74之类,那没办法了。

==》这个我已经讲啦原因,而且也不是最重要的评价标准。你有你的理解,我有我的看法:如果是设计商业成品,那么成本也是重要的考核条件,老板1分钱都要扣的。中国的市场你了解吗?况且这个也不是非常高科技的东西。一旦你有市场,马上一批同样的东西就会出来。哪怕你的东西能放32K,48K的东西,可这个功能不影响产品的主要用途。22.05K,44.1K,甚至8K都能满足要求的。但我的比你便宜3块钱,那我就把市场抢下了。中国大部分低技术含量的产品,其市场是价格战的市场。想想看,在展览会上面对一群非专业的用户(非专业指不清楚什么是采样频率、位数的),我们两个产品放出的声音质量都相同(你放的48K,我放的44.1k,他们能分辨的出吗),但是我的价格比你的便宜3块,你却拼命解释,能放48K,32K, 所以要贵时,用户会选择谁。唯一的办法是,这是北大设计的平产品,用北大的牌子贴金(最后这句作为冷笑话理解)。

2. TCNT0这里您要揪住不放,那您胜利了,呵呵。
==》你还是有点不服气。但这个就是我发现你技术水平层次重要判决。这个定时器的使用,就是另外哪个帖子的比试的核心!等把你这里处理掉,还要到那里去发言,你等着再长点见识、学点东西吧。


3.   if( !fifo_is_empty() )   ==》这里最好优化掉,用一个判断。       -------不知道马老师啥意思,我的fifo操作都已经用宏代替过了
==》 这个就不是真正北大学生的风格,别人说的问题,自己应该前面思考一下,看能悟出什么道理?难道北大的学生自己飞分析能力就如此差?
这句统指下面的处理,由于我不知道 NOTREADY和fifo_is_empty()之间的关系,以及你的具体处理手段,所以只是给个提示。
if( play_ready == NOTREADY )
        return;
    if( !fifo_is_empty() )
        fifo_del( data );
    else
        data = 0x0;

    按我的理解你中断是不停的,每隔44.1K一次,所以NOTREADY是指不播放的时候,而fifo_is_empty() 是指播放过程中,数据没有跟上。按我的处理,不播放的时候应该是静音(0x8000),而播放的时候没有数据,就把上次的数据重放。而你的代码好像是不播放的时候就直接返回了,输出则保持了上次的数据,而播放过程中没有数据跟上你放的是静音。如果你是这样考虑的,那就应该这样处理比较好,也符合逻辑:不播放的时候和无数据跟上都是静音。当然我建立在“不播放的时候,缓冲数据应该是空的。至于你的代码如何处理,我不了解。所以给个建议。

      data = 0;
      if( !fifo_is_empty() )  fifo_del( data );      

    你以为我不懂宏?中国的教授,会写文章吹牛的一撸一大吧,但是真正写和会写好代码的没几个。北大也是如此。你还是看轻我了,至少在MCU本身应用这个专业的方面。真正的MCU高手在硬件和软件两个方面都是相当高水平的,要不然敢在这里开设专栏,还“打擂”“激将”?尽管开这个专栏我是被阿莫老弟(叫老弟,一是他比我小,二是请我吃过饭。透露一下,他的酒量不怎么样)强开的。不过手里没有金刚钻,也不敢揽这瓷器活的。

4.     data = 0x0;  ==》此时为什么等于0?  ------似乎直接return返回更好些。总之执行到这里时,音质肯定是受影响了,就看怎么做影响小些呗
===》见上条吧

5.  data += 0x8000这里,编译出来就是        subi r20,lo8(-(-32768))               sbci r21,hi8(-(-32768),两条单周期指令。我试了用    *((uint8_t *)((&data)+1)) += 0x80, 结果编译出汇编指令反倒多了几条。至于为啥不在读数的时候处理,您再仔细看一下我读数的部分就知道。 ps. 您读数的做法就算效率高点,可读性都给破坏了,我觉得得不偿失。
===》 不专业马上出来了。avr是8位机,一个字节的加法只需要一条汇编指令,你的data是16位的,加法需要2条汇编指令。没学过微机原理吧(根据你说是化学的本科)?我再说几句我要挨砖的话:你是C和编译器的高级傻瓜,被编译器骗了。PC平台上编程的高手通常不是MCU编程的高手!记住!

*((uint8_t *)((&data)+1)) += 0x80, 能用这样的语句吗,这条C语句要做多少事情?你分析过,或想过吗?
&data 指针是个16位的,你的指针运算处理不需要代码?

我不会再读你的代码了,你为什么不去读我的代码?北大高才生都是不读导师代码,是让导师读他的代码的吗?不过也有点道理,导师通常不会写代码,所以根本看不懂代码,所以学生拿代码顶导师。这个我们这里也有。
但既然你是为了你师弟冲着我来的,就应该先了解我,买本我的教程看看,摸摸实力吧。至少本书还是普通高等教育“十一五”国家级规划教材,AVR的书有几本到这个层次?你北大的高才生,学习使用AVR至少要挑一本好的书吧。你以为你聪明到看了手册就会用啦。

再给你个深度问题:你代码中的常用指针变量(比如front、rear)是在寄存器中还是在RAM中的?如果是在寄存器中,你是如何安排他在寄存器中的?希望你能明白为什么问这个问题。

6.   sd读取我用的就是elm-chan自带的方式。这里还可以优化,不过对avr要求不要太高了,再优化也就这样了。
===》你根本就没有全面和吃透SPI的工作方式,我提到的尝试方法1、3、4 你都回避掉,搪塞掩盖过去。又是北大高才生的毛病。

7.  按键换后一首的功能我做了。就两三天时间,您说的那么多功能有必要一一实现么?音质么,可以说放16位 44.1k没一点杂音,我是用LM4871推动4欧音箱,耳朵贴音箱上听的,要不要我用麦克风再录一段下来?
===》记住,比试赢的是说做的要超过我做的,不是做出来。你要帮你的师弟,至少要达到我的水平。按键要做消抖处理,还要及时响应用户的动作,增加这些功能多的话,都要消耗CPU的时间,处理不好会影响播放。

8. 我之前已经说了,3月份我试过一次8位pwm的,两晚上加两白天,fat系统操作和sd卡操作都是这段时间里我自己实现的。
===》“fat系统操作和sd卡操作”只是上层的东西,从“sd读取我用的就是elm-chan自带的方式”可以得知,这2个最重要的底层你是用别人的,而且也根本没有理解透。如果什么都没有,仅仅靠看资料和白皮书,从底层开始编程实现,你花2-3天时间就做好,那么你是真正的天才。
你站在姚明的肩上把篮球扣进篮筐一次,就以为你自己能扣篮啦。

9.  40楼我准备做和在做的几个东西,马老师再给点意见?
===》没有意见,仅仅是建议。
1、对你的执着和兴趣爱好表示称赞,希望你坚持,要从基础开始,扎扎实实的一步一步前进。没有捷径,要想再上一层楼,先把基础打扎实。
2、不要背上北大的包袱,被北大所谓的光环照迷了路。要知道天外有天,山外有山。北大的文科有名,但理工不是最强。而且北大的光环是几十年前的事情,现在的北大只是在这个光环下生存。只是有这样的光环,生存的条件好点,国家也需要这样的光环学校做门面。你能在北大读书,说明你还是比较优秀的,而且已经镀上了金色外壳。至于内涵还是要你自己充实的。
3、如我前面所讲,现在的教授和教师搞理论的写文章的多,真正自己动手会做和能做的不多。对于我们这个专业,每年都会培养一些优秀的学生,但很少留在学校。一来学校至少要博士,二来真正动手能力强的早被国际和国内的大公司企业选走了,待遇比在高校好的多。另外真正喜欢电子的人通常不喜欢学校的环境和氛围。你现在读物理,为什么喜欢玩这些DD,恐怕也是为将来做准备吧。这个没错。但对于高校,就出现这样的情况,老教授理论强,有社会关系,可以拿到项目,但自己不会做。新进的的教师并不是真正动手实践能力强的学生,留校的通常也是写文章的,所以真正懂MCU,在基础和实践上都能教学生和指导学生的教师在高校里反而很少。
4、鉴于第3点,学生基本上都是自己修炼的,而且也只是不多的几个有兴趣的。因此出现的情况是,当你从网上移植一个DD,通过自己的努力做了出来,你就成为“高手”了。因为周围其他的同学不会,甚至老师也不会。从此开始认为自己厉害,功力强大了。其实真正成为高手是要经过专业基础的学习和严格训练的,没有捷径可以走。武林中的高手,开始练的就是蹲马步、调节呼吸,有了好的基本功,又碰到一位真正的好的师傅或秘籍,再加上自己的天资苦练,最后才成名的。如果有天资,但只是碰到一个乡村武士,练上半年后,看上去是有功夫,但只是花拳绣腿,最终大部分只能是在街上摆个练武摊。不知道你看武侠小说吗?里面的故事还是有意义的。拿它做个比喻。
5、现在我不建议你多做什么东西。你把你做过的DD拿出来,看看与这个DD相关的知识、协议是否都彻底的懂了,不懂就要从基础学习,补上,然后看是否可以改进,优化,做的更好。或者还有没有其它更好的方法或方案实现。网上的参考可以使用,但必须是在掌握了全面的基础后,以审视的态度移植,多想为什么它要这样处理,是否还有更好的方法进行改进。
6、有机会到公司做个实习什么的,不一定是大公司,只要能参与研发,能有个比你有经验的人带就可以。这个是积累的过程。
发表于 2013-6-11 17:17:21 | 显示全部楼层
最后建议你先思考几天,问问北大学这个专业的高材生后再回复或问问题吧。
否则暴露的漏洞会越来越多。本周内不会理会此贴。我还要在隔壁讨论备课的,也是关于定时器的。愿意的话到那里参与。
 楼主| 发表于 2013-6-11 19:40:33 | 显示全部楼层
machao 发表于 2013-6-11 17:11
不是因为你是北大的学生,而是看你喜欢玩这些东西(爱才,不爱北大),就多说点吧,希望你有收获。

1.  ...

马老师,您应该好好反思一下您为何对北大有如此偏见、您为什么能有如此的优越感以及如此强大的精神胜利法。

首先告诉您,我已经博士毕业几年了,现在做的工作跟IT没关系,搞这些东西现在只是我的业余爱好,以后也是。有机会找个公司实习?还不一定大公司?这话留给您那边的学生吧,我根本就没打算找这方面的工作,以后即使跳槽也不会搞IT。

另外那个贴子里已经有人指出了,跟您PK不但得实现您提的要求,还得思路跟您的一样才行,或者顺着您所谓NB闪闪的思路进行下去,不然您就又自称胜利了,和您不一样的就错了。您总是以如此方式取胜,有什么意义?

您那个第一名的学生为什么要去北大,而不是留在您这里?我本来强忍着不想说的,不过我还是替他回答您吧,北大有各种各样的机会,能接触各种各样的人,学到很多不同的东西,总之一句话,有无限的可能性。北大当然有很多衰人、烂人;没问题,哪里都有,论比例可能北大比其他地方还多一些,但同样北大牛人的水平是其他地方的人达不到的。您这个学生不知道跟您多久,不过我想他是个聪明人,在您这里已经看明白了,版上看了贴子的各位如果不是跟您一样水平,估计也差不多看明白了,知道他在您这里能做什么,能学到什么,能认识什么样的人;他去北大又能做什么,学到什么,能跟什么样的人在一起做事。他做了他的选择,很好。至于他现在混得好不好?您觉得他做得不顺心,我只能说,您不是他,他现在做得怎样,好不好,这恐怕已经超出了您的理解能力。

我为什么不读读您的代码?我不想打击您,但是,我求求您了,您真的多应该读读世界上真正高水平的代码。不说上google code、github参加几个开源项目吧,至少去找几个高水平项目的代码看看,您总该能做到吧?您贴上来这几段,我只能说,您在这个贴子里表现出来的水平,别说入不入流了,在南方小厂做点山寨产品还算凑和,拿来教学生、企图把学生教成您这样,那就是误人子弟了。我自己的水平我知道,很垃圾,很烂,但是我起码知道高水平的代码应该写成什么样——首先不是您这样。

不多说了,我觉得我在您这里浪费的时间够多了,谢谢。
发表于 2013-6-11 20:32:48 | 显示全部楼层
本帖最后由 machao 于 2013-6-11 20:46 编辑

哈哈,你终于露出了本来的面目,你是来踢馆的。
你技术上不行,不要拿北大说事。北大再好也不能掩盖你在MCU技术上面确实还是有待提高的事实。我已经在学校环境下给你了优秀成绩,在我的正常标准下的70分,没有说你“很垃圾,很烂”。
如果你想要,我可以给你100分,就像你的博士学位一样。但不能改变我对北大的评价。

我的精神支柱就是我多读了几遍各种MCU的手册。在这个方面,我不会盲目的崇拜任何的权威和什么北大光环。
关于北大,我喜欢五四运动时期的北大,现在的北大,我欣赏的是孔和尚,孔庆东教授。尽管从事的领域不同。
现在有北大,我的工资不会多,没有北大我的工资不会少。
你这个博士,在精神上和人格上面,你比北大那个卖肉的博士差远了。

“我自己的水平我知道,很垃圾,很烂,但是我起码知道高水平的代码应该写成什么样”

1. “很垃圾,很烂”,从你北大毕业的博士口中说出不容易。说明在MCU方面技术我还是有点功力,你只是个练地摊的,被我说中。
2. 那么既然你在MCU技术方面"很垃圾,很烂”,那你凭什么去评价别人的水平?你连起码的资格都都没有的。作为博士这个应该懂的。
3. 看表面不知道内涵。代码写的再漂亮,再规范,实际的功能很差,有什么用。跟你的博士一样,面子工程。

是你自己来浪费时间的,我陪你了几天,不说技术,从年龄讲也好,从资历也好(你是博士,我是教授),你都应该讲你浪费了我的时间,谢谢才行。
现在北大的高才生,把中国的尊师爱教的传统文化都丢掉了,让人怎么还会对北大有敬意?!

封帖吧。 谁也不要浪费谁的时间。
发表于 2013-6-11 21:09:19 | 显示全部楼层
二位都是赢家。北大博士也在这个帖子里学到不少实用的知识。马老师能逐字逐句的阅读您的代码,对您的确有很大的帮助。马老师在其他帖子里也说了,他是激将法。对北大并没有什么看法的。二位的讨论,也让我等菜鸟跟着学习了不少。按我的理解,马老师是当代值得我们尊敬的大教育家。
发表于 2013-6-11 21:21:50 | 显示全部楼层
我不是封贴了吗,怎么还能跟上。在操作一次。
发表于 2013-6-12 14:33:36 | 显示全部楼层
本帖最后由 machao 于 2013-6-12 18:03 编辑

tomzbj通过站内短信提出抗议,这个故事的起源有误。
我再次游览2年前的帖子,发现是有出入。在这点上特此向tomzbj表示道歉。
下面对此故事做修改。

==============================================================
总觉得奇怪,几年前的事情啦,现在有人出来为“师弟”打抱不平,北大的优秀TX如此仗义,可不得了啦,如此一来,岂不是将来中国会变成北大的天下,今天我得罪了北大,今后老夫在有生之年会无处藏身的。

花了10分钟的时间,在我的专栏里找了一下,赫然明白了(开始查错了,光看头像了)。原来此打抱不平者,的确是当年我挑战的那位的师兄,当时在现场。

可能我在搜索twoperson时,到过tomzbj的博客。看到他喜欢玩这些“山寨”DD,留下了几句:
“用m16加一片lm324,设计一个读取sd卡上wave文件,并播放的wave播放器,看谁做的好
尝试过就知道了,关键还是要做的好。参考一下我编写教程第2版的第19章,有什么心得可以到我的讨论组交流。”

这个好像还不算挑战,也没说5人。不过已经有话在前:“关键还是要做的好”,意味着不仅仅是做出来。

下面是此故事的来源:
http://www.amobbs.com/thread-4518509-5-1.html帖子的标题是:“本次实践操作考核题,请有兴趣的给个简单代码—-- 157楼给出目前最佳的代码 ”

关于北大5个学生做DD的所谓挑战在此贴的196-197楼:


此位师哥在200楼还劝我:


当然好心了,现在看还是应该感谢你的好心。因为怕我丢丑,因为5个北大学生,做这样“山寨”DD,还要一个月?老夫肯定输的。

没想到的是(谁会想到),被挑战者着根本没有收下挑战书,(在忙他的国家级重大科研项目),2年半后,他的师兄来了。

说实话,我还是挺佩服你的。俗话说的好“大丈夫报仇十年不晚”。一个物理博士,利用业余时间苦苦的做些所谓“山寨的DD”(这可是你下的定义),除了爱好之外,还为了这一天为北大出口气。佩服。

抱歉一下,我实际已经忘了此事,本帖开始时间,还把你当成在校的学生,所以说让你“实习”什么的。现在我明白了,你绝不会从事“IT”行业的工作,你的业余爱好的目的我是有的理解了。

不过作为博士,我还是说你不“博”。IT行业的面非常广泛,MCU的应用只是IT行业中一个小小的应用领域。
每个行业都有其门道和深层的的东西,这些精髓是需要时间积累的。
好像你本科是化学,研究生是什么专业不知道,博士是读的物理。业余爱好是电子。
我22岁,78年本科读的物理专业,四大力学都学过,大学期间,有“普通物理”和“数学物理方程”两门课程考试得到100分。由于当时的年龄问题和一些其它的问题,没有继续读书,所以没有硕士、博士的学位。毕业担任教师后,由于喜欢计算机和硬件电路,逐渐转到现在的方向。教过的课程有《程序设计》《数据库》《微机原理》《数字逻辑电路》《计算机网络》《动态WEB开发技术》《数据结构》《数字图像处理》《嵌入式系统与单片机》。。。带过的实验和玩过的东西有(只说硬件相关)《数字逻辑》《Z80双板机》《6502》《微机原理》、51、AVR、STM32、FPGA、、、。尽管我们学校不如北大有名气,至少也是全国重点高校,能在这样的学校混,还是有那么点小小的功力吧。

只所以只有小小的功力,是说与本人与其它的教授,尤其是北大教授不能比。由于本人的兴趣和爱好,不喜欢写文章(也写不出高深的文章),不喜欢申请国家大项目(没有能力申请),所以到现在也就是一个小小无名的副教授。不过我这点小小功力是专业的,你的业余爱好还只是花拳绣腿。在这点上我占了上风。

其实,就你的业余爱好,我还是实事求是的给了评价,学校环境的优秀,我的标准70分。要知道,每年我面对的都是学这个专业的学生,给优秀的寥寥无几。换句话讲,你的业余水平比达到我教的专业学生的优秀层次。

这个不是说我教的不好,或我教的学生不聪明。其关键在于兴趣和社会的环境。如果没有兴趣的话,就是只求个通过。你的兴趣和坚持,主要可能是的确喜欢,但2年多前的那个挑战可能也是一个刺激。而我上课的学生没有这样的激情(在这个方面他们倒是应该向你学习的)。仅仅是我自己带的研究生有几个愿意钻下去。今年我的研究生毕业,国际一家IT公司给他1.8W月薪的待遇,他不愿意去,选择到我10年前指导的一个学生的深圳公司参与创业(可以归类“南方小厂做点山寨产品”)。每个人有每个人对生活理解和追求,北大的博士也有卖肉的。至少我这个讨论组里的DD不是培养卖肉的。至于是不是只能用于“南方小厂做点山寨产品”,也不是你这个外行能评价了的。至少你定时器用的对,少一条指令,可能对于导弹制导的精度就会提高。“失之毫厘差以千里”这个道理作为博士应该懂得。

现在一切真相大白,我也算了断一江湖冤仇,可以放心喝酒、大口吃肉了。

不过,看官且慢。君子报仇十年不晚。2011年1月的事情,要到2021年才是十年。没准此兄没日苦练(或那位twoperson同志、或北大新的可谓后生),又得到真正高手指点,凭他(或他们)的少年气勇、聪明才智,还会到此一搏的。老夫可要不断学习、坚持练功,以防被打的找不到北。

要知后事如何,且听下回分解。(还会有下回会吗......)

最后,请大家听从北大博士的话:“我只能说,您在这个贴子里表现出来的水平,别说入不入流了,在南方小厂做点山寨产品还算凑和,拿来教学生、企图把学生教成您这样,那就是误人子弟了。”
好在他现在还不是华东师大的校长,否则我肯定失业了,因为误了很多良家子弟。
他将来可能有一天做华东师大校长的,不过此时我已经退出江湖了。
朋友们从此请不要光顾此讨论组,免得被我误了。
请阿莫站长慎重考虑,有必要请关闭此讨论组。否则我会继续误人子弟的。将来一旦大难当头,我一拍屁股开溜了,但有可能北大的学生,还有那些被误的子弟会找你网站的麻烦哟。今天的北大可是传承了优秀五四运动精神的北大,今天北大的学生各个都了不起(冷笑话一个)。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
友情提示:标题不合格、重复发帖,将会被封锁ID。详情请参考:论坛通告:封锁ID、获得注册邀请码、恢复被封ID、投诉必读
您需要登录后才可以回帖 登录 | 注册

本版积分规则

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

GMT+8, 2019-2-21 11:02

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

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

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