Perangkat lunak dupleks penuh UART untuk ATtiny13

Halo untuk semua pengguna Geektimes! Suatu hari, sebagai hasil dari layanan saya, saya perlu mengimplementasikan perangkat lunak UART pada mikrokontroler ATtiny13 yang populer. Googling, saya menemukan sejumlah besar artikel tentang topik ini, banyak dari mereka diposting di sini:


Dan ada sumber daya lain:


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

— , , .

Jadi, mari kita mulai. Saya hanya akan memposting file header uart13.h dalam bentuk yang berisi komentar dalam kode, semuanya sederhana di sana.

File header Uart13.h
/*    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_ */



Tapi saya akan memecah deskripsi kode implementasi perpustakaan menjadi beberapa bagian agar tidak mengubah artikel menjadi satu spoiler besar dengan kode.

Ganggu 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 , , .

, 0xFFFF , , TXD . , : 1 , 8 1 , 10 (0x0A), txbyte . . void uart_send(uint8_t tb).

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
}


Mengganggu INT0. Ini bekerja pada ujung pulsa di input INT0, digunakan untuk melacak awal penerimaan informasi. Atur penghitung bit ke 9, atur ulang isi variabel rxbyte. Menetapkan nilai untuk register OCR0B, yang menentukan frekuensi TIM0_COMPB interupsi, harus dalam waktu di tengah bit yang diterima. Setelah itu, interupsi TIM0_COMPB diaktifkan, dan interupsi INT0 dinonaktifkan.

Berikut ini adalah fungsi pengguna untuk bekerja dengan UART.

Fungsi 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();				//   
}


Fungsi inisialisasi UART. Tidak ada argumen, tidak ada nilai balik. Menginisialisasi variabel global dan register mikrokontroler. Dari komentar dalam kode semuanya harus jelas.

Jadi, inilah saatnya untuk menulis beberapa main sederhana () menggunakan perpustakaan kami dan melihat apa yang terjadi dalam hal jumlah kode dan memeriksa kinerjanya.

main.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);
}


Kami mengkompilasi:
Penggunaan Memori Program: 482 byte 47,1%
Data Penuh Penggunaan Memori: 5 byte 7,8% Penuh

Tidak buruk, kami memiliki lebih dari setengah memori mikrokontroler dalam persediaan!
Memeriksa dalam Proteus:

Simulasi itu OK!


Intinya: implementasi ternyata cukup bermanfaat, data ditransmisikan dan diterima secara independen, perpustakaan delay.h tidak digunakan sama sekali, dan lebih dari setengah memori mikrokontroler dibiarkan sebagai cadangan.

Saya lampirkan kode sumber perpustakaan, mereka dikompilasi dalam avr-gcc: Sumber di GitHub

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


All Articles