AVR ATmega programmer

I made my own programmer for AVR ATmega microcontroller that uses TX and RX lines to send/receive data to/from the microcontroller. This is different than in most designs where RTS/DTR control lines are used for this purpose.

 

3-bit and 4-bit miniframes

The idea... to encapsulate every single data bit into its own mini frame consisting of one start bit and one stop bit. Like this:

(the 3-bit miniframe)

The start bit must be high and the stop bit must be low. Therefore, we can make only two legal versions of a 3-bit miniframe: ‘high-low-low’ and ‘high-high-low’.

After a bit of thinking I concluded that it would be much easier to build the programmer if the data bit is actually repeated twice. Like this:

(the 4-bit miniframe)

Again, there are two legal versions: ‘high-high-high-low’ and ‘high-low-low-low’. (But also the ‘high-low-high-low’ may come good - explained latter.)

Immediately you see that the actual transfer rate is going to be 1/3 (3-bit miniframes) or even 1/4 (4-bit miniframes) of the serial port baud rate. At the time I was hoping to achieve some 460 kbaud from my serial port, but found later that it is limited to 115.2 kbaud. This severely restricted the download rate to 28.8kbps or 38.4kbps maximum. Still, I continued.

The picture below shows how miniframes are packed into one RS232 character.

Into one character we can pack up to three 3-bit miniframes (using 7,N,1 parameterization) or two 4-bit miniframes (using 6,N,1). Note that in both cases the start bit of the character is also the start bit of its first miniframe. And, the stop bit of the character is the stop bit of its last miniframe.

 

The basic circuit

The basic circuit that converts above-described miniframes into SPI bus data is shown on the picture below.

The circuit employs two 4001 CMOS chips (4xNOR gates). It is powered form the serial port (from the RTS line that is kept high all the time). For 115.2kbaud, I used: C1=100pF, R1=15k, C2=2.2nF, R2=15k.

At the heart of the circuit there is a monostable multivibrator (gates U3 and U4). This monostable generates clock signal (SCK) for the SPI bus. The monostable is triggered on the rising edge of every miniframe start bit. When it gets triggered, the SCK goes temporary low.

After some constant time (defined by the capacitor C2 and resistor R2), the monostable reverts and SCK goes up again. At that exact moment the data is clocked from the voltage level currently present on the MOSI line.

It is important to adjust the time constant of the monostable to match the baud rate used on the serial port. The monostable must revert itself at some point during data bit(s) transmission... The correct diagram is shown below (examples show 4-bit miniframes).

Incorrect examples are shown below (monostable time constant is either too long or too short):

The need to adjust the monostable time according to the serial port baud rate means that, if no configuring jumpers/DIP switches are anticipated, we are limited to only one serial port speed. (The AVR microcontroller can be configured to use very slow main clock frequency of 32.768kHz only. It then needs to be communicated at very slow pace. With such programmer optimized for high speeds, this won’t be possible because low-level portion of the SCK signal always has fixed duration. Even if we send single miniframe per character with pauses in between. Be aware of this problem.)

Another thing about this monostable is that it should be able to accept new trigger impulse shortly after reverting to its stable state. For example, if the monostable time constant is set to 2.5 bit times, then there is only 1.5 bit time pause until new trigger arrives (2.5 + 1.5 = 4-bit miniframe).

The diode D is used for this purpose - it prepares the monostable for new cycle by draining the C2 capacitor rapidly. However, as the diode D is already present inside 4001 chip, there is no need to put it as discrete component!

 

Reset condition - purpose of the U1/U2 bistable

What complicates things up is the fact that the SCK signal must be set to low during AVR microcontroller reset. This is a problem, because our monostable actually keeps SCK high when no data is present. The solution is to employ a bistable (gates U1 and U2)...

At some moment, preceding any data transmission, the DTR line must be lowered for several milliseconds. Two things will then happen:

- the AVR chip will receive non-reset signal briefly and will therefore start a new SPI session
- the bistable U1/U2 will be set high - this will lock the monostable U3/U4 in its low SCK position.

Even after DTR line goes high again, the bistable U1/U2 remains high and keeps SCK locked low until the very first bit of data is transmitted over the TX line. This first bit (the rising edge of the start bit) will reset the U1/U2 bistable, and the monostable will continue normal operation. See the diagram above.

 

Operation

So how is this programmer operated?

1.The RS232 serial port is opened by the programming software.
2. The RTS is pulled high. This will power up the circuit (wait several milliseconds to stabilize). As the DTR start low, the U1/U2 bistable is driven high, monostable is locked, and SCK is forced low.
3. The DTR is now pulled high. The AVR chip resets. (wait several milliseconds)
4. Data is prepared (miniframes formed) and sent as a stream through the TX line. The first bit that is sent resets the U1/U2 bistable, starting the first monostable cycle.
5. While data is transmitted, the return data can be simultaneously read on the RX line.
6. The RS232 port is closed.

However, as it might happen that the AVR microcontroller was already in reset condition and its SPI session state may be unknown, I suggest a bit different (more clumsy) opening sequence with steps 2 and 3 modified:

2. The RTS and DTR are pulled high simultaneously. This powers up the circuit and also resets the AVR microcontroller (if not reset already). Wait a few milliseconds.
3. The DTR is then pulled low for just a brief time, and then back high. This will arm our bistable (brings SCK to low) and the AVR microcontroller will be instructed to restart its SPI communication.

 

Data receiving

The data receiving is a more unclear activity... The U7 and U8 NOR gates are used to produce start and stop bits on the RX line. The start bit is taken from the TX line. The stop is taken from the SCK signal. The MISO line defines what will be in between.

But there is one important catch with this simple approach: the TX line must be kept low while receiving (otherwise the high TX would mask data from MISO line). It is therefore recommended that ‘high-low-low-low’ 4-bit miniframes are always sent while data is received. The high start bit will be reflected on the RX line, while three low bits will allow for data receiving. Fortunately enough, the AVR microcontroller doesn’t care what we might send while it is responding back.

The diagram below shows how TX, SCK and MISO lines combine to create fairly valid RS232 frame. In this example, the AVR chip sends three bits on its MISO line: low-high-low.

In the above example, 4-bit mini frames are used. Vertical arrows show data bits that are reliable for reading. The reading is correct: low-high-low.

By examining the example above, the following can be concluded:

- the start bit is guaranteed by the TX line. High level on the TX will produce high level on the RX.
- the stop bit is guaranteed by the SCK signal. The high level on the SCK will produce low level on the RX.
- while both, the TX and the SCK are low, the RX line reflects the state of MISO line
- to have at least one data bit per miniframe that reliably reflects the MISO line, the monostable time constant must be longer than 2 bit times!!!
- finally, it is now obvious that 4-bit miniframes are the only possible choice when it comes to reading. There are no reliable data bits in 3-bit miniframes.

Therefore, to read properly you should:

- use 4-bit miniframes
- set the monostable time constatant to 2.5 bit times (with tolerances, say, plus/minus 0.3 bit times)
- read only the first data bit in the miniframe as reliable one
- ignore any UART error reports that may come from unreliable second data bit.
- while reading, you should transmit an array of ‘high-low-low-low’ 4-bit miniframes over the TX line

When you finally receive characters through your RS232 port, they will have the following structure:

bit 0 - the reliable data bit of the first miniframe (should be used)
bit 1 - the unreliable data bit of the first miniframe (not to be used)
bit 2 - the stop bit of the first miniframe - always '1'
bit 3 - the start bit of the second miniframe - always '0'
bit 4 - the reliable data bit of the second miniframe (should be used)
bit 5 - the unreliable data bit of the second miniframe (not to be used)

As you noted, if it is a must to send ‘high-low-low-low’ on TX to receive on RX, then we cannot send any meaningful data simultaneously with receiving. As said already this is not even needed in AVR microcontroller case. But if needed, it actually can be done by sending ‘high-low-data-low’ miniframes! Therefore we can name those four bits in 4-bit miniframe as: the start bit, the receive data bit, the transmit data bit and the stop bit…

In theory, you could make successful readings even with 3-bit miniframes. To make this possible, you would have to adjust monostable time constant very precisely (and make it stable) to something as 1.75 bit times (plus minus 0.05 bit times). This almost cannot be done, especially at high baud rates.

 

Advanced notes

- The serial port on your PC may be limited to 115.2 kbaud. Even if it can go faster, the question is can you force Windows to use that higher speed. If you can, I suppose that about 500kbaud would be the upper limit.

- The C1 and R1 derive the TX signal - producing pikes at rising edges of every miniframe start bit. The pike should be shorter than the monostable time constant – therefore, roughly, R1C1 - There are 15k and 100k resistors between RS232 lines and NOR gates (U5 and U6). The resistance is not really important. These resistors are used to limit current during negative input signal (4001 leaks a lot in that case because of protective diodes inside).

- When programmer is unpowered, the resistance between U6 output and ground will not be very high. Therefore, the AVR microcontroller may be in reset as long as an unpowered programmer is connected to it. To avoid this, you can use lower-resistance (<47k, but >4.7k) pull-up resistor on microcontroller's reset pin.

- It seems that AVR microcontroller actually clocks the data few microseconds latter than the rising edge of the SCK signal. It may depend on its clock frequency.

- Some RS232 ports will give you less than +6V on their lines. At least 6V is needed for this programmer to work (except if you somehow improved the power supply section). Otherwise, you would have to connect external power supply.

 

Improvements - the power supply

The simple power supply, as shown on the basic circuit may not be good enough. Especially if you have plans to power the AVR microcontroller from the programmer. We can use DTR line as secondary power source, because it is set high 99.9% of the time.

In addition we can use some real +5V regulator. This is better than zenner diode and a resistor because RS232 line voltages may vary from 15VDC to 5VDC and the internal resistance can also vary a lot.

The diode D2 can be absent if the RTS line is always high. The +Vhigh may be needed in some cases (read about RX driver).

 

Improvements – the 3-bit / 4-bit discriminator

As explained before, only 4-bit miniframes can be used when reading. However, 3-bit miniframes give 33% higher data transmission rate.

The programmer can be modified to use 4-bit miniframes when reading and 3-bit miniframes when writing. The programming software should be able to somehow reconfigure monostable time constant from cca 2.5 bit times to cca 1.5 bit times. This can be done using the RTS line as suggested below.

First, the power supply must now be drawn from both, the RTS and the DTR because the RTS doesn't have to be high at all times any more (but at least one of them, RTS or DTR, will be high).

The R3 resistor is now connected to the RTS line (indirectly). When the RTS line is high, the R3 resistor is disconnected by reversely polarized diode D3 and the circuit behaves as before (the monostable time constant is R2C2).

When the RTS is lowered, the R3 gets connected to 0V (provided by D4) and actually becomes parallel to the R2. This shortens monostable time constant. A good choice for R3 resistance would be roughly twice as much as R2 (for example if R2 is 15k, then 33k for R3 sounds like a right value). The circuit will now be able to transfer 3-bit miniframes.

Important... you better keep RTS high until the very first bit is transmitted (lower RTS only after the communication already started - I suggest that you send at least 'programming enable' command with 4-bit miniframes, and then switch to 3-bit miniframes). This way the monostable will be securely locked in its low position and, more importantly, the first monostable period will not be affected.

Is this added complexity worth 33% download rate increase? Your call. If not, you may decide to use the RTS to somehow discriminate between two different baud rates, a slow one and a fast one.

One more thing, the R4 resistor is here only to limit current through D4 when the RTS is low. It should have lower resistance value than R3.

 

Improvements - the RX driver

Note that the base circuit actually drives RX line with TTL signal level (0V..+5V)!!! Strange, but it works on many computers.

If driven by-the-book, the RX should receive at least +3 when high and -3 when low. This can be done easier than it looks. Three suggestions follow:

The first suggestion uses an optocoupler. We can use +Vhigh generated by RST/DTR for the high voltage. And we can also get the -Vhigh from the TX line (provided through 15k resistor). This works because, as said before, while reading the TX line is kept low. Therefore, the negative voltage will be present on the TX line when needed.

The second suggestion is similar to the first one but uses no optocoupler. Instead a PNP transistor is used. In this solution we cannot provide +Vhigh, but we can still provide -Vhigh. The +Vhigh is replaced by +5V and this should be enough. Note that during high TX level (the start bit), the transistor may become inversely active. Note also that whenever TX is high, also the emiter of the transistor is driven high (+5V) from U8.

The third suggestion uses optocoupler again, but is simplified because two NOR gates (U7 and U8) are removed. In this circuit, the power to drive optocopler's LED comes from the AVR microcontroller itself.

 

A simplified version - can it get any simpler?

Maybe. If you don't like CMOS logic, you may find the following circuit simpler (only one 4001 chip is used here).

In the circuit above, I use 5.1 zenners to convert RS232 signals to TTL levels. Note that the above programmer is in a sense 'incompatible' with the earlier one because the DTR line should be inversely handled. In addition, on the TX line we should invert data bits.

If you know that your serial port RTS line has high enough internal resistance, you could remove the 470 ohm resistor.

If you already have RS232 to TTL level convertor, it can get much simpler (no zenners, no some resistors), but the question is can your TTL convertor provide enough current on RTS line to power the circuit.

Even simpler? Well we could remove the receiver part (the optocoupler and two resistors). We all know that data receiving is for pussies. Real men only send data.

 

Without using DTR/RTS control lines

Under some assumptions, it is possible to make equally simple programmer that only uses TX and RX lines. No DTR and RTS lines are used.

Note that I didn't actually make this one (I have no 4098 chip on hand), but I believe that it might work.

The disadvantage is that only 4-bit miniframes can be used!!! Also, you have to find 4098 or similar (4528, 4538, 4938...) CMOS chip – 2x retriggerable monostable.

To work properly, R1 and C1 must produce long times (about 1 sec), while R2 and C2 must produce short times (2.5 bit times, plus minus 0.3 bit times).

In steady state (no data is transmitted for longer than 1 sec), the first monostable (U1) keeps AVR chip in non-reset condition. At the same time it keeps SCK low (by blocking SCK signal on U4 gate).

To start transmission, an array of 0x15 characters must be transmitted for several miliseconds (RS232 parameters set to 6,N,1). This way the TX line will be excited with fast-changing square signal. As both monostables are retriggerable, both will be active as long as 0x15 characters are transmitted (even the U2 - because its time is set to more than 2 bit times). This means that RESET line will go low (AVR chip resets) while SCK is still kept low because U2 output is kept active now.

Once the AVR chip is ready, we immediately, without any pause, start transmitting our 4-bit miniframes. This will generate SCK signal. Care must be taken that there are no pauses longer than 1 sec in our transmission as the first monostable will then bring up the reset line.

Obviously, during preparation phase (while 0x15 characters are transmissted) we must make sure that there is absolutely no pause between characters. We must feed transmitter well. At higher baud rates, this might be a problem on some computers, especially if you employ USB/RS232 converter or similar. If anybody has better idea how to start transmission, let me know.

Another problem is a possible pike that may be generated on U4 at the moment transmission begins (at the same time one input to this gate goes low, while the other goes high).

 

Demo software

For the demonstration, I made a simple(st) software. You cannot write to EEPROM or change fuses with this demo.

Click to download this Demo AVR programmer (167.8kB). For any question use: danijel.gorupec( at )gmail.com

 

Danijel Gorupec, 2010.
(Download the Math-o-mir, the free mathematical notepad!)