Sida 2 av 2

Re: Kycklingkvitter från uC?

Postat: 25 mars 2010, 23:14:20
av bearing
Kul att ni gillart'! =)

Jag har labbat med en gammal PIC16F627 med intern 4MHz-oscillator. PORTB.3 (PWM-utgången) är kopplad till en piezohögtalare (traditionella högtalare går nog också bra) via ett 220 ohms motstånd.

Koden borde funka på alla PIC som har TMR0, TMR2 och PWM-modul. Ifall INTRC är på 8 MHz behöver man ställa in prescaler 2 på TMR0.

Med mindre ändringar borde koden även funka på PICar med bara en TMR0. Då får man använda två utgångar, med varsitt motstånd kopplad till högtalaren.

Koden är skriven för CC5X, men borde gå att använda med andra kompilatorer utan några större ändringar. Kompilerad med CC5X tar den 184 word programminne och 37 bytes variabelminne.

Kod: Markera allt

#include <INT16CXX.H>

#pragma config |= 0xFF7F
#pragma config PWRTE=on, WDTE=off, FOSC=0b100, BODEN=on

#pragma bit speaker @ PORTB.3
#pragma bit TRISspeaker @ TRISB.3

//----- Globals -------------------------------------------------------
bit     active;
uns8    halfPeriod;
uns16   periodCounter;
uns8    volume;
uns8    step;
uns8    stepPeriod;
uns8    stepCounter;
struct 
{
    uns8    volume;
    uns8    startHalfPeriod;
    uns16   periodCount;
    uns8    step;
    uns8    stepPeriod;
}   ramps[4], *rampPointer, *rampEndPointer;


//----- Interrupts ----------------------------------------------------
#pragma origin 4
interrupt int_server(void)
{
    int_save_registers    // W, STATUS (and PCLATH)
    char sv_FSR = FSR;  // save FSR

    if (T0IF)
    {
        T0IF = 0;
        TMR0-=halfPeriod-3;
        
        
        if (active)
        {
            if (volume<4)
                CCP1CON |= volume<<4;
            else
                CCPR1L = 1;
            active=0;
        }   
        else
        {
            CCP1CON&=0b11001111;
            CCPR1L=0;
            active=1;
        }
        
        if (--stepCounter==0)
        {
            halfPeriod+=step;
            stepCounter=stepPeriod;
        }
        
        if (--periodCounter==0)
        {
            rampPointer++;
            if (rampPointer>=rampEndPointer)
            {
                rampPointer=&ramps[0];
            }   
            volume=rampPointer->volume;
            step=rampPointer->step;
            halfPeriod=rampPointer->startHalfPeriod;
            stepCounter=stepPeriod=rampPointer->stepPeriod;
            periodCounter=rampPointer->periodCount;
        }   

    }

    FSR = sv_FSR;       // restore FSR
    int_restore_registers // W, STATUS (and PCLATH)
}


void init();

//----- Main ----------------------------------------------------------
void main()
{
    volume=0;
    rampEndPointer=&ramps[sizeof(ramps)/sizeof(ramps[0])];
    rampPointer=rampEndPointer;
    
    ramps[0].volume=1;
    ramps[0].startHalfPeriod=110;
    ramps[0].periodCount=100;
    ramps[0].step=-1;
    ramps[0].stepPeriod=10;
    
    ramps[1].volume=2;
    ramps[1].startHalfPeriod=100;
    ramps[1].periodCount=500;
    ramps[1].step=0;
    ramps[1].stepPeriod=255;
    
    ramps[2].volume=4;
    ramps[2].startHalfPeriod=100;
    ramps[2].periodCount=500;
    ramps[2].step=1;
    ramps[2].stepPeriod=8;

    //Pausen mellan pipen   
    ramps[3].volume=0;
    ramps[3].startHalfPeriod=100;
    ramps[3].periodCount=5000;
    ramps[3].step=0;
    ramps[3].stepPeriod=255;
    
    init();

    while(1)
    {
        nop();
    }
}


void init()
{
    OPTION_REG  =   0b1.0001.000;   //Pullups, no TMR0 prescaler

    CMCON   =   0x07;   //Turn off comparator
    TRISB   =   0;
    PORTB   =   0;
    
    PR2     =   1-1;//1 MHz
    T2CON   =   0b00000100; //T2ON;
    CCPR1L  =   0;  //0% duty
    CCP1CON =   0b00001111; //PWM active high

    
    T0IE=1; //TMR0 interrupt enabled
    GIE=1;  //Global interrupt enabled
}

Re: Kycklingkvitter från uC?

Postat: 26 mars 2010, 02:40:50
av bearing
Annan version, t.ex. för mindre PIC:ar. Den klarar två volymnivåer i mjukvara, utan att använda PWM-modul. Testad på PIC12F629.

Kod: Markera allt

#include <INT16CXX.H>

#pragma config |= 0xFFFF
#pragma config PWRTE=on, WDTE=off, FOSC=0b100, BODEN=on

#pragma bit speaker @ GPIO.5
#pragma bit TRISspeaker @ TRISIO.5

//----- Globals -------------------------------------------------------
bit     active;
uns8    halfPeriod;
uns16   periodCounter;
uns8    volume;
uns8    step;
uns8    stepPeriod;
uns8    stepCounter;
uns8    temp;
struct 
{
    uns8    volume;
    uns8    startHalfPeriod;
    uns16   periodCount;
    uns8    step;
    uns8    stepPeriod;
}   ramps[4], *rampPointer, *rampEndPointer;


//----- Interrupts ----------------------------------------------------
#pragma origin 4
interrupt int_server(void)
{
    int_save_registers    // W, STATUS (and PCLATH)
    char sv_FSR = FSR;  // save FSR

    if (T0IF)
    {
        T0IF = 0;
        
        if (active)
        {
            if (volume)
                speaker=1;
                
            if (volume<3)
                TMR0-=(halfPeriod/2)-3;
            else
                TMR0-=halfPeriod-3;
            active=0;
        }   
        else
        {
            speaker=0;
            if (volume<3)
            {
                temp=halfPeriod;
                temp+=(halfPeriod/2);
                TMR0-=temp-3;
            }   
            else
                TMR0-=halfPeriod-3;
            active=1;
            
            if (--stepCounter==0)
            {
                halfPeriod+=step;
                stepCounter=stepPeriod;
            }
            
            if (--periodCounter==0)
            {
                rampPointer++;
                if (rampPointer>=rampEndPointer)
                {
                    rampPointer=&ramps[0];
                }   
                volume=rampPointer->volume;
                step=rampPointer->step;
                halfPeriod=rampPointer->startHalfPeriod;
                stepCounter=stepPeriod=rampPointer->stepPeriod;
                periodCounter=rampPointer->periodCount;
            }   
        }
    }

    FSR = sv_FSR;       // restore FSR
    int_restore_registers // W, STATUS (and PCLATH)
}


void init();

//----- Main ----------------------------------------------------------
void main()
{
    volume=0;
    periodCounter=1;
    rampEndPointer=&ramps[sizeof(ramps)/sizeof(ramps[0])];
    rampPointer=rampEndPointer;
    
    ramps[0].volume=1;
    ramps[0].startHalfPeriod=111;
    ramps[0].periodCount=50;
    ramps[0].step=-1;
    ramps[0].stepPeriod=5;
    
    ramps[1].volume=2;
    ramps[1].startHalfPeriod=100;
    ramps[1].periodCount=250;
    ramps[1].step=0;
    ramps[1].stepPeriod=255;
    
    ramps[2].volume=4;
    ramps[2].startHalfPeriod=100;
    ramps[2].periodCount=250;
    ramps[2].step=1;
    ramps[2].stepPeriod=4;

    //Pause
    ramps[3].volume=0;
    ramps[3].startHalfPeriod=100;
    ramps[3].periodCount=2500;
    ramps[3].step=0;
    ramps[3].stepPeriod=255;
    
    init();

    while(1)
    {
        nop();
    }
}


void init()
{
    OPTION_REG  =   0b1.0001.000;   //Pullups, no TMR0 prescaler

    CMCON   =   0x07;   //Turn off comparator
    TRISIO  =   0;
    GPIO    =   0;
    
    
    T0IE=1; //TMR0 interrupt enabled
    GIE=1;  //Global interrupt enabled
}

Re: Kycklingkvitter från uC?

Postat: 26 mars 2010, 11:17:51
av E85
Vore kul med ett program där man kan rita linjer i ett spectrogram och sen genererar den kod utifrån det. :)

edit: Med en vanlig adderare byggd av en opamp borde man kunna använda flera PWM-utgångar till att spela flera frekvenser samtidigt va? Eller finns det enklare sätt?

Re: Kycklingkvitter från uC?

Postat: 26 mars 2010, 11:27:54
av wombat
Precis vad jag tänkte.
Lite som autogenererade delayloopar, fast liiite mer komplext. :roll:
Har även funderat röstsyntes.

Jag filar i alla fall vidare på min spela-melodier rutin.


/W

Re: Kycklingkvitter från uC?

Postat: 26 mars 2010, 12:38:26
av bearing
Visst är det kul med ljud ur uC:n!
Du får visa hur det låter sen!
E85 skrev:edit: Med en vanlig adderare byggd av en opamp borde man kunna använda flera PWM-utgångar till att spela flera frekvenser samtidigt va? Eller finns det enklare sätt?
Enklare är att bara dra ett motstånd per utgång till högtalaren.

Eller göra en lite mer avancerad tongenerator i mjukvaran, och addera allt ljud i mjukvaran istället. Det borde gå utmärkt att få till fina sinusvågor (eller andra vågformer, olika tabeller bara) i flera frekvenser samtidigt, med. t.ex. en ATtiny85, som ju har 16 MHz INTOSC. Om generatorinterrupt går i 31250 Hz blir det 512 cykler per interrupt, och det borde räcka gott för att skapa i alla fall 4 stämmor samtidigt, tycker jag.

Jag gjorde en gång ett program som spelade melodin och basgången från introlåten till Björnes magasin, i mjuka sinustoner. Den satte mig i en sorts meditativ stämning. Kunde inte sluta lyssna. :D

Re: Kycklingkvitter från uC?

Postat: 26 mars 2010, 13:37:58
av E85
Kände detsamma när jag la gamla nintendolåtar som Zelda m.fl. på minneskort och spelade upp på en liten högtalare mha en 1W förstärkare genom AVR. :) Det blir annat än att lyssna på hifi-systemet från datorn.