Assembler Tiny13, 8bits über Port outshiften in Assembler, ist das möglich?

dg2ffp

Neues Mitglied
26. Jan. 2012
20
0
1
54
Sprachen
Hallo,
ich möchte gerne in Assembler mit einem Tiny13 8bits in ein Schieberegister (z.B 74hc595)shiften. Da der Tiny13 kein seriel-Interface besitzt, muss man die 3 benötigten Signale zu Fuß ausgeben oder gibt es eine einfachere Lösung dafür?
Gruß Heiko
 
Hallo Heiko,

der 13er hat praktisch nur Ports und den ADC - nichts womit man die serielle Ausgabe automatisieren könnte.

Bist du nur zu faul ;-), das zu programmieren, oder gibt's einen anderen Grund für die Frage ?

Gruß
Pirx
 
Hallo Heiko,
Da der Tiny13 kein seriel-Interface besitzt, muss man die 3 benötigten Signale zu Fuß ausgeben oder gibt es eine einfachere Lösung dafür?

also "zu Fuß" ist doch schon ziemlich einfach. Ich schätze den Aufwand für den Code zum Shiften auf knapp 10 Instructions, also ein klein wenig mehr, als ein Hardwaremodul vom AVR konfigurieren. Das Thema mit dem 595 Shift-Register hatten wir hier im Forum schon öfter mal, ich meine ich hätte da auch schon mal irgendwo eine Lösung in Assembler geschrieben.

Dirk :ciao:
 
Wie Pirx schon angedeutet hat, verfügt der Tiny13 über kein einziges Kommunikationsinterface. Als Hardware hast Du lediglich Den ADC, einen Analog-Komperator und einen 8-bit-Timer (immerhin mit 2 PWM-Ausgängen, bzw einen wenn OCR0A für 'ne Frequenzkorrektur verwendet wird). Er kann keinen externen Quarz treiben.
Jeder I/O kann den PinChangeInterrupt triggern.
Das wars, glaub ich.
Folglich mußt Du mit diesem AVR "zu Fuß" schieben.
Als Master sollte das aber kein Thema sein, insbesondere, wenn man sich über das Tempo keine Gedanken machen muß... der 595 kann doch schneller, oder?
Wenn Du unbedingt 'ne automatisierbare Hardware-Lösung suchst, brauchst Du 'n anderen AVR.
Als Alternative mit 8 Pins fällt mir da eigentlich nur der ATtiny25/45/85 ein. Der hätte USI.
 
Hallo zusammen,

einige werden mich jetzt wahrscheinlich :viking:... hier mal ein Beispiel, wie man das "zu Fuß" machen kann. ;)

Das Beispiel muss entsprechend noch angepasst werden, es ist auch nicht getestet.

Dirk :ciao:

Code:
; PB0: Data
; PB1: SCK
; PB2: Store
; r16: Data
; geändert wird r17 und SREG

SerialShiftOut:

   ldi r17, 8
serialshiftout_loop:

   rol r16            ; links rotieren, bit 7 in carry
   brcs serialshiftout_set
   cbi PORTB, PB0    ; Data low
   rjmp serialshiftout_sck
serialshiftout_set:
   sbi PORTB, PB0    ; Data high

serialshiftout_sck:
   cbi PORTB, PB1    ; SCK
   sbi PORTB, PB1

   dec r17
   brne serialshiftout_loop

   cbi PORTB, PB2    ; Store
   sbi PORTB, PB2

ret
 
Hallo, vielen Dank erstmal für die Infos.

@Pirx Zu faul bin ich nicht, hatte bisher alles in Bascom realisiert, möchte mich jetzt aber auch mit assembler beschäftigen. Da ist dies erst seit ein paar Tagen mache, deswegen vielleicht diese naive Frage.


@LotadaC Mit dem USI vom Tiny25 wird das wohl in assembler erstmal nichts geben, das wird für den Anfang zu sehr kompliziert sein.


@Dirk Werde mir das Beispiel auf dem Simulator anschauen und testen, ggf. auch direkt am Port messen, was da passiert. Danke für das
Beispiel.

Gruß Heiko
 
...@Dirk Werde mir das Beispiel auf dem Simulator anschauen und testen, ggf. auch direkt am Port messen, was da passiert. Danke für das Beispiel...
Da könnte man jetzt noch das R17 über den Stack retten und wiederherstellen lassen, um dann im laufenden Programm nicht mehr drauf achten zu müssen. Achso, die Pins müssen natürlich vorher mal irgendwo zum Ausgang gemacht worden sei, klar.
Außerdem künnte man SCK, Data und Store als Kompilervariablen/Konstanten (.equ) irgendwo im Kopf festlegen lassen, um das im Programm einfach ändern zu können.

Zum Algorithmus - der ist ja hier so (in der Schleife):
Code:
Datenbyte rollen
Wenn 'ne 1 ins carry fällt, springe nach "eins", sonst:
  lösche Datenbeinchen
  springe nach "Clock-Impuls"
"eins":
  setze Datenbeinchen
"Clock-Impuls":
Impuls durch löschen und setzen des Clock-Beinchens
Bitzähler dekrementieren, und ggf (Z-Flag=0) zurück zum Anfang,
sonst Impuls am Store-Beinchen (Latch) und Ende

Könnte man auch so machen:
Code:
Datenbeinchen löschen
Datenbyte rollen
Wenn 'ne 0 ins carry fällt, springe nach "Clock-Impuls", sonst:
  setze Datenbeinchen
"Clock-Impuls":
Impuls durch löschen und setzen des Clock-Beinchens
Bitzähler dekrementieren, und ggf (Z-Flag=0) zurück zum Anfang,
sonst Impuls am Store-Beinchen (Latch) und End
...@LotadaC Mit dem USI vom Tiny25 wird das wohl in assembler erstmal nichts geben, das wird für den Anfang zu sehr kompliziert sein..
Ach... so schlimm sollte das auch nicht sein. Eine Lösung steht ja schon im Datenblatt des Tiny25 (15.3.2 auf S. 113). Entgegen dem richtigen SPI ist das USI nicht selbst an irgendeine Taktquelle gekoppelt. Es verfügt aber über einen 4-bit-Zähler, der beim Überlauf einen Interrupt auslösen kann. Der Zähler zählt die Flanken der SCK, dabei wird außerdem das Datenregister geschoben (wobei das herausgeschobene Bit im DO landet, die andere Seite mit DI gefüllt wird.. Als Master kann man diese Flanken auf unterschiedliche Arten auslösen.
Im Beispiel erfolgt das durch setzen des USITC-Bits im USICR.
Das ganze soll in der Schleife wiederholt werden bis der 4-bit-Zähler überläuft. Dabei wird automatisch das USIOIF (USI Counter Overflow Interrupt Flag) im USISR gesetzt, welches in der Schleife geprüft wird. Achtung, hier scheint irgendwer beim Copy&paste Mist gemacht zu haben. Das Register wird hier nach R16 geladen, und das Bit ausgewertet. R16 enthält aber eigentlich das Bitmuster, welches ins USICR geschrieben wird, um die SCK zu generieren (also jede Flanke). Würde hier überschrieben werden.
In anderen Tiny-Datenblättern wird USIOIF direkt im USISR geprüft (bzw eben die entsprechende Skip-Operation für I/O-Register (SBIS) verwendet), und gar nicht in ein Rechenregister geladen. Im Tiny25 hat USISR die Adresse 0x0E - würde also auch hier gehen. Warum man hier unbedingt über ein Rechenregister gehen wollte ist mir nicht klar - aber R16 wird in der Schleife bereits anderweitig verwendet.
C&P läßt sich übrigens auch am dann folgenden Abschnitt erahnen:
Code:
The code is size optimized using only eight instructions (plus return)...
Es sind aber 9. Wenn man das Register eben nicht nach R16 laden würde, und statt mit SBRS das Rechenregister... mit SBIS gleich das I/O-Register prüfen würde... ja, dann wärens 8.

Ein 2tes Beispiel verzichtet auf den Zähler, stattdessen werden einfach 16 Toggle-Impulse nach USITC geschrieben, bei jedem 2ten wird außerdem das Registerschieben ausgelöst (USI Clock Strobe).

Als letzte Möglichkeit könnte man den Zähler übrigens noch an den Timer0 CompareMatch heften (hm... A oder B? Timer0 hat ja 2 OCR... beim Tiny26 ging das nur mit dem TOV0)
 
Hallo LotdaC,

danke für deine recht ausführliche Informationen. Werde verschiedene Dinge ausprobieren. Der Hintergrund ist der,
ich möchte aus Platzgründen auf der Schaltung einen Tiny10 programmieren, das geht leider nicht mit Bascom, deswegen möchte ich mich mit dem Assembler beschäftigen. Die Hardware ist beim Tiny10 noch etwas kleiner als beim Tiny13, aber besitzt auch einen AC und ADC (8bit)und paar I/Os.
Ich erkenne zwar noch nicht die Vorteile von verschiedenen Befehlen wie z.B. die Stapelverarbeitung während des Ablaufes eines Programmes oder das Nutzen von den Restigern 0-15, aber ich komme bestimmt auch bald dahinter, um das optimal auszunutzen.

Gruß Heiko
 
Du meinst diesen Tiny, oder diesen?

Dazu einige Hinweise:
- Daß der Tiny via TPI programmiert werden muß, ist Dir wahrscheinlich klar (deswegen nicht Bascom )
- bei mir war ein update des Studios (V4) auf irgendein SP nötig, außerdem ein Firmwareupgrade des AVRisp MKII.
- der Tiny startet immer mit dem internen RC-Oscillator (hat gar keine entsprechenden Fußes) @8MHz/8=1MHz. Dein Programm kann aber zur Laufzeit den SystemClockPrescaler manipulieren, ebenso die ClockSource. Beachte hier auch:
- Einige Register sind "protected", und erfordern ein besonderes Schreib-Procedere (steht dann immer im DB)
- abweichend von den meisten anderen AVR verfügt er nur über 16 Rechenregister. Bei so einem kleinen Controller sollte das aber reichen.
- LPM kennt der Tiny nicht, um Konstanten aus dem Flash laden zu können, ist dieser lesetechnisch in den SRAM ge-remapped - ab Adresse 0x4000 also mit LD bzw LDS zu erreichen.

Nur so fürs erste. Viel Spaß mit dem Zwerg!

Nachtrag: Hier hatten wir übrigens einen seriellen Empfang implementiert: SW-UART mit 8fach Oversampling. Das empfangene Byte wurde als duty einer PWM verwendet. Ich hatte das spassenshalber auf dem Tiny10 umgesetzt.
 
Hallo LotadaC,

sind die Bilder verschiedene Outline's vom Tiny10? Ich wollte den in der SMD-Ausführung nehmen.
Das die kleinen Zwerge ihre Eigenheiten haben, war mir bekannt. Ich werde erstmal mit dem Tiny13 üben und mir ein
Überblick verschaffen.
Ich habe mir von Dirk das Beispiel etwas näher angeschaut und finde es sehr passend für Controller, die dafür keine
Hardware besitzen. Dadurch habe ich verstanden, wenn eine eins herausgeschoben wird, dass dabei das carry-flag gesetzt wird.
Naja, es gibt eben noch viel zu Lernen.

Gruß Heiko
 
Hallo Heiko,

sind die Bilder verschiedene Outline's vom Tiny10? Ich wollte den in der SMD-Ausführung nehmen.
Das die kleinen Zwerge ihre Eigenheiten haben, war mir bekannt. Ich werde erstmal mit dem Tiny13 üben und mir ein
Überblick verschaffen.
der Tiny10 ist im SOT23-6 Gehäuse. Den gibts nach meiner Info nur in dem Gehäuse. Er ist auf den Fotos lediglich einmal mit meinen Klemmen angeschlossen und einmal von LotadaC mit einem Nullkraftsockel.

Ich habe mir von Dirk das Beispiel etwas näher angeschaut und finde es sehr passend für Controller, die dafür keine
Hardware besitzen. Dadurch habe ich verstanden, wenn eine eins herausgeschoben wird, dass dabei das carry-flag gesetzt wird.
Naja, es gibt eben noch viel zu Lernen.

Dann hast du das nicht ganz verstanden. Du verwechselst Ursache und Wirkung.

Das Bit wird aus dem Register ins Carry-Bit geschoben.
Das Carry-Bit wird als Bedingung für einen Sprungbefehl verwendet (wie zB ... If Carry=1 then Goto 30)
Durch den bedingten Sprung wird das PortBit durch entsprechende Befehle entweder mit einer 1 oder mit einer 0 geladen. Je nachdem welche Befehle übersprungen werden und welche ausgeführt werden.
Danach wird durch zwei Befehle ein Kurzer Taktimpuls erzeugt.

Gruß
Dino
 
Hallo Dino,

Heiko hat sich bestimmt nur auf "rol" bezogen, hier wird aus dem Register (bei meinem Beispiel r16) das Bit7 in das Carry geschoben. Carry ist dann gesetzt, wenn das Bit7 "1" war. Dann hat er es doch verstanden :hmmmm: :)

Ob man Carry (bzw. SREG) und andere Register, die in der Routine verändert werden, dort auch sichern muss (zB. über den Stack), hängt vom Programm ab ... vom Programmierstil. Ich verwende oft bestimmte Register zur Parameterübergabe, die auch verändert werden können, da spare ich Zeit und Code, und manche wenige haben feste "Aufgaben", hier vermeide ich diese überhaupt zu verwenden.

Dirk :ciao:
 
Es ist sogar ein und derselbe!
@Dino: als UDFN kannst Du den auch verlöten. (Dir trau ich das echt zu...)

@Heiko: In meiner Liste sind auch Punkte, die den tn10 vom tn13 unterscheiden. Das mit den protected I/O-Registern, der Clock-Geschichte, und den fehlenden unteren 16 Rechenregistern. Auf die kann man zwar wie üblich zugreifen, man landet dabei aber auch (Remapping) auf den oberen 16 Registern.
Also: gut das Datenblatt lesen!
 
Hallo Dirk,

könnte ich bei deinem Beispiel dies in ein Macro einbinden und dann zweimal aufrufen um z.B. 16 bit zu shiften? Das R16 könnte man vorher ändern.

Gruß Heiko
 
Hallo Heiko, ich würde da kein Macro verwenden.

Lass es doch in einer Funktion und verwende als Parameter zum Beispiel zl, zh (r30, r31) anstelle r16. Die Schleife machst du eben zweimal hintereinander. Das wäre auch passend und du könntest diese Funktion in anderen Projekten einfach einbauen.

Dirk :ciao:
 
Wozu das Macro? (Geht natürlich, aber...)
Das ist doch schon 'ne Subroutine.
Du könntest(!) auch 'ne Subroutine schreiben, die 2x Dirks Subroutine aufruft.
Oder Dirks Subroutine auf Words umschreiben (zB indem das eine Byte (über das Carry) in das andere gerollt wird, und dieses dann (über das Carry) auf den Data-Pin. Das ganze dann halt 16x...
 
Hallo,

dann werde ich das so als Funktion lassen, ich dachte man könnte es etwas kürzen.
Manche Ic's haben 32 Register die über spi gefüllt werden wollten, werde dies mal versuchen.
Danke nochmals für die Info. Ich hoffe es gelingt mir.

Gruß Heiko
 
Hi Heiko,

du kannst die Funktion auch so belassen, im Moment schiebst du ein Byte. Möchtest du 4 Byte schieben, rufst du sie einfach vier mal mit anderem Parameter auf. Man könnte es ja auch anders herum sehen: was ist, wenn du eine Funktion hast, die 32 Bit schiebt, der Slave aber nur 8Bit benötigt ;-)

Ob mein Beispiel funktioniert, ich denke schon, getestet habe ich es nicht. Nur mal so als Hinweis :D

Falls du nicht zurecht kommst ... wir haben hier einige User, die sich gut in Assembler auskennen :cool: Das wirst du aber bestimmt hinbekommen :D



Dirk :ciao:
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)