Lässt sich ein ATtiny13 in WinAVR trotz seines kleinen Flash-Speichers von 1kByte so programmieren, dass er mit einem PC über dessen serielle Schnittstelle kommunizieren kann, und zwar mit 9600 Baud?
Dies ist möglich, wie ich im Folgenden aufzeigen werde. Kürzlich entwarf ich nämlich in Anlehnung an Burkhard Kainkas Assembler-Unterprogramme zum Empfangen und Senden für den ATtiny13 auf der Platine des Lernpakets Mikrocontroller ein Programm in WinAVR, damit dieser Mikrocontroller, wie es in den ursprünglichen Programmen in Assembler der Fall ist, über Pin PB1 senden und über PB2 empfangen kann.
Es entstand schließlich für den Kleinen, der über einen Flash-Speicher von nur 1 kByte verfügt, ein „bescheidenes“ Interface-Programm, mit dem man den Analog-Eingang ADC2 im 8-Bit-Modus einlesen kann. Außerdem lässt sich PortB.3 setzen bzw. wieder zurücksetzen. Man kann auch ein Byte am PWM-Ausgang PB0 ausgeben lassen. Zusätzlich kann der Analog-Eingang ADC0 an Pin 1, der auch als RESET-Eingang dient, abgefragt werden.
Allerdings wird anders als bei einem Assembler-Programm für die vorliegende Programmversion in WinAVR wesentlich mehr Programmspeicher, nämlich ca.68%, benötigt.
Dies ist ein Auszug aus dem Programmlisting:
...
void main()
{
DDRB=0b00001011;// PB5: Eingang (ADC0, RESET) PB2:
Eingang(RXD)
//
PB0:Ausgang, PB3: Ausgang, PB4: Eingang(ADC2)
ADC_Init();
while(1)
{
A_Reg=0;
RdCOM();
A_Reg=E_Reg;
switch (A_Reg)
{
case 1: // PWM
PWM_Init();
RdCOM();
OCR0A=E_Reg;
break;
case 2:
ADMUX |= (1<<MUX1);
// MUX1 setzen
RdADC(); // ADC2
lesen
WrCOM();
break;
...
Zum Senden an den PC wird die folgende Routine verwendet:
void WrCOM()
{
PORTB |=(1<<PB1);
// TXD = 1
warte();
for (c=0;c<8;c++)
{
if((E_Reg & 1)==0)
{
PORTB |=(1<<PB1);
// TXD = 1
}
else
{
PORTB &=~(1<<PB1); // TXD = 0
}
warte();
E_Reg=E_Reg>>1;
}
PORTB &=~(1<<PB1); // TXD = 0
warte();
}
WrCOM und RdCOM verwenden abwechselnd folgende Routine:...
void warte()
{
for (n=0;n<7;n++);
// 1 Bitlaenge
}
…
Auf einem kleinen Steckboard befinden sich wenige zusätzliche Bauteile, mit denen sich das oben erwähnte Interface-Programm für den Attiny13 erproben lässt.Die Reihenschaltung von R2 und R3, die Kollektor-Emitter-Strecke des Fototransistors T1 im Optokoppler sowie die Schaltung im Inneren des Mikrocontrollers sorgen dafür, dass die Spannung am Reset-Eingang nicht soweit sinkt, dass ein RESET ausgelöst wird. Wird der Eingang E des Optokopplers mit dem 5V-Anschluss verbunden, leuchtet LED1, und die Kollektor-Emitter-Strecke des Transistors T1 wird niederohmig. Die Spannung an ADC0 steigt von zuvor etwa 2,57 V auf ca. 4,75 V an.
Die Software auf der CD zum Lernpaket Mikrocontroller beinhaltet ein Terminal-Programm, von dem hier ein Screenshot zu sehen ist. Gerade wurde durch Senden einer 2 an den Mikrocontroller dieser dazu veranlasst, ADC2 einzulesen und den Wert, im Bild 92, zu senden. Danach wurde an den Attiny13 eine 5 gesendet, worauf dieser bei offenem Eingang E des Optokopplers 132 zurücksendete und anschließend bei erneutem Senden einer 5 den Wert 245 an den PC übertrug, als der Eingang E des Optokopplers mit dem 5V-Anschluss verbunden war. Schließlich wurde durch Übertragung einer 1 und dem Wert 127 der PWM-Ausgang des Attiny13 angesteuert. Anschließend wurde PB3 zunächst mit einer 3 gesetzt und darauf mit einer 4 zurückgesetzt.
Die Werte der Bauteile der abgebildeten zusätzlichen Schaltung wurden so gewählt, dass diese im Betrieb den Spannungsregler auf der Platine des Lernpakets Mikrocontroller nur minimal belasten. Die Stromstärke in der Zuleitung zum Steckboard beträgt weniger als 3 mA.
Download: T13_SER4.zip
// softuart-echo-final.c
#include <avr/io.h>
#include <util/delay_basic.h>
unsigned char n,A_Reg,c,E_Reg;
void WrCOM(){
PORTB |=(1<<PB1); // TXD = 1
_delay_loop_1(27);
for (c=0;c<8;c++){
if((E_Reg & 1)==0){
PORTB |=(1<<PB1); // TXD = 1
}
else{
PORTB &=~(1<<PB1); // TXD = 0
}
_delay_loop_1(27);
E_Reg=E_Reg>>1;
}
PORTB &=~(1<<PB1); // TXD = 0
_delay_loop_1(27);
}
void RdCOM(){
while((PINB & 4)==0);
_delay_loop_1(45);
E_Reg=0;
for(c=0;c<8;c++){
E_Reg=E_Reg>>1;
if((PINB & 4)==4){
E_Reg=E_Reg|128;
}
_delay_loop_1(30);
}
_delay_loop_1(30);
E_Reg =~E_Reg;
}
void main(){
DDRB=0b00000010; // PB1:Ausgang
PORTB=0b00011010; // Pullups an ungenutzten Eingängen
//E_Reg = 85;
//WrCOM();
while(1){
RdCOM();
WrCOM();
}
}