Forum: Mikrocontroller und Digitale Elektronik LED mit Microcontroller schalten


von Stefan H. (stefan_h68)


Angehängte Dateien:

Lesenswert?

Hallo,
ich bin Azubi im ersten Lehrjahr und soll den PIC16F688 programmieren.
Ein Blinklicht habe ich schon hinbekommen. Ich benutze MPLAB und den 
HI-TECH C-Compiler. Hier mein Code bis jetzt:

#include <htc.h>              //include header file
#include <pic16f688.h>            //include PIC header

#define FOSC_INTOSCIO        0xFFFC      //set internal oscillator I/O 
function on RA4/OSC2/CLKOUT and RA5/OSC1/CLKIN
#define WDTE_OFF             0xFFF7      //disable Watchdog Timer
#define MCLRE_OFF            0xFFDF      //MCLR pin function is digital 
input, MCLR internally tied to VDD




int main(){

  ANSEL = 0b00000000;            //All I/O pins are configured as 
digital
  CMCON0 = 0b00000111;          //Coperators turned OFF
  PORTC = 0x00;              //write 0 to port C
    TRISC = 0x00;              //set RC0 as output pin / set TRIS 
register BIT 0 to 0

  while(1){
    PORTC = 1;              //LED_ON
    _delay(1000000);          //delay for 1000000 cycles
    PORTC = 0;              //LED_OFF
    _delay(1000000);          //delay for 1000000 cycles
  }
}

Die LED blinkt.
So meine Frage: Jetzt leuchtet die LED nur am RC0. Wie kann ich denn 
andere Ausgänge des Ports ansteuern? Sodass ich mehrere LED blinken 
lassen kann? oder gar ein lauflicht?
PORTC = 0x01
TRISC = 0x01 etc.pp.  hab ich schon versucht...es zeigt keine wirkung.

sprut kenne ich auch schon und hilft mir leider nicht weiter, da er viel 
assembler macht. ICh hoffe ihr könnt mir helfen.
Ich habe auch schon etwas über bitmanipulation gelesen, aber ich 
verstehe es nicht ganz, bzw. die syntax.

Liebe Grüße
Stefan

von Timmo H. (masterfx)


Lesenswert?

Wenn du 8 LEDs an PortC hast dann versuche mal folgendes:
1
  while(1){
2
    PORTC = 0xAA;              //jede zweite an
3
    _delay(1000000);          //delay for 1000000 cycles
4
    PORTC = 0x55;              //und umdrehen
5
    _delay(1000000);          //delay for 1000000 cycles
6
  }
oder einfacher
1
  PORTC = 0xAA;
2
  while(1){
3
    _delay(1000000);          //delay for 1000000 cycles
4
    PORTC ^= 0xFF;              //und umdrehen
5
6
  }

von Jürgen D. (poster)


Lesenswert?

Du hast ja schon mal den ganzen Port C auf Ausgang gestellt. Das ist 
schon mal gut.
PortC ist aber nicht nur ein Bit sondern ein ganzes Byte.
Du sagst jetzt ja mit PORTC =1 das gleiche wie PORTC = b00000001.

von Ralf G. (ralg)


Lesenswert?

Versuche mal die ersten beiden Zeilen in deiner main() zu verstehen, 
dann ist das gaaanz einfach!

von Stefan H. (stefan_h68)


Lesenswert?

Hallo,

vielen lieben Dank für eure schnellen Antworten. Erstens entschuldige 
ich mich für die etwas gewöhnungsbedürftige Schreibweiße meines ersten 
Posts, doch ich war leider unter Zeitdruck, da ich bald Feierabend 
hatte.

Ich habe eigentlich zu jeder eurer Antworten Fragen, die stelle ich hier 
einfach mal, in der Hoffnung, dass Ihr sie noch lest und der Thread 
nicht schon "Schnee von gestern" ist.

@Timmo H. : Wie meinst du das mit 8 LEDs an PortC? Mein PIC16F688 hat 
nur 6 I/O beim Port C, die anderen 2 bits sind laut Datenblatt nicht 
implementiert. Zu meiner eigentlichen Frage:
1
PORTC = 0xAA;
2
  while(1){
3
    _delay(1000000);          //delay for 1000000 cycles
4
    PORTC ^= 0xFF;              //und umdrehen
5
6
  }
was genau meinst du denn mit PORTC ^= 0xFF , was genau macht denn diese 
Zeile und was bedeutet "^"?


@Jürgen D. : So wie ich deine Antwort verstehe, liegt mein Problem 
darin, dass ich durch PORTC = 1 sage, dass das erste Bit gesetzt werden 
soll, sprich RC0? Wenn ich dann die anderen Ausgänge ansprechen will, 
muss ich gerade das Bit weiterschieben: PORTC = 0b00000010 ? Wäre das 
dann der Ausgang RC1? Und ist es egal ob ich 0b00000010 schreibe oder 
0x01?


@Ralf G. : Die ersten beiden Zeiler meiner main() besagen:
1
 ANSEL = 0b00000000;            //All I/O pins are configured as digital
2
  CMCON0 = 0b00000111;          //Coperators turned OFF

ich weiß leider gerade nicht, was du von mir willst, so wie ich die 
beiden Zeilen verstehe, sage ich durch den ersten Befehl, dass alle I/O 
Pins digital sind, und durch CMCON0 sage ich, dass der interne 
Comperator deaktiviert sein soll. Oder verstehe ich das falsch?



Übrigens sehr nett, dass Ihr mir helft, ich hatte ein Semester Java 
Programmieren gelernt in der Uni, jetzt da ich die Ausbildung mache, 
wünschte ich mir, ich hätte mehr aufgepasst, dann hätte ich vermutlich 
nicht so viele PRobleme mit C. Ich wrne hier schon vor, dass es bestimmt 
noch zu einigen weiteren Fragen kommen wird. Vielen Dank und einen 
schönen Abend wünsche ich euch noch.

Stefan

von Karl H. (kbuchegg)


Lesenswert?

Stefan H. schrieb:

> @Timmo H. : Wie meinst du das mit 8 LEDs an PortC? Mein PIC16F688 hat
> nur 6 I/O beim Port C, die anderen 2 bits sind laut Datenblatt nicht
> implementiert.

Auch recht.


> muss ich gerade das Bit weiterschieben: PORTC = 0b00000010 ? Wäre das
> dann der Ausgang RC1? Und ist es egal ob ich 0b00000010 schreibe oder
> 0x01?

Es ist wie im dir gewohnten Dezimalsystem.
1 ist nicht dasselbe wie 10 (Zehn) und das ist nicht dasselbe wie 100 
(hundert) etc. etc.

Nur das du hier erst mal kein Dezimalsystem zur Basis 10, sondern ein 
Binärsyste, zur Basis 2 hast. Das vorangestellte 0b kennzeichnet die 
Zahl als Binärzahl.

Wie wird da gezählt?
Na genauso, wie auch im Dezimalsystem gezählt wird. Nur dass es dann 
eben nur die beiden Ziffern 0 und 1 gibt. Ist ja schliesslich das Binäre 
Zahlensystem (und im Dezimalsystem haben wir 10 verschiedene Ziffern. Im 
Oktalsystem [Oktal == 8] sind es 8 und im Hexadezimal-System sind es 16)

Also wie zählen wir? (Dezimalsystem)
Du fängst bei 0 an, und bei jedem mal weiterzählen nimmst du die nächste 
Ziffer aus deinem Vorrat.
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Wie gehts weiter? Dein Ziffernvorrat ist aufgebraucht. Mehr als diese 10 
Ziffern hast du nicht. Ist aber kein Problem. Wir benutzen ja ein 
Stellenwertsystem. D.h. ein bestimmtes Symbol hat unterschiedliche 
Wertigkeiten, je nachdem an welcher Stelle es in einer Zahl auftaucht 
(AChtung: Zahl - nicht Ziffer. Eine Ziffer ist eine einzelne Stelle. 
eine Zahl ist das ganze, also die Summe aller Ziffern)

Deine Ziffern sind aufgebraucht. Aber du weist auch, dass du dir vor der 
Einerstelle einen ganzen Haufen führende 0-en vorstellen kannst. Diese 
ändern ja an der Zahl erst mal nichts. Ob du 5 oder 005 oder 0000000005 
schreibst - es ist immer die gleiche Zahl.

Zurück zum 'Experiment'. Wir hatten
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
und wie gehts weiter? Na ganz einfach. Du erhöhst einfach die 
unmittelbar links davon liegende Stelle um 1 (da wo jetzt noch eine 
gedachte führende 0 steht) und beginnst in der Einerstelle wieder bei 0
10, 11, 12, 13, 14, 15, 16, 17, 18, 19
und wieder hast du in der Einerstelle alle deine möglichen 10 Ziffern 
verballert. Aber du kennst das Rezept ja schon: zu der Stelle davor 1 
dazuzählen (also die nächste Ziffer aus dem Vorrat nehmen) und weiter 
gehts.
20, 21, 22, 23, 24, 25, 26, 27, 28, 29
das geht jetzt natürlich solange weiter ...
90, 91, 92, 93, 94, 95, 96, 97, 98, 99
... bis du in der Situation bist, dass du wieder in der Einerstelle 
keine Ziffer mehr hast, und daher in der Zehnerstelle um 1 erhöhen 
musst. Nur: Auch dort hast du bereits alle deine 10 dir zur Verfügung 
stehenden Ziffern aufgebraucht. Was machst du? Du machst dasselbe, was 
du auch gemacht hast, als die Ziffern das erste mal in der Einerstellt 
aufgebraucht waren: du borgst dir wieder in der ersten Stelle links eine 
führende 0 und schreibst das nächste Zeichen deines Vorrats rein. Die 
nächste Zahl nach 99 ist daher 100
und schon läuft der Laden wieder
100 101 102 103 104 105 106 107 108 109
110 111 112 113 114 115 116 117 118 119


Klar soweit?
Zählen ist also nichts anderes als nacheinander alle Zeichen des Vorrats 
'durchzuspielen' und wenn alle Zeichen an einer Stelle aufgebraucht 
sind, dann geht es an der Stelle links davon weiter dort das nächste 
Zeichen zu benutzen. Falls das geht - wenn nicht, dann gehts halt noch 
eine Stelle weiter. etc. etc.

Und im Binärsystem ist das auch nicht anders. Nur dass du nicht 10 
Ziffern hast, sondern nur 2.  Die 0 und die 1

Wie wird also gezählt

 0,  1

mehr geht erst mal nicht. Aber völlig identisch zu oben, nehmen wir 
einfach mal die stille führende 0 mit dazu und erhöhen die auf die 
nächste Ziffer

  10  11

wieder ist aufgebraucht. Die 'Zehner'-Stelle ist ebenfalls aufgebraucht 
(so wie das im Dezimalsystem bei der 99 war), also nehmen wir die 
nächste Stelle links dazu

  100  101
  110  111

und wieder sind wir am Ende. Noch eine Stelle muss her

  1000  1001
  1010  1011
  1100  1101
  1110  1111

und die nächste

  10000 10001
  10010 10011
  10100 10101
  10110 10111
  11000 11001
  11010 11011
  ...

und so geht das immer weiter .... fast

Denn im Computer hat man nicht beliebig viel Platz. Irgendwo muss man 
eine Grenze setzen. Und da hat man als kleinste Zusammenfassung das Byte 
gewählt. Ein Byte besteht aus 8 binären Stellen (den sog. Bits)

D.h. du hast nicht beliebig viele stille führende 0-en sondern nach der 
8-ten ist SChluss. Und da man die 8 Bits oft in einem Aufwasch braucht, 
schreibt man diese 8 Stellen auch tunlichst hin. Etwas das wir im 
Dezimalsystem selten tun (zb bei Uhrzeiten. Wir schreiben nicht 8:5 für 
8 Uhr 5, sondern wir schreiben 08:05)

Hier sind diese 8 Bits, die ein Byte bilden

  0b0000000

und wenn da gezählt wird, dann funktioniert das nach genau dem Schema, 
das du schon kennst

  0b00000000      ( = dezimal 0 )
  0b00000001      (           1 )
  0b00000010      (           2 )
  0b00000011      (           3 )
  0b00000100      (           4 )
  0b00000101      (           5 )
  0b00000110      (           6 )
  0b00000111      (           7 )
  0b00001000      (           8 )

  ....

  0b11111110      (         254 )
  0b11111111      (         255 )

fertig, mehr geht mit 8 Bits nicht.

Dein Port ist nun so verkabelt, dass er auch diese 8 Bits hat. Aber: 
Anstatt dass da die Bits einfach nur gespeichert sind, sind sie aus dem 
µC herausgeführt. Diese Bits kontrollieren also tatsächlich den Pegel 
deiner Ausgangsleitungen. Gut, bei deinem Port sind nur 6 herausgeführt. 
Das hat aber mehr technische Gründe als sonst irgendwas anderes

   0b00000000
     ||||||||
     |||||||+---->  zum Pin RC0
     |||||||
     ||||||+----->  zum Pin RC1
     ||||||
     |||||+------>  zum Pin RC2
     |||||
     ||||+------->  zum Pin RC3

     ..... etc.

D.h. wenn du deinem Port das Bitmuster

     0b00001011

zuweist

   PORTC = 0b00000110;

dann gehen RC1 und RC2 auf 5V und die anderen Pins bleiben auf 0.


Da du aber oben in der Tabelle gesehen hast, dass das Bitmuster 
0b00000110 der dezimalen Zahl 6 entspricht (weil es ja beim Abzählen an 
dieser Position drann kam), kannst du daher auch schreiben

   PORTC = 6;


und es passiert genau dasselbe. Denn 6 ist nur eine andere Schreibweise, 
in einem anderen Zahlensytstem (nämlich im Dezimalsystem) für das 
Biutmuster 00000110.

Der Unterschied ist das 0b in 0b00000110.
Es kennzeichet eine Binärzahl

     PORTC = 0b0000110;
und  PORTC = 00000110;

sind also verschiedene Dinge. Das erste ist die Binärzahl 0000110 und 
das andere ist die Dezimalzahl 110 (ein hundert zehn)


Uff. Ist etwas lang geworden :-)

von Stefan H. (stefan_h68)


Lesenswert?

Wow, vielen lieben Dank für die wirklich sehr ausführliche Antwort, 
jetzt bin ich dem System doch einen Schritt näher gekommen. Schön zu 
wissen, dass es Menschen gibt, die sich die Zeit nehmen einem mal zu 
helfen in der sonst so schnelllebigen hecktischen Welt.

So, das mit den Ports und den Pins hab ich jetzt verstanden, werde das 
nach dem Feiertag gleich mal ausprobieren (hoffentlich klappt es :-) ). 
Was ich jetzt noch nicht ganz verstanden habe ist folgendes: Wenn ich 
durch wenn ich durch das Bitmuster dem Port Hi und Low Pegel zuweisen 
kann, wozu brauche ich dann das TRIS - Register des Ports? In meinem 
Code aus dem ersten Post habe ich das Byte im TRIS-Register auf 
0b00000000 gesetzt. Ohne diese Zeile funktioniert der Code nicht.

von Karl H. (kbuchegg)


Lesenswert?

mal ein kleines Testprogramm, damit man auch mal was sieht
1
int main()
2
{
3
  unsigned char i;
4
5
  ANSEL = 0b00000000;            //All I/O pins are configured as digital
6
  CMCON0 = 0b00000111;           //Coperators turned OFF
7
  PORTC = 0x00;                  //write 0 to port C
8
  TRISC = 0x00;                  //set RC0 as output pin / set TRIS register BIT 0 to 0
9
10
  i = 0;
11
12
  while(1){
13
14
    PORTC = i;
15
    i = i + 1;
16
17
    _delay(1000000);          //delay for 1000000 cycles
18
  }
19
}

von Karl H. (kbuchegg)


Lesenswert?

Stefan H. schrieb:

> So, das mit den Ports und den Pins hab ich jetzt verstanden, werde das
> nach dem Feiertag gleich mal ausprobieren (hoffentlich klappt es :-) ).
> Was ich jetzt noch nicht ganz verstanden habe ist folgendes: Wenn ich
> durch wenn ich durch das Bitmuster dem Port Hi und Low Pegel zuweisen
> kann, wozu brauche ich dann das TRIS - Register des Ports?

Weil du an deinen µC Pins nicht nur LEDs anschliessen willst, sondern 
auch mal einen Taster.
D.h. du brauchst eine Möglichkeit, wie du festlegen kannst, welcher Pin 
als Ausgang und welcher als Eingang fungieren soll.

Ausgang:   dein Programm kontrolliert den Pegel. Eine externe
           Hardware (zb eine LED) reagiert darauf

Eingang:   die externe Hardware (zb ein Schalter oder Taster)
           kontrolliert die Spannung an diesem Pin.
           Dein Programm kann das abfragen und das Programm
           reagiert auf den Pegel.

von MaWin (Gast)


Lesenswert?

_delay auf einem PIC16 nimmt wirklich ein Long als Parameter ?
Ich bin erstaunt.

von Stefan H. (stefan_h68)


Lesenswert?

Karl Heinz Buchegger schrieb:
> int main()
> {
>   unsigned char i;
>
>   ANSEL = 0b00000000;            //All I/O pins are configured as digital
>   CMCON0 = 0b00000111;           //Coperators turned OFF
>   PORTC = 0x00;                  //write 0 to port C
>   TRISC = 0x00;                  //set RC0 as output pin / set TRIS register BIT 
0 to 0
>
>   i = 0;
>
>   while(1){
>
>     PORTC = i;
>     i = i + 1;
>
>     _delay(1000000);          //delay for 1000000 cycles
>   }
> }

Ich habe jetzt zwar nicht die Möglichkeit das zu testen, werde ich aber 
gleich am Freitag machen. So wie ich deinen Code verstehe, müssten die 
Ausgänge beginnend bei RC0 bis RC5 nacheinander schalten? Allerdings 
läuft das ganze nur bis 255 theoretisch. Oder liege ich hier falsch? 
Weil i kann ja maximal 255 werden bzw. 0b11111111?

Karl Heinz Buchegger schrieb:
> D.h. du brauchst eine Möglichkeit, wie du festlegen kannst, welcher Pin
> als Ausgang und welcher als Eingang fungieren soll.

Ah, das heißt also, wenn ich schreibe
1
 
2
TRISC = 0b00010101;

das RCO -> Input
    RC1 -> Output
    RC2 -> Input
    RC3 -> Output
etc.
ist?

von Stefan H. (stefan_h68)


Lesenswert?

MaWin schrieb:
> _delay auf einem PIC16 nimmt wirklich ein Long als Parameter ?
> Ich bin erstaunt.

ich weiß zwar nicht genau was du meinst, da ich nur diesen PIC kenne, 
aber das _delay funktioniert :)

gibt es noch eine andere Möglichkeit eine Zeitverzögerung zu 
implementieren?

von Karl H. (kbuchegg)


Lesenswert?

Stefan H. schrieb:

> Ah, das heißt also, wenn ich schreibe
>
>
1
> TRISC = 0b00010101;
2
>
>
> das RCO -> Input
>     RC1 -> Output
>     RC2 -> Input
>     RC3 -> Output
> etc.
> ist?

Ja.

ABer ich hab jetzt eigentlich nicht vor, hier alles wiederzukäuen, was 
auf unzähligen PIC-Tutorial Seiten und nicht zuletzt im Datenblatt des 
µC des langen und breiten vorgekaut wird.

http://www.mikroe.com/chapters/view/4/

von Karl H. (kbuchegg)


Lesenswert?

Stefan H. schrieb:

> gibt es noch eine andere Möglichkeit eine Zeitverzögerung zu
> implementieren?

Jede Menge. Und ein delay ist die schlechteste Version davon.
Aber das soll dich im Moment nicht daran hindern. Irgendwo muss man mal 
anfangen und man kann nicht alles auf einmal lernen.

MaWin war nur erstaunt darüber, dass man die Anzahl der zu verballernden 
Zyklen angibt und nicht gleich direkt eine Zeitangabe und der µC rechnet 
sich selber aus, wieviele Taktzyklen er verballern muss um auf diese 
Zeit zu kommen. Dazu müsste allerdings das Programm wissen, wie schnell 
der µC getaktet wird. Überlässt man diese Rechnerei dem Programmierer, 
ist es zunächst erst mal einfacher. Auch wenn es für den Programmierer 
erst mal mehr Arbeit bedeutet.

von MaWin (Gast)


Lesenswert?

> MaWin war nur erstaunt darüber, dass man die Anzahl der zu verballernden
> Zyklen angibt und nicht gleich direkt eine Zeitangabe und der µC rechnet
> sich selber aus,

Nein, ich war erstaunt, daß micht bei 65535 Schluss ist mit der _delay 
Zahl, oder gar bei 255.

Denn das heisst, daß der Compiler für den kleinen PIC einen int als 32 
bit Wert umsetzt.

von Karl H. (kbuchegg)


Lesenswert?

MaWin schrieb:
>> MaWin war nur erstaunt darüber, dass man die Anzahl der zu verballernden
>> Zyklen angibt und nicht gleich direkt eine Zeitangabe und der µC rechnet
>> sich selber aus,
>
> Nein, ich war erstaunt, daß micht bei 65535 Schluss ist mit der _delay
> Zahl, oder gar bei 255.

Das erstaunt jetzt mich wieder.
Was willst du mit maximal 65535 verballerten Zyklen gerade als Anfänger 
anfangen?

> Denn das heisst, daß der Compiler für den kleinen PIC einen int als 32
> bit Wert umsetzt.

sind doch nur 4 ineinander geschachtelte Byte-Schleifen. Und ob das so 
genau zugeht ... ich hab da so meine Zweifel. Aber egal. Er braucht erst 
mal eine einfach Version um irgendwas im Sekundenbereich hinzukriegen. 
Und dafür dürfte es ja reichen.

> int als 32
> bit Wert umsetzt.

wie kommst du da drauf?
auf einem int mit 16 Bit ist ein 1000000 automatisch ein long.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.