PIC Microcontroller Projekte

Auf dieser Seite werden diverse kleine Microcontoller Projekte vorgestellt. Diese Beispiele sollen zeigen, wie man auf einfache Weise und mit wenig Aufwand viele komplexe Funktionen implementieren kann.


Regelbarer PWM

Pulsweitenmodulierte (PWM) Signale werden heutzutage in der Regeltechnik sehr oft verwendet. PWM Signale, sind Rechtecksignale, bei denen der Duty-Cycle, also das Verhältnis von Impulsdauer zu Periodendauer in einem Bereich zwischen 0 und 100% eingestellt werden kann. Zum Beispiel kann man mit einem solchen PWM einen regelbaren Antrieb für Gleichstrommotoren erstellen. Für das Verhalten des Motors ist der Strom durch die Motorwicklungen entscheidend. Bei einer Spule folgt wegen der Selbstinduktivität der Wicklung der Strom dem Mittelwert der angelegten Spannund. Das Bedeutet, dass an den Motorwicklungen bei kleinem Duty-Cycle nur eine kleine Spannung abfällt. Der Motor läuft also langsam. Bei einem hohen Duty-Cycle liegt schon fast die volle Betriebsspannung am Motor an. Der Motor läuft schnell. Diese Methode eines regelbaren Antriebs ist sehr effizient und verlustarm und wird deshalb sehr häufig angewendet.

Der Versuchsaufbau ist sehr einfach. Zur Regelung des Duty-Cycles wird ein Potentiometer an einen der ADC-Eingänge (Analog/Digital-Converter) des Microcontrollers angeschlossen. Mit diesem Poti lässt sich die Spannung am ADC im Bereich von null Volt und der Betriebsspannung des Prozessors einstellen. Diese Spannung wird vom ADC in eine 8Bit lange Dualzahl umgewandelt. Null Volt entsprechen demnach 0x00 (also 0b00000000)und die Betriebsspannung des Prozessors 0xFF (0b11111111). Dieses Resultat des A/D Wandlers bestimmt dann den Duty-Cycle des PWM Signals. Die Frequenz des PWM und der Duty-Cycle werden auf einem LC-Display ausgegeben.


Der Schaltplan



Der Versuchsaufbau





Der Programmcode


//PWM, adjustable duty-cycle
//using AD-Converter
//10.5.08, Oliver Knecht

#include <16F877.h>
#DEVICE ADC=8
#use delay(clock = 4000000)
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP
#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)

#include "lcd.c"

void main()
{
    byte duty=0, period;
    byte prescale;
    int value=0;
    float div=2.55;

    setup_port_a( ALL_ANALOG );
    setup_adc( ADC_CLOCK_INTERNAL );
    set_adc_channel( 0 );

    delay_ms(500);
    lcd_init();
    delay_ms(500);

    setup_ccp1(CCP_PWM);   // Configure CCP1 as a PWM

    period = 0xFF;         // 100% Duty-Cycle
    prescale =  4;

    setup_timer_2(T2_DIV_BY_4, period, 1);

    // The cycle time will be (1 / clock) * 4 * t2div * (period + 1)
    // (1/4000000) * 4 * 4 * 256 = 1 ms or 1kHz
    lcd_putc("\fPWM 1       1kHz");
    //          ----------------

    while(1)
    {
      value = Read_ADC();
      duty=value;
      value=(value/div);
      printf(lcd_putc,"\nDuty-Cycle: %u%% ",value);
      printf("\n\rDuty-Cycle: %u",value);   // RS232
      set_pwm1_duty(duty);

      delay_ms(100);
    }
}

Das Ergebnis








ADC Voltmeter

Folgendes kleine Programm zeigt ein einfaches Voltmeter. Es wird eine Spannung am ADC-Eingang des Microcontrollers angelegt und dieser Rechnet das Ergebnis des A/D-Wandlers um in eine Fliesskommazahl und und gibt diese auf dem LC-Display aus.

Der Versuchsaufbau ist derselbe wie oben. Zur Regelung der Spannung wird ein Potentiometer an einen der ADC-Eingänge (Analog/Digital-Converter) des Microcontrollers angeschlossen. Mit diesem Poti lässt sich die Spannung am ADC im Bereich von null Volt und der Betriebsspannung des Prozessors einstellen. Diese Spannung wird vom ADC in eine 8Bit lange Dualzahl umgewandelt. Null Volt entsprechen demnach 0x00 (also 0b00000000) und die Betriebsspannung des Prozessors 0xFF (0b11111111). Die Auflösung des Voltmeters ist also Betriebsspannung/256. Im Fall von 5V Betriebsspannung ergibt das eine Auflösung von von etwa 0.02V. Der A/D Wandler lässt sich auch auf eine 10Bit Auflösung einstellen und damit wäre die Auflösung schon 0.005V. In den meisten Fällen genügen jedoch die 8Bit.

Für den Versuchsaufbau ist der Schaltplan derselbe wie oben. Will man höhere Spannung messen als die Betriebsspannung des Prozessors muss man die Spannung mit einem Spannungsteiler auf maximal Betriebsspannung herunterteilen. Ausserdem muss man den ADC-Port noch mit einem Schutzwiderstand und Zenerdioden vor Überspannungen schützen. Man muss auch beachten, dass der Innenwiderstand des Spannungteilers nicht höher ist als 10kΩ. Der Grund ist folgender: Der interne Kondensator am ADC-Eingang mit der am analogen Pin anliegenden Spannung geladen. Später wird die Spannung an diesem Kondensator gemessen. Deshalb muß man dem Microcontroller genügend Zeit lassen, den Kondensator komplett auf die Spannung aufzuladen, bevor man die eigentliche Messung auslöst. Wie lange diese 'aquisition time' dauert, hängt auch stark vom Innenwiderstand der zu messenden Spannungsquelle ab! Sinnvoll wäre es dann auch einen Spannungsfolger zwischen Spannnungsteiler und ADC-Pin zu schalten.


Der Schaltplan




Der Programmcode

Der folgende Code ist für eine Betriebsspannung von 3,3V geschrieben und demnach ist der Wert mit dem man das ADC Ergebnis teilen muss, um die am analogen Pin angelegte Spannung zu erhalten (256 / 3,3) = 77,27.

//Voltmeter, using
//AD-Converter
//10.5.08, Oliver Knecht

#include <16F877.h>
#DEVICE ADC=8
#use delay(clock = 4000000)
#fuses XT, NOWDT, NOPROTECT, BROWNOUT, PUT, NOLVP

#include "lcd.c"

void main()
{
    float value=0;
    float div=77.27;

    setup_port_a( ALL_ANALOG );
    setup_adc( ADC_CLOCK_INTERNAL );
    ADC_CLOCK_DIV_8;
    set_adc_channel( 0 );

    delay_ms(500);
    lcd_init();
    delay_ms(500);

    lcd_putc("\fVoltmeter");
    //          ----------------

    while(1)
    {
      value = Read_ADC();
      value=(value/div);
      printf(lcd_putc,"\n%1.2f V ",value);

      delay_ms(200);
    }
}

Das Ergebnis






Codeschloss

Bei einfacheren Steuerungen genügt für die Kommandoeingabe oft ein direkt in die Frontplatte integriertes Tastenfeld. Um Portanschlüsse zu sparen, verschaltet man die Tasten zu einer Matrix, dann können mit n Anschlüssen und einem entsprechenden Programm (n/2)^2 Tasten decodiert werden. Im folgenden Beispiel eines Codeschlosses werden 8 Portanschlüsse benötigt, um Tasten zu decodieren. Eigentlich würden 7 Portanschlüsse ausreichen, aber der Programmcode bleibt für 7 oder 8 Portpins der gleiche.

Das Funktionsprinzip ist einfach und clever: An Port B4-B7 wird der Reihe nach immer an einem anderen Anschluss LOW ausgegeben, während die anderen Anschlüsse auf HIGH sind. Die Eingänge B1-B3 sind durch die Pullup-Widerstände auf HIGH. Sie können für die Eingabe benutzt werden. Ist zum Beispiel Port B1=LOW, während B6=LOW, erkennt das Programm, das Taste 9 gedrückt wurde.


Der Schaltplan




Das Tastenfeld

Auf dem Tastenfeld befinden sich die 12 Tasten und die 2 LED's zur Zustandsanzeige des Schlosses. Ist das Schloss zu, leuchtet die rote LED. Ist es offen, leuchtet die grüne LED.







Der Versuchsaufbau

Ich habe das Programm über die RS232 Schnittstelle am Computer getestet. Deshalb ist auf dem folgenden Bild auch kein LC-Display am PIC-Board angeschlossen.





Der Programmcode

// Codeschloss mit Tastenfeld
//5.7.08, Oliver Knecht

#include <16F877.h>
#use delay(clock=4000000)
#fuses XT,NOPROTECT,NOLVP,NOWDT,NOBROWNOUT
//#use rs232(baud=9600, xmit=PIN_C6, rcv=PIN_C7)
#include "lcd.c"

char readbutton(void);
int getswitch(char button);

void main(void)
{
    int code[8]={2,5,7,0,1,6,3,4};  //Code

    delay_ms(15);
    lcd_init();
    delay_ms(2);

    while(true)
    {
        int p=0, i=0, t=0;
        int check=10;
        int checkold=0;
        int buffer[8]={0,0,0,0,0,0,0,0};
        boolean reset=true;
        boolean access=true;
        char button=0;
        char button2=0;

        output_high(PIN_A1);    //red LED
        lcd_putc("\fIhre Eingabe:\n    ");
        //          ----------------
        //printf("\n\rIhre Eingabe:\n");

        while(true)
        {
            button=readbutton();

            //Verhindert mehrfache Ausgabe bei
            //länger gedrücktem Schalter.
            while(check!=10 && button==button2)
            {
               button2=readbutton();

               if(button!=button2)
               {
                   button=0x00;
               }
            }
            button2=button;
            check=getswitch(button);
            delay_ms(1);

            if(check!=10 && check!=100 && t<8)
            {
                buffer[t]=check;
                t++;

                if(check==50 && t>0)
                {
                    t=t-2;
                }
            }
            else if(check==100)
            {
                if(t<8)
                {
                    delay_ms(200);
                    lcd_putc("\nCode too short");
                    //          ----------------
                    //printf("Code too short!");
                }

                for(p=0;p<8;p++)
                {
                    if(access==true && buffer[p]==code[p])
                    {
                        access=true;
                    }
                    else
                    {
                        access=false;
                        break;
                    }
                }
                break;
            }

        }
        delay_ms(500);

        if(access==true)
        {
            lcd_putc("\f     ACCESS\n    GRANTED!");
            //printf("\rACCESS GRANTED");
            output_low(PIN_A1);     //red LED
            output_high(PIN_A0);    //green LED
        }
        else
        {
            lcd_putc("\f     ACCESS\n     DENIED");
            //printf("\rACCESS DENIED");
            delay_ms(500);

            for(p=0;p<5;p++)
            {
                output_low(PIN_A1);
                delay_ms(200);
                output_high(PIN_A1);
                delay_ms(200);
            }
        }

        while(reset)
        {
            button=readbutton();
            check=getswitch(button);

            if(check==100)
            {
                reset=false;
            }

        }
        lcd_putc("\f  ...RESET...");
        //          ----------------
        //printf("n\rRESET...");
        output_low(PIN_A0);
        delay_ms(1000);
      }
}

char readbutton(void)
{
    int i;
    char button1=0, button2=0;
    char line[4]={0xEF,0xDF,0xBF,0x7F};

    for(i=0;i<4;i++)
    {
        output_B(line[i]);
        delay_ms(1);
        button1=input_B();
        delay_ms(1);

        if(button1 != line[i])
        {
            delay_ms(15);        //Entprellen des Schalters
            button2=input_B();

            if(button2==button1)
            {
                return button2;
            }
        }
    }
    return 0;
}

int getswitch(char button)
{
    switch(button)
    {
        case 0xEE:   lcd_putc("1"); //printf("1");
                     return 1; break;
        case 0xED:   lcd_putc("2"); //printf("2");
                     return 2; break;
        case 0xEB:   lcd_putc("3"); //printf("3");
                     return 3; break;
        case 0xDE:   lcd_putc("4"); //printf("4");
                     return 4; break;
        case 0xDD:   lcd_putc("5"); //printf("5");
                     return 5; break;
        case 0xDB:   lcd_putc("6"); //printf("6");
                     return 6; break;
        case 0xBE:   lcd_putc("7"); //printf("7");
                     return 7; break;
        case 0xBD:   lcd_putc("8"); //printf("8");
                     return 8; break;
        case 0xBB:   lcd_putc("9"); //printf("9");
                     return 9; break;
        case 0x7E:   lcd_putc("\b"); //printf("\b");
                     return 50; break;
        case 0x7D:   lcd_putc("0"); //printf("0");
                     return 0; break;
        case 0x7B:   lcd_putc("\f"); //printf("\n\r");
                     return 100; break;

        default: return 10;
    }
}
Impressum PHP Counter Sie sind Besucher: 526834