wzavr 发表于 2013-12-6 12:54:22

avr和弦播放程序中,asm改c这样对吗?

一个用avr播放和弦的程序中,主程序已经改为c了,但是中断函数还是用asm写的,为了更好得理解过程,把asm改成了c,肯定有错的,请看下什么地方理解得不对。
#include <avr/io.h>
/*
struct Note{
unsigned int k;          //相位步进
unsigned char ac;      //相位累加器0~7位
unsigned char *wp;       //指向wave_table表中元素索引,2字节(相位累加器第8~24位)
unsigned char wrap;      
unsigned char loop;
unsigned char lp;
unsigned char lvl;
}Notes;
*/
#define Nk    0
#define Nac   2
#define Nwp   3
#define Nwrap 5
#define Nloop 6
#define Nlp   7
#define Nlvl8

#define _0                           R2
#define TEMP                        R3
#define INDEX                        R17
#define sMUL                    R18
#define usMUL                   R19
#define SUML                       R20
#define SUMH                       R21
#define PHASEL              R22
#define PHASEH                   R23
#define ADDRESSL                R24
#define ADDRESSH                   R25

.extern time1,time2,time3,Notes,wave_table
.section .text
.global TIMER2_COMP_vect
TIMER2_COMP_vect:
               PUSH R0
                               PUSH R1
                               PUSH R2
                               PUSH R3
                               PUSH R4
                               PUSH R18
                               PUSH R19
                               PUSH R20
                               PUSH R21
                               PUSH R22
                               PUSH R23
                               PUSH R24
                               PUSH R25
               PUSH R28
                               PUSH R29
                               PUSH R30
                               PUSH R31
                   IN R0, _SFR_IO_ADDR(SREG)
                               PUSH R0                      //保存状态寄存器

               LDI YL,lo8(Notes)                       //Y指向Notes
                               LDI YH,hi8(Notes)
                               LDI ADDRESSL,lo8(wave_table) //计算wave_table最后一个数据地址.wave_table总共数据为1024(0x400)个
                               LDI ADDRESSH,hi8(wave_table)
                               LDI INDEX,0x4
                               ADD ADDRESSH,INDEX         //相当与:ADDRESSL+0x00;ADDRESSH+0x4(addressh:addressl +=0x400)
                               LDI INDEX,6
                               CLR SUML   
                               CLR SUMH
                               CLR _0
////////////////////////////////////////////////////
TONE_LP:
               LDD TEMP,Y+Nac
                               LDD ZL,Y+Nwp                //加载wave_table首地址给Z
                               LDD ZH,Y+Nwp+1
                               LPM sMUL,Z         //从wave_table 取数据
                               LDD PHASEL,Y+Nk    //加载相位步进
                               LDD PHASEH,Y+Nk+1
                       
                               ADD TEMP,PHASEL    //相位累加
                               ADC ZL,PHASEH
                               ADC ZH,_0

                               CP ZL,ADDRESSL
                               CPC ZH,ADDRESSH
                               BRCS SAVE          //如果小于跳
               SUBI ZL,128      //大于,超过数据表的范围了,则减去128(128个正弦波波表数据)
                               SBC ZH,_0
                               STD Y+Nwrap,_0   //大于则将wrap清零,开始将音量衰减
SAVE:
               STD Y+Nac,TEMP   //不管大于还是小于都将相位保存
                               STD Y+Nwp,ZL
                               STD Y+Nwp+1,ZH

                               LDD usMUL,Y+Nlvl   //获取衰减数据
               
                               MULSU sMUL,usMUL   //有符号与无符号相乘,将所得结果送到R1:R0中
               ASR R1         //将结果除以8(向右边移动3位),这样做目的是保正几个通道音量数据总和不超过两字节所容纳的数据大小
               ROR R0         //不将结果除以128,是为了不在循环里面花费过多时间。
               ASR R1         //为什么要除以128?原因将衰减数据最大值(0xff)看作2,进行比例缩小(比例是128:1)
               ROR R0         //为什么不是除以255?原因是波表数据是-128~+127之间,如果乘以衰减系数最大值(2)则得到结果在-255~255之间
               ASR R1
               ROR R0
               ADD SUML,R0    //累加各通道的值
                               ADC SUMH,R1               

                               ADIW YL,9      //下一通道数据占用RAM的值
               DEC INDEX      //通道索引减少1
                               BRNE TONE_LP   //获取下一通的值
////////////////////////////////////////////////////
               ASR SUMH                   //SUMH:SUML=SUMH:SUML除以16        (向右边移动4位)
               ROR SUML       //配合SUM在相乘之后除以8,那么相当于每通道数据除以128
               ASR SUMH       //即:SUM是每通道数据除以128之后的总和,这样做目的是
               ROR SUML       //尽可能缩短中断程序所花费的时间
               ASR SUMH               
               ROR SUML
               ASR SUMH
               ROR SUML
////////////////////////////////////////////////////
               ASR SUMH                //SUMH:SUML=SUMH:SUML除以4,而不是除以6(虽然SUM是6通道数据的总和)       
               ROR SUML       //原因是:不是每一个通道的数据是很大的,类是弹钢琴,当前按下的是音量最大的
               ASR SUMH       //前面已经按下的声音已经衰减了,越前面的就越小声。这个程序采取6通道,
                               ROR SUML       //就是模拟这种效果(模拟6个音符混在一起的效果,SUM表示6个音符混杂一起的音量)
                                                                
            
                             LDI R18,253    //判断SUM是否大于253,如果大于则SUM=253
                               LDI R19,0
                               CP SUML,R18
                               CPC SUMH,R19
                               BRLT NEXT2   //寄存器SUM中带符号二进制数小于在寄存器VALUE中带符号二进制数时,将发生跳转
                               MOVW SUML,R8

NEXT2:         LDI R18,1      //VALUEH:VALUEL=-255(-255的补码是0xFF01)
                               LDI R19,255
                               CP SUML,R18    //判断SUM是否小于-255,如果小于则SUM=-255
                               CPC SUMH,R19
                               BRGE NEXT3
                               MOVW SUML,R18

NEXT3:         ASR SUMH       //由于输出是差动输出的,PB1和PB2的输出是反相的
               ROR SUML
                               SUBI SUML,0x80 //由于-127<= SUML <=126,当SUML-0x80,则-255<= (SUML-0x80) <=-2,
                               MOV SUMH,SUML//所对应的十六进制:0x01<= (SUML-0x80) <=0xfe,也就是归一化。
                               COM SUMH
                               OUT _SFR_IO_ADDR(OCR1AL),SUML//输出相位相反的PWM
                               OUT _SFR_IO_ADDR(OCR1BL),SUMH
         
////////////////////////////////////////////////////
               SEC
                               LDS TEMP,time1                //每次中断发生,time1都会增加1
                               ADC TEMP,_0
               STS time1,TEMP

                               LDS TEMP,time2                //只有time1由0xff->0时候time2才会增加1
                               ADC TEMP,_0
               STS time2,TEMP

                               LDS TEMP,time3                //只有time2由0xff->0时候time3才会增加1
                               ADC TEMP,_0
               STS time3,TEMP

                               POP R0
                               OUT _SFR_IO_ADDR(SREG),R0   //恢复状态寄存器
                               POP R31
                               POP R30
               POP R29
                               POP R28               
                               POP R25
                               POP R24
                               POP R23
                               POP R22
                               POP R21
                               POP R20
                               POP R19
                               POP R18
                               POP R17
                               POP R3
                               POP R2
                               POP R1
                   POP R0
                               RETI



struct Note{
unsigned int k;          //相位步进
unsigned char ac;      //相位累加器0~7位
unsigned char *wp;       //指向wave_table表中元素索引,2字节(相位累加器第8~24位)
unsigned char wrap;      
unsigned char loop;
unsigned char lp;
unsigned char lvl;
}Notes;


extern uchar    time1,time2,time3,Notes,wave_table;

{
    uchar * pY;
    uchar * pZ;
    uchar * ADDRESS;
    uchar INDEX;

    pY = Notes;
    ADDRESS = wave_table;
    INDEX = 4;
    ADDRESS = ADDRESS + 0x400;

    INDEX = 6;
    SUM = 0;

TONE_LP:   
    uint PHASE;

    TEMP = Notes.ac;
    pZ = Notes.wp;
    sMUL = *pZ;
    PHASE = Notes.k;


    tTEMP = TEMP + (0xff | PHASE);
    PHASE = TEMP + PHASE;
    TEMP = tTEMP;

    pZ = pZ + (PHASE >> 8);

    if(pZ > ADDRESS)
    {
      pZ = pZ -128;
      Notes.wrap = 0;   //大于则将wrap清零,开始将音量衰减
    }

SAVE:
    Notes.ac = TEMP;    //不管大于还是小于都将相位保存
    Notes.wp = pZ;

    usMUL = Notes.lv1;

    uint tMUL;

    tMUL = sMUL * usMUL;
    tMUL = tMUL >> 3;   //将结果除以8(向右边移动3位),这样做目的是保正几个通道音量数据总和不超过两字节所容纳的数据大小

    SUM = SUM + tMUL;

    pY = pY + 9;
    INDEX--;
    if(INDEX == 0)
    {
      goto TONE_LP;
    }

    SUM = SUM >> 6;   //SUMH:SUML=SUMH:SUML除以
    if(SUM > 253)   //判断SUM是否大于253,如果大于则SUM=253
    {
      SUM = 253;
    }

    if(SUM < -255)//判断SUM是否小于-255,如果小于则SUM=-255//VALUEH:VALUEL=-255(-255的补码是0xFF01)
    {
      SUM = -255;
    }


NEXT3:
    char cSUM;
    SUM = SUM / 2;      //由于输出是差动输出的,PB1和PB2的输出是反相的
    cSUM = (uchar)SUM;
    cSUM = cSUM - 0x80; //由于-127<= SUML <=126,当SUML-0x80,则-255<= (SUML-0x80) <=-2,
    cSUM = 0xff - cSUM;

    OUT _SFR_IO_ADDR(OCR1AL),(SUM & 0xff);//输出相位相反的PWM
    OUT _SFR_IO_ADDR(OCR1BL),cSUM;

    time1++;
    if(time1 == 0)
    {
      time2++;
      if(time2 == 0)
      {
            time3++;
      }
    }
}
页: [1]
查看完整版本: avr和弦播放程序中,asm改c这样对吗?