In this question:
Why usb to serial port converter can’t program avr microcontroller?
Cornelius claims:
"DTR and RTS are the only pins of the serial port that can be bitbanged."
But is that claim true? Is there some problem bitbanging the TxD pin that I am not aware of?
How would I bitbang TxD? Simple:
First: Make sure that there are no characters queued for transfer on TxD.
Then: In Delphi, I would do it like this:
var
Com1Port : TBlockSerial; // TBlockSerial comes from SynaSer.
// SynaSer is part of Synapse communication library:
// http://www.ararat.cz/synapse/doku.php/start
procedure TfrmMyCom1Test.SetTxD(const Value: Boolean);
const
EscCommands : Array[Boolean] of DWORD = (SETBREAK, CLRBREAK);
begin
if NOT Com1Port.InstanceActive
then Exit;
// Break:
if EscapeCommFunction(Com1Port.Handle, EscCommands[Value])
then FTxD := Value;
// Value = False -> SETBREAK ; Sets TxD to Logical 0.
// Value = True -> CLRBREAK ; Sets TxD to Logical 1.
end;
When a call is made:
SetTxD(False); // This makes TxD a Logical 0.
SetTxD(True); // This makes TxD a Logical 1.
So to me it seems it is possible to bitbang TxD also (on COM1, for example) as long as one takes care that there are no queued characters queued for transfer on TxD.
Actually, there may be another way to bitbang TxD:
If you make your circuit (AVR serial programmer for PC serial port) so, that a logical 1 (Mark) on TxD translates to 0 V on the AVR SCK pin (on the ATmega328 this is PB5), and a logical 0 (Space) on TxD translates to about 5 V on SCK, that makes a 2nd way of bitbanging TxD possible, but adds an extra programming difficulty: Either one must find a way to know, when transmitting a byte/character is ready (HOW?), or alternatively one must wait long enough so that the delay is enough for transmitting one byte to always be finished, which may waste some time.
This way of bitbanging simply sends 1 to 5 bits to the AVR (either all bits ZERO or all bits ONE), and regardless of sending just 1 bit or 5 bits, one batch of send always uses the time of 10 line bits to send.
Like, at 115200 bits per second, each bit consumes 8.681 µs, which makes 10 line bits to consume 86.81 µs.
I thought the idea of my 2nd way of bitbanging would be obvious - but maybe not, so let me explain:
The 2nd way allows to bitbang 1 to 5 bits to the AVR (but either all bits sent must be ZERO or all bits sent must be ONE).
First: set the MOSI pin (could use either DTR or RTS for this, and use the other one of these to control the RESET pin of AVR) to match the bit value you want to send.
Next: send one of the following bytes (Hexadecimal values):
FF, FD, F5, D5, 55
regarding if you want to send 1 (0FFh) to 5 (55h) copies of the same value bit.
Since the start bit is 0 - translates to 5V at SCK and the stop bit is 1 - translates to 0V at SCK.
and each 1 data bit translates to 0V at SCK, so the data byte must contain one less 0 bits (which translate to 5V at SCK) as how many copies of the same bit you want to send, but there must not be two (or more) consequent 0 bits in the byte, but each two 0 bits must be separated by a "1" bit.
The ATMega328 datasheet: https://ww1.microchip.com/downloads/aemDocuments/documents/MCU08/ProductDocuments/DataSheets/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061B.pdf
The general idea here is to use the built-in COM1 port of a windows PC to use bitbang-mode of the COM1 port to do serial programming of the ATMEGA328 microcontroller.
Of course, the same idea would work to program any microcontroller that accepts SPI as the mode of serial programming.