Kycklingkvitter från uC?

PIC, AVR, Arduino, Raspberry Pi, Basic Stamp, PLC mm.
bearing
Inlägg: 11676
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: Kycklingkvitter från uC?

Inlägg 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
}
bearing
Inlägg: 11676
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: Kycklingkvitter från uC?

Inlägg 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
}
Användarvisningsbild
E85
Inlägg: 1274
Blev medlem: 29 maj 2007, 16:24:19
Ort: Övik

Re: Kycklingkvitter från uC?

Inlägg 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?
Användarvisningsbild
wombat
Inlägg: 721
Blev medlem: 6 november 2006, 16:39:08
Ort: Stockholm

Re: Kycklingkvitter från uC?

Inlägg 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
bearing
Inlägg: 11676
Blev medlem: 2 mars 2006, 01:01:45
Ort: Ängelholm

Re: Kycklingkvitter från uC?

Inlägg 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
Användarvisningsbild
E85
Inlägg: 1274
Blev medlem: 29 maj 2007, 16:24:19
Ort: Övik

Re: Kycklingkvitter från uC?

Inlägg 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.
Skriv svar