Some AVR devices, like attiny85 (on Adafruit Trinket, for example), do not have hardware UART. However, it would be still nice to have serial output from them.
I wrote a simple Soft_Serial package, which can use any GPIO pin as TX UART pin.
package Soft_Serial is Serial_Pin : Boolean renames AVR.MCU.PORTB_Bits (2); Serial_Pin_DD : Boolean renames AVR.MCU.DDRB_Bits (2); procedure Init; procedure Write_Byte(B : Interfaces.Unsigned_8); procedure Write (S : AVR.Strings.AVR_String); end Soft_Serial;
The interesting bits in the implementation are:
MCU_FREQ : constant := 16_000_000; procedure Wait_A_Little is new AVR.Wait.Generic_Wait_USecs (Crystal_Hertz => MCU_FREQ, Micro_Seconds => 102); procedure Write_Byte(B : Interfaces.Unsigned_8) is Data : Interfaces.Unsigned_8 := B; Mask : Interfaces.Unsigned_8 := 1; begin Serial_Pin := False; Wait_A_Little; for I in Interfaces.Unsigned_8 range 0 .. 7 loop if (Data and Mask) = Mask then Serial_Pin := True; else Serial_Pin := False; end if; Wait_A_Little; Mask := Mask * 2; end loop; Serial_Pin := True; Wait_A_Little; end Write_Byte;
Basically, the Write_Byte procedure will put the GPIO pin up or down with certain intervals.
The Wait_A_Little procedure has magic number 102, which gives us UART speed 9600bps. (9600bps means one bit about every 104 microseconds, the difference comes from the inaccurate internal oscillator.)
The procedure uses 8N1 format, meaning that the start bit (pin low) is sent first, then the data byte as 8 bits, and finally one stop bit (pin high).