
ATtiny202 und CCL
CCL
heißt Configurable Custom Logic und ist eine interessante Peripherie
der Tiny0-Serie, die ich gerne ausprobieren möchte. Vorher aber soll
der Brenner noch etwas komfortabler werden. Jetzt brauche ich nur noch
auf pyupdi klicken,
um den Chip zu brennen. Damit entfällt das lästige Kopieren des
Hexfiles. Fabi hatte herausgefunden, wie man einen Arduino mit
jtag2updi einsetzt und in das Atmel-Studio einbindet. Und Rainer hat
die Einbindung für pyupdi hinbekommen. Man muss im Fenster External
Tools den Namen des Brenners (pyupdí) und das zu startende Programm
(py.exe) eingeben und die Arguments festlegen:
C:\Users\User\pyupdi.py -d tiny202 -c COM4 -b 115200 -f $(TargetDir)$(TargetName).hex
Was von PC zu PC verschieden sein kann, ist der Dateipfad
(C:\Users\User\) und die COM-Nummer (COM4). Außerdem habe ich hier eine
höhere Baudrate eingegeben. Damit funktioniert es ebenfalls.
Dreifach-AND
Zurück zum eigentlichen Thema, CCL. Informationen findet man im Datenblatt und in einer Application Note von Microchip: http://ww1.microchip.com/downloads/en/AppNotes/TB3218-Getting-Started-with-CCL-90003218A.pdf Die Quelltexte dazu findet man bei Github: https://github.com/MicrochipTech/TB3218_Getting_Started_with_CCL
Die Beispiele sind für einen ATmega4809 geschrieben. Als erstes habe
ich das AND-Beispiel genommen und für den Tiny202 angepasst. Änderungen
betreffen die verwendeten Ports und die Wahrheitstabelle für die
gewünschte logische Funktion. Beim Tiny202 liegen die drei Eingänge an
A0, A1 und A2. Der Ausgang liegt an A6. Und hier muss die Tabelle LUT0
verwendet werden, nicht aber wie im Beispiel LUT1. Mit allen
erforderlichen Änderungen sieht das Programm nun so aus:
//ATtiny202 CCL 3 Input AND
#include <avr/io.h>
void PORT0_init (void);
void CCL0_init(void);
/**
* \brief Initialize ports
*/
void PORT0_init (void)
{
PORTA.DIR &= ~PIN0_bm; //PA0 - LUT0 IN[0]
PORTA.DIR &= ~PIN1_bm; //PA1 - LUT0 IN[1]
PORTA.DIR &= ~PIN2_bm; //PA2 - LUT0 IN[2]
PORTA.DIR |= PIN6_bm; //PA6 - LUT0 output
}
void CCL0_init(void)
{
//configure inputs for used LUTs
CCL.LUT0CTRLB = CCL_INSEL0_IO_gc /* IO pin LUTn-IN0 input source */
| CCL_INSEL1_IO_gc; /* IO pin LUTn-IN1 input source */
CCL.LUT0CTRLC = CCL_INSEL2_IO_gc; /* IO pin LUTn-IN2 input source */
//Configure Truth Table
CCL.TRUTH0 = 0x80; /* Truth 1: 128 */
//Enable LUT0 output on IO pin
CCL.LUT0CTRLA = CCL_OUTEN_bm; /* Output Enable: enabled */
//Enable LUTs
CCL.LUT0CTRLA |= CCL_ENABLE_bm; /* LUT Enable: enabled */
//Enable CCL module
CCL.CTRLA = CCL_ENABLE_bm; /* Enable: enabled */
}
int main(void)
{
PORT0_init();
CCL0_init();
while (1)
{
;
}
}
Und
das Dreifach-AND-Gatter funktioniert wie gewünscht! Nur wenn ich A0, A1
und A2 an VCC lege, geht auch der Ausgang A7 hoch. A0 hing übrigens
noch am Programmiergerät (RXD und TCD über Widerstand) und blieb im
Ruhezustand high. Den Eingang A2 habe ich dann testweise an einen
externen Generator gelegt. Bis über 100 kHz habe ich getestet, und
konnte kein Jitter erkennen. Das bedeutet, dass tatsächlich ein völlig
unabhängiges AND-Gatter gebildet wurde, das in keiner Weise vom
internen Takt des Controllers abhängt. Man kann zwar auch die
Eingangssignale mit dem Takt synchronisieren, aber das wird in diesem
Beispiel bewusst nicht gemacht. In einem zweiten Test habe ich dann
meinen Kurzwellensender ganz vorsichtig über einen Schutzwiderstand an
den Eingang gelegt. Bis 24 MHz konnte das Gatter mithalten! Das öffnet
ungeahnte Möglichkeiten sogar für HF-Anwendungen.
Entscheidend für die AND-Funktion war die Einstellung CCL.TRUTH0 = 0x80;. Damit ist Bit 7 in TRUTH0
gesetzt, mit dem Ergebnis, dass alle drei Eingange 1 sein müssen, damit
der Ausgang 1 wird. Man kann auch mehrere Bits gleichzeitig setzten, um
eine bestimmte Funktion zu bekommen.
Inverter
Um alles etwas genauer zu verstehen, wollte ich einen einzelnen
Inverter mit dem Eingang PA1 und dem Ausgang PA6 programmieren. Der
Gedanke dabei war, dass man damit auch einen Oszillator bauen könnte.
In diesem Fall muss ich Bit 0 setzen, damit der Ausgang hochgeht, wenn
alle Eingänge low sind, CCL.TRUTH0 = 0x01; Weil ich aber nur einen
Eingang brauche, wird auch nur einer definiert. CCL.LUT0CTRLB =
CCL_INSEL1_IO_gc; kann auch als CCL.LUT0CTRLB = 0x50; geschrieben
werden. So wird PA1 als Eingang eingerichtet.
//ATtiny202 CCL, PA1 Inverter/Oscillator PA6
#include <avr/io.h>
void PORT0_init (void);
void CCL0_init(void);
/**
* \brief Initialize ports
*/
void PORT0_init (void)
{
PORTA.DIR &= ~PIN1_bm; //PA1 - LUT0 IN[1]
PORTA.DIR |= PIN6_bm; //PA6 - LUT0 output
}
/**
* \brief Initialize CCL peripheral
*/
void CCL0_init(void)
{
//configure inputs for used LUTs
CCL.LUT0CTRLB = CCL_INSEL1_IO_gc; /* IO pin LUTn-IN1 input source */
//Configure Truth Table
CCL.TRUTH0 = 0x01; /* Truth 0: 1 */
//Enable LUT0 output on IO pin
CCL.LUT0CTRLA = CCL_OUTEN_bm; /* Output Enable: enabled */
//Enable LUTs
CCL.LUT0CTRLA |= CCL_ENABLE_bm; /* LUT Enable: enabled */
//Enable CCL module
CCL.CTRLA = CCL_ENABLE_bm; /* Enable: enabled */
}
int main(void)
{
PORT0_init();
CCL0_init();
while (1)
{
;
}
}
In der Form funktioniert das Programm. Wenn ich PA1 an GND lege geht
PA6 hoch, wenn ich PA1 an VCC legen, geht PA6 runter. Und wenn ich mit
einem Widerstnd eine Gegenkopplung baue? Dann entstehen Schwingungen.
Mit 10 k bekomme ich ca. 7 MHz. Ein Kondensator zwischen Eingang und
GND liefert eine kleinere Frequenz. Mit 10 k und 100 nF werden es ca.
2,5 kHz. Aus dem Verhältnis 7000 kHz / 2,5 kHz kann man die Kapazität
des Eingangs und des Aufbaus mit 35 pF abschätzen. Wenn ich allerdings
1 k verwende, steigt die Frequenz nicht auf 70 MHz, sondern nur auf
17,8 MHz. Und eine direkte Drahtverbindung zwischen Eingang und Ausgang
liefert 32 MHz. Daraus ergibt sich eine Gatterlaufzeit von ca. 30 ns.
Der Oszillator
funktioniert bei kleineren Frequenzen so schön, als wäre er mit einem
NE555 gebaut. Das bedeutet, dass der Eingang eine Hysterese haben muss.
Ein Blick auf den Eingang zeigt tatsächlich eine Hysterese von etwa
einem Volt. Jedenfalls hat die CCL-Einheit hier einen ersten
praktischen Nutzen. Man kann sich einen zuverlässigen RC-Oszillator
ohne ein zusätzliches IC bauen.