Vollduplex-Software UART für ATtiny13

Hallo an alle Benutzer von Geektimes! Als Ergebnis meines Dienstes musste ich einmal die Software UART auf dem beliebten ATtiny13-Mikrocontroller implementieren. Beim Googeln habe ich eine große Anzahl von Artikeln zu diesem Thema gefunden, von denen viele hier veröffentlicht sind:


Und es gibt noch andere Ressourcen:


, -, ( ). , -, CodeVision AVR, , -, . C UART-. , ( 8- ). , .. , AVR.

— , , .

Also fangen wir an. Ich werde einfach die Header-Datei "uart13.h" in der Form veröffentlichen, in der sie mit Kommentaren im Code versehen ist. Dort ist alles einfach.

Uart13.h-Headerdatei
/*    UART   ATtiny */

#ifndef _UART13_H_
#define _UART13_H_ 1

#include <avr/io.h>
#include <avr/interrupt.h>

/*
*	        
*	    UART.
*/

#define TXPORT PORTB		//    
#define RXPORT PINB		//    
#define TXDDR DDRB		//     
#define RXDDR DDRB		//     
#define TXD 0			//       
#define RXD 1			//       

/*
*	  ,     ()
*	 BAUD_DIV   :
*	BAUD_DIV = (CPU_CLOCK / DIV) / BAUD_RATE
*	 CPU_CLOCK -   , BAUD_RATE -   UART,
*	 DIV -    ,   TCCR0B.
*	,   9.6 ,   8,   9600 :
*	BAUD_DIV = (9 600 000 / 8) / 9600 = 125 (0x7D).
*/

//#define T_DIV		0x01	// DIV = 1
#define T_DIV		0x02	// DIV = 8
//#define T_DIV		0x03	// DIV = 64
#define BAUD_DIV	0x7D	//  = 9600 

/*
*	         UART
*/

volatile uint16_t txbyte;
volatile uint8_t rxbyte;
volatile uint8_t txbitcount;
volatile uint8_t rxbitcount;

void uart_init();
void uart_send(uint8_t tb);
int16_t uart_recieve(uint8_t* rb);

#endif /* _UART13_H_ */



Aber ich werde die Beschreibung des Bibliotheksimplementierungscodes in Teile zerlegen, um den Artikel nicht in einen riesigen Spoiler mit Code zu verwandeln.

Unterbrechen Sie TIM0_COMPA
ISR(TIM0_COMPA_vect)
{
	TXPORT = (TXPORT & ~(1 << TXD)) | ((txbyte & 0x01) << TXD); //    TXD   txbyte
	txbyte = (txbyte >> 0x01) + 0x8000;	//  txbyte   1   1    (0x8000)
	if(txbitcount > 0)			//    (   ),
	{
		txbitcount--;			//     .
	}
}


OCR0A. , , OCR0A. , TCNT0 ( CTC, TCCR0A). UART. txbyte : , , txbyte TXD , , .

Solange keine Daten zu übertragen sind, wird die Nummer 0xFFFF in der Variablen gespeichert und daher wird am TXD-Ausgang kontinuierlich ein hoher Logikpegel aufrechterhalten. Wenn wir Daten übertragen möchten, müssen wir die Anzahl der zu übertragenden Bits in den Bitzähler schreiben: 1 Start, 8 Datenbits und 1 Stopp für insgesamt 10 (0x0A) und txbyte-Daten für die Übertragung zusammen mit dem Startbit eingeben. Danach werden sie sofort übertragen. Die Funktion void uart_send (uint8_t tb) ist an der Bildung des Pakets beteiligt.

Unterbrechen Sie TIM0_COMPB
ISR(TIM0_COMPB_vect)
{
	if(RXPORT & (1 << RXD))			//      RXD
		rxbyte |= 0x80;			//   1,   1    rxbyte
	
	if(--rxbitcount == 0)			//   1         
	{
		TIMSK0 &= ~(1 << OCIE0B);	//  ,   TIM0_COMPB
		TIFR0 |= (1 << OCF0B);		//    TIM0_COMPB
		GIFR |= (1 << INTF0);		//     INT0
		GIMSK |= (1 << INT0);		//   INT0
	}
	else
	{
		rxbyte >>= 0x01;		//   rxbyte   1
	}
}


OCR0B. TIM0_COMPA, , , TCNT0. , , . , RXD , , rxbyte, , , . rxbyte, .

INT0
ISR(INT0_vect)
{
	rxbitcount = 0x09;			// 8    1  
	rxbyte = 0x00;				//   rxbyte
	if(TCNT0 < (BAUD_DIV / 2))		//        
	{
		OCR0B = TCNT0 + (BAUD_DIV / 2);	//         
	}
	else
	{
		OCR0B = TCNT0 - (BAUD_DIV / 2);	//        
	}
	GIMSK &= ~(1 << INT0);			//    INT0
	TIFR0 |= (1 << OCF0A) | (1 << OCF0B);	//    TIM0_COMPA (B)
	TIMSK0 |= (1 << OCIE0B);		//    OCR0B
}


INT0. INT0, . 9, rxbyte. OCR0B, TIM0_COMPB, . TIM0_COMPB , INT0 .

UART.

uart_send
void uart_send(uint8_t tb)
{
	while(txbitcount);		//      
	txbyte = (tb + 0xFF00) << 0x01; //     txbyte        1
	txbitcount = 0x0A;		//     10
}


UART. , . , , 8 txbyte , 8 0xFF, , . 10.

uart_recieve
int16_t uart_recieve(uint8_t* rb)
{
	if(rxbitcount < 0x09)		//       9
	{
		while(rxbitcount);	//     
		*rb = rxbyte;		//      
		rxbitcount = 0x09;	//    
		return (*rb);		// 
	}
	else
	{
		return (-1);		//   -1 ( )
	}
}


UART. 8- , . , , , (-1). , . , , (-1).

uart_init
void uart_init()
{
	txbyte = 0xFFFF;		//     -  
	rxbyte = 0x00;			//     -  
	txbitcount = 0x00;		//     -  (   )
	rxbitcount = 0x09;		//      - 9 (  )
	
	TXDDR |= (1 << TXD);		//       
	RXDDR &= ~(1 << RXD);		//       
	TXPORT |= (1 << TXD);		//     TXD
	RXPORT |= (1 << RXD);		//     RXD
	OCR0A = BAUD_DIV;		//    OCR0A    
	TIMSK0 |= (1 << OCIE0A);	//   TIM0_COMPA
	TCCR0A |= (1 << WGM01);		//   CTC ( TCNT0   OCR0A)
	TCCR0B |= T_DIV;		//        
	MCUCR |= (1 << ISC01);		//   INT0    
	GIMSK |= (1 << INT0);		//   INT0
	sei();				//   
}


UART-Initialisierungsfunktion. Keine Argumente, kein Rückgabewert. Initialisiert globale Variablen und Mikrocontroller-Register. Aus den Kommentaren im Code sollte alles klar sein.

Es ist also Zeit, ein einfaches main () in unsere Bibliothek zu schreiben und zu sehen, was in Bezug auf die Codemenge passiert ist, und die Leistung zu überprüfen.

Haupt c
#include "uart13.h"

int main(void)
{
	uint8_t b = 0;
	uart_init();
	while (1) 
	{
		if(uart_recieve(&b) >= 0)	//    ,    
			uart_send(b);		//   ,  
	}
	return (0);
}


Wir kompilieren:
Programmspeicherauslastung: 482 Byte 47,1% Vollständiger
Datenspeicherbedarf: 5 Byte 7,8% Vollauslastung

Nicht schlecht, wir haben sogar mehr als die Hälfte des Speichers des Mikrocontrollers auf Lager!
Einchecken in Proteus:

Die Simulation ist in Ordnung!


: , , delay.h , .

, avr-gcc: GitHub

Source: https://habr.com/ru/post/de386485/


All Articles