Path: Home => AVR overview => Applications => Power supply scanner   Diese Seite in Deutsch: Flag DE Logo
Power scanner with ATmega Applications of
AVR single chip controllers AT90S, ATtiny, ATmega and ATxmega
Power supply load scanner with RS232
Note that this page is preliminary, changes are still made without notice. It is incomplete and additions will be made in the near future.

A power supply current load scanner with RS232 interface

You wanna know how much voltage your power supply has at different load currents, at which load your power supply drops down and gives up? No problem with this device: an AVR increases the current that a power transistor pulls from your supply, measures the rising current and the voltage of your power supply. Such a scan lasts roughly five minutes, then you know exactly the current at which your power supply gives up.

At maximum your power supply provides up to 35 Volts at a maximum of 3.3 A current, so you can test even larger power supplies. The maximum current can be switched down by a rotary switch, so that smaller power supplies can also be scanned.

And the nice thing on this is the RS232 interface, that transmits the results and can be collected from there (both versions with the ATmega48 or the ATmega324PA) or can be viewed over an attached LCD (ATmega324PA only).

The RS232 interface also receives orders: displayed voltages and currents can be adjusted (the multiplication factors are stored in EEPROM and are loaded on every start-up, therefore the adjustment has to be made only once), the scan can be started, held at the current stage for a long time check, and ended, all on command.

Like always on this webpage, the following is provided for free:

Top Schematics Mounting Operation Software Functioning

1 Schematics of the power scanner

The power scanner comes in two versions:
  1. The version with the ATmega48 has the RS232 interface only, so can only work with those commands.
  2. The version with the ATmega324PA additionally has a four-line LCD and two switches, so can either work manually and stand-alone or with commands over the RS232 interface.

1.1 Schematic of the power scanner with ATmega48

Powerscanner with RS232 and ATmega48 The schematic with the ATmega48 consists of the following parts:
  1. the NPN-Power-Darlington-Transistor TIP 141, its base is driven by a one-, two- or three-stage RC filter from the 8/9/10-bit PWM generator of TC1, which via the OC1B-pin can adjust a pulswidth-controlled voltage between zero and 5V, other Darlingtons can also be used as far as they have enough thermal power dissipation and are cooled accordingly,
  2. for the case that the rotary switch is between two contacts or is disconnected, the two power diodes on the emitter protect the ADC input from overvoltage,
  3. an AD-converter with an internal reference voltage of 1.1 Volt, which is externally cleaned with a capacitor on the AREF pin, and
    1. measures on ADC0 the voltage of the power supply, which is divided down to the reference voltage level with two resistors,
    2. measures on ADC1 the emitter voltage of the Darlington (voltage range 0.0 to 1.1 V, 10-bit-ADC),
    3. measures on ADC2 the base voltage of the Darlingtons via a resistor divider and controls the RC- filter,
  4. the serial interface with the MAX232, which transmits and receives via the TXD- and RXD-pins and communicates over the two RTS- and CTS-lines,
  5. the crystal on the two controller pins, which for an exact clocking of the serial interface should be of 2.457600 MHz (or an equivalent, see below for the use of other crystals),
  6. the rotary switch below, that selects from six different emitter resistors, which control the measuring range and resolution, the second level of the switch reports the active position to the controller via PCINT pins,
  7. the ISP6 interface, over which the flash can be programmed and the internal EEPROM receives its initial start values, and
  8. two power supply plugs for the 5V supply of the controller and the serial interface.
Because all voltages and currents are calculated with adjustable constants, all voltage dividers and the emitter resistors can be equipped with 5% or even 10% tolerance. Because the constants can be adjusted and stored in EEPROM, an adjustment is needed only once.

Note that the two diodes protect the ADC1 input pin imperfectly. If no resistor is on (e. g. during switching) and if the base voltage is larger than 2.8 V, the over-current can destroy the Darlington, if the power supply can deliver large currents. As the RC filter output will drop down fast to 2.8 Volt in that case, the over-current lasts for a very short period. Usually one would use 4.7V Zener diodes for that, but the 10W type of these components are currently not manufactured any more. So, simply just do not switch the rotary switch under heavy load.

Who wants to build the hardware on a breadboard, finds test software for various components here, that can be used for quick checks.

1.2 Schematic of the power scanner with ATmega324PA

Power scanner with RS232, LCD and ATmega324 The ATmega324PA version has all that the ATmega48 version has, but on different pins. Additionally this version has
  1. a four-line-LCD, which is controlled via a bi-directional 8-bit data bus and with the three control lines LCD-RS, LCD-R/W and LCD-EN, and with a controlled backgound light over LCD-BL from the PWM channel TC2,
  2. additional potentiometers
    1. on ADC3 for adjusting the background light intensity of the LCD,
    2. on ADC4 for selecting the menue entry, and
    3. on ADC5 for adjusting numbers,
  3. two switches S1 (Menu) and S2 (Ok), over which the calculation constants for voltages and currents and the PWM's actual value can be adjusted, and over which the scan can be started, held, continued (up- or downwards) and ended.
The serial communication lines for handshaking Request-To-Send (RTS) and Clear-To-Send (CTS) are in that case tied to the SCK- and MOSI-pins of the ISP6 connector. Both are decoupled by resistors for not interfering with the ISP communication during programming.

For this version specific test programs for the hardware components are also available.

1.3 Schematic of the serial interface

Schematic of the serial interface The serial interface is identical for both versions, only the four controller pins used are different.

The MAX232 generates the +/-12V for the interface with four electrolytic capacitors and has two inverting TTL-to-RS232-drivers as well as two inverting RS232-to-TTL-driver stages on board. With that is possible to not only communicate via the TXD- and RXD-pins but also to use RTS and CTS for the RS232 protocol.

The standard-DB9-female plug is connected via a 10-pole flat cable and plug with the controller board.

1.4 Schematics of the RC filter of the power scanner

Single stage RC filter for the power scanner Three-stage RC filter for the power scanner This here shows a single- and a triple-stage RC filter for the PWM signal, which drives the base of the Darlington transistor with DC.

For the properties of both filters as well as a two- and three-stage RC filter see the chapter below.

1.5 The emitter resistors of the power scanner

The emitter resistors for the power scanner The emitter resistors of the Darlington-transistor determine the current range that are accessible via the PWM. This reaches from 3.3 A down to 50 mA full range-

The table shows the resistor values and the ranges that can be accessed with those. The thermal power of the resistors are also given.

A note on the mechanical rotary switch: this is necessary to switch the high currents. With smaller switches, such as dip switches, such high currents cannot be handled. The two levels of the switch report to the controller, which of the resistors is active, and so the correct multiplicator for the current calculation can be automatically applied.

As already mentioned, the resistors can have 5, 10 or even 20% tolerance: you can adjust all of these. But make sure that the resistors have enough thermal power dissipation, so that rising temperatures do not influence your current calculation.
Top Schematics Mounting Operation Software Functioning

2 Mounting the power scanner

2.1 Parts list for the ATmega48 version

Parts list of the M48 version Here are all the parts for the ATmega48 version. Listed are all 38 parts, which include a large casing and all plugs.

Parts that cause more than 5% of the total price of approximately 45€ are red on yellow background.

The plastic case is relatively large, because the heat sink would be placed inside. If you isolate the Darlington from the heat sink, you can mount those outside the plastic case and you can use a smaller one. If inside, do not forget to screw enough holes in the casing for heat power dissipation.

2.2 Parts list for the ATmega324 version

Parts list of the M324 version Here is a list of the 48 component types of the ATmega324PA version.

Some of the necessary items can be cheaper, if other sources can be used, e.g. for the LCD.

2.3 ...

TBD further

Top Schematics Mounting Operation Software Functioning

3 Operating the power scanner

3.1 Selecting the current range

Selecting the current range is done with the rotary switch, which changes the emitter resistor of the Darlington. During switching, two extreme situations have to be anticipated:
  1. For a short time the two contacts in between the previous and the next can switch two resistors at once. The mimimum switches the 0.33Ω and the 1Ω on, resulting in a resistor of 0.25Ω.
  2. For a short time, none of the two contacts is active, the emitter has no resistor and any current via the CE of the Darlington flows through the two ADC1 protection diodes BY100.
While the first case is not problematic, because it results in a slightly higher CE current of less than the factor two, the second case can be problematic. The following conditions may be problematic: Current and power with PWM at full voltage If both conditions occur, and in case that the PWM is at its maximum, these are the currents through and the power of the Darlington (assumed the voltage of the power supply is at 30 V). One can see that the over-current (blue curve, left scale) drops down very fast, as the third capacitor of the filter unloads within 10 ms time. So, assumed that the power supply delivers it, the Darlington would die from this current. If the power supply does not deliver that, the current after the first 10 ms is fine for both Darlington types shown here.

Not so with the power of the Darlington (red curves, right scale: The power remains above the limit of both types. Only if either the voltage of the power supply is smaller (e. g. 24 V) or the PWM is at less than its maximum, the power limit of the Darlington is respected.

For a brief analysis: the voltage of the PWM, the three resistors of the RC filter (150Ω each), the four VBE, the forward amplification of the Darlington and the voltage of the power supply determine the maximum power:
Pmax = (VPWM - 4 * VBE) / RRC / 3 * hFE * VPS

So, if you use the scanner with large power supply units: switch the rotary switch only at no, at low or at medium PWM ratios or disconnect the power supply prior to switching to protect your Darlington.

3.2 Operation via RS232 interface

Operating both versions of the scanner can be done over the serial interface, the commands to be used are listed in the respective column of the table below. All commands are executed after reception of a carriage return or line feed character.

Additionally, the ATmega324PA version can be operated with the two added potentiometers and the keys, so operation can be done without the serial interface. Operating via switches can be done as follows:
  1. The switch 1 on INT0 (black) changes to the next input mode.
  2. The switch 2 on INT1 (red), while key 1 is off, executes the displayed menu entry.
  3. The switch 2 on INT1 (red), while key 1 is pressed, changes to the previous input mode.
Available are the following adjustment modes:

#Switch 1LCD outputSwitch 2 executesRS232 commandNumber inputDefault numbers
0Voltage on
power supply output
increase↑Voltageincrease voltage displayV+, V++V=numberV=3497
1decrease↓Voltagedecrease voltage displayV-, V--
2Voltage on
the transistor base
increase↑Baseinrease base voltage displayB+, B++B=numberB=5009
3decrease↓Basedecrease base voltage displayB-, B--
4Current displayincrease↑Currentincrease current displayI+, I++I=numberI=3333/1100/5000/
5decrease↓Currentdecrease current displayI-, I--
6Upper scan limitincrease↑Limitincrease current maximumL+, L++L=numberL=Imax/2
7decrease↓Limitdecrease current maximumL-, L--
8PWM resolutionincrease↑PwmResincrease resolution of the PWMR+, R++R=8/9/10R=10
9decrease↓PwmResdecrease resolution of the PWMR-, R--
10PWM valueincrease↑PwmValincrease PWM valueP+, P++P=numberP=0
11decrease↓PwmValdecrease PWM valueP-, P--
12ScanStart→Scanstart the scanS--
13hold↔Holdkeep the scan on holdH
14continue up↗Scan-upcontinue the scan upwardsU
15continue dwn↘Scan-dwncontinue the scan downwardsD
16end←Stopend the scanE or Z

Note that lower case characters over the serial interface are converted to upper case.

Top Schematics Mounting Operation Software Functioning

4 Software for the Powerscanner

  1. Assembler source code downloads
  2. Entries in the sourcecode
  3. Fuse settings
  4. Test programs for the hardware

4.1 Download of source codes

The software is still under devellopment and not yet finalized and not ready for publication.

For assembling the ATmega324PA version the LCD routines here are needed, place this include file into your project path. Please consult the section here on adjustments prior to assembling.

Both versions produce an EEPROM standard file, so when burning the assembler output do not forget to burn the EEPROM content as well. Without the EEPROM content the device does not work correct.

Please also adjust the fuses like shown here, your serial interface and your LCD will not work without that fuse change.

Top Schematics Mounting Operation Software Functioning

4.2 Adjustments prior to assembling

Make sure that all debug switches are set to zero, otherwise the software will not work properly.

In the section "Adjustable constants" of the source code the following entries can be made:
Top Schematics Mounting Operation Software Functioning

4.3 Fuse changes

ATmega48 TBD

Original fuses in ATmega324PA Changed fuses for ATmega324PA To the left you see the orginal fuses in an ATmega324PA, to the right the changed fuses in this device.

To be changed are the JTAGEN fuse and the CKDIV8 fuse. The external crystal has also to be activated.

Top Schematics Mounting Operation Software Functioning

4.4 Hardware test programs

For the stepwise mounting of the device in the following row
  1. crystal and ISP6 connection,
  2. RC filter and Darlington-transistor driver,
  3. serial interface, and
  4. in the ATmega324PA version: the LCD,
I have written the following short programs to test the hardware.
Hardware to testATmega48ATmega324PA
PWM and RC filter m48_tc1.asm, u.c. m324pa_tc1.asm
Serial interface m48_serial.asm, u.c. m324pa_serial.asm, u.c.
LCD n.n. m324pa_lcd.asm, u.c.
u.c. = under construction n.n. = not necessary

The following chapters describe the test programs in detail.

4.4.1 Test of the RC filter

To check the RC filter, the software m48_tc1.asm and
m324pa_tc1.asm have been written. The software does the following:
  1. It switches the OC1B pin to be output.
  2. It initiates the timer TC1 in 10-, 9- or 8-bit-mode as PWM, with two selectable PWM puls widths in percent, and
  3. it switches after a selectable pause time in seconds between both pulse widthes.
  4. .
All parameters can be adjusted in the source code. By default a 10-bit-PWM switches every five seconds between 50 and 51% wide pulses.

You can use the switching for determining the speed by which changes of the RC come into effect. In the final device, you can also determine if a single stage RC is sufficient or better a double- or triple-stage RC would be better. Who wants a fixed pulse width, sets both to the same value in percent.

The PWM signal in 10 bit mode In 10-bit-mode the PWM frequency are crystal stable and clean 1,200 Hz. Already on the first capacitor the RC filter effect is astoundingly high: R*C = 150 * 100 / 1000000 = 0,015; 1 / 1200 = 0,00083, a filter factor of 18. On the third capacitor the PWM ripple can neither be seen on a scope with the highest resolution nor in an AC multimeter. The switching between both pulse widthes is so fast that a slow running oscilloscope can not detect the changes.

4.4.2 Test of the serial interface

The test programs m48_serial.asm and m324pa_serial.asm
  1. initiate the serial interface with the baud rate that can configured on top (default is 19k2),
  2. transmit in polling-mode 40 times the ASCII character "U" to synchronize the serial receiver, and
  3. echoes in interrupt mode all received characters, error in reception are echoed as "F", "O" or "P".
In an ATmega324PA the received characters are also outputted on port C, the pins PB5 (RXC0), PB6 (TXC0) and PB7 (UDRE0) reflect the current status of the UART if you attach a LED and a resistor to the operating voltage. In an ATmega48 the received characters are not outputted, but the pins PB5 (RXC), PB4 (TXC) and PB3 (UDRE) reflect the status bits of the UART. Please be aware that at high baud rates very short blinking pulses cannot be detected reliably. Who wants to see status bits can switch down the baud rate to 45 or 50 baud (at 2.457600 MHz the baud rate can go down to a minimum of 38 Baud).

4.4.3 Testing the LCD

The test program m324pa_lcd.asm needs the include file to test the LCD. In the source code the parameters of the LCD and the port connections are handed over to the include. An entry message is displayed, it is waited for five seconds and a second form is displayed.

The LCD is connected to the pins that can be seen in the schematic, it uses Write-Read-access via the data bus.

Top Schematics Mounting Operation Software Functioning

5 Functioning of hard- and software

  1. Functioning of the current adjustment
  2. Calculation of voltages and currents
  3. Functioning of the serial interface
    1. Clocking of the serial interface
    2. Ring buffer organization
    3. Ring buffer input
    4. Ring buffer output
  4. Functioning of the LCD

5.1 Functioning of the current adjustment

PWM ripple from the RC filters The OC1B output produces a pulse-width modulated signal that is fed into the RC filter, which can be single-, double- or triple-staged. The output of the filter drives the base of the Darlington transistor. At high currents of 100 mA and higher, the current through the base reduces the voltage of the RC filter output slightly, so a triple-stage RC needs a higher pulse width to drive the same emitter current like a single- or double-stage RC.

At a clock frequency of 2.457600 MHz the PWM frequencies are In order to achieve a fast following speed at a mean capacitor value, but at a large filter factor, I chose a resistor of 150Ω only. For a single-stage RC, I chose a C of 1,000µF, for the double-stage two 220µF and for the three-stage filter three times 100µF. This selection proved to be rational and achieves good filter results at all those frequencies.

For the single-stage (VC1-S) a PWM ripple of 2.9 mV results, which corresponds with a resolution of 0.13 mA in the highest current stage. The two-stage version (VC2-D) the ripple is 90 µV only, the triple-stage 28.3 µV only. The two- and three-stage version are sufficient, at 8 bit PWM resolution the single-RC combination can be used (at a higher PWM frequency).

Filter answer to changing Compare value This here is the filter answer to a change of the Compare match value by eight units smaller for the 10-bit PWM. The compare match change triggles within 500 milli-seconds through the filter. Because during a scan those changes are by one unit only, a delay time of 0.5 seconds is sufficient.

Emitter current at changing Compare values This is the move of the emitter current after changing the PWM value, at 4.7Ω emitter resistor. The currect can be adjusted in small portions. After half a second the change is done.

The slightly different current values between the three filter versions demonstrate the influence of the base current losses in the filter stages: even though the base current at 50 mA emitter current is rather small (50 µA at hFE = 1.000), the currents on the emitter are different. The base current cannot be ignored when calculating the RC filters. And definitely not at a RE of 0,33Ω.

The low value for R in the RC network (R = 150Ω) has as consequence that the AVR's OC1B PWM output sees high currents of around 20 mA. The voltage losses at those currents in High- and Low-direction have also to be considered in calculating the RC values.

Those who want to calculate their own filter network find the LibreOffice Calc file and therein the sheet "With base current" here.

Top Schematics Mounting Operation Software Functioning

5.2 Calculation of voltages and currents

5.2.1 Measuring voltages and currents

All voltage and current measurements are performed by the ADC at an internal reference voltage of 1.1 Volt. To not being stick to a single measurement only, which could be faulty, 64 measurements are made in a row and are added in a 16-bit register pair. Why 64? Now, that yields a 16-bit value, if 10 bits are received with each measurement: 210 * 64 = 216, and dividing by 64 can be easily integrated into the multiplication factor (see below).

The ADC sum in register pair R13:R12 This here is the ADC sum for 64 measurments of 1023, which is the largest sum the ADC can deliver. As can been this is not simply 0xFFFF, but slightly below.

Five different measurement types are to be made:
  1. the voltage of the output of our power supply under test is an interesting thing to know. As the output voltage can be as high as more than 30 V, we have to divide that voltage down to 1.1 V for the ADC input. For that a resistor divider of 390Ω and 12kΩ does the necessary downsizing. This divides the input voltage by the (12.000 + 390) / 390-fold = 31.769-fold. With that 31.769 * 1.1 = 34.946 V can be measured in full range (all measurements yield 1,023, sum of 64 measurements = 65,472). As 34.946  are measured with a 10-bit resolution, a resolution of 34.946 / 1,024 = 34 mV is to be expected. So it makes snese display the voltage as 34.95 Volts, not as 34.946. To avoid the use of floating point math, we multiply this voltage by 100 and insert the decimal point in between digit 3 and 2 of the decimal. To display this ADC sum result of 65,472 as 3495, we would have to multiply it with 0.053376. In order to - again - avoid floating point math, we multiply by the 65,536-fold * 0.053376 = 3.498-fold and skip the lower 16 bits of the result (respectively use it for rounding). The 16 bit result is then converted to decimal format (which in case of 65,472 yields 229,021,056), skip the lower 16 bits, which yields 3,494.58398, round the 0.58398 up, get 3,495. After conversion to decimal we insert the dot, and receive the voltage as 34.95 as a decimal string,
  2. similarly we divide the base voltage of the Darlington with a 11kΩ / 39kΩ resistor divider down to 1.1 V. This corresponds to a divider ratio of (11 + 39) / 11 = 4.545. Again the highest ADC sum would be 65,472 at a base voltage of 1.1 * 4.545 = 4.9995 V. To display this as 4.995V, we need to multiply it by 4.995 / 65.472 = 0.076292 or the 65,536-fold of 5,000. In that case the decimal dot is between digit 4 and 3 of the decimal string.
  3. the emitter current through the selected emitter resistor of the Darlington shuould result in not more than 1.1 V, So IE * RE = VE <= 1.1. So IE = VE / RE. With RE = 0.33Ω a current of 3.333 A, with RE a current of 0.05 A at 1.1 V (minus one digit = 1.0989 V) can be measured. Therefore an ADC sum of 65,472 corresponds with 3.330 A, displayed as 3330 plus the decimal dot on the correct location. That requires a multiplier of 3,333. The same for the 22Ω, where the displayed result should be 49.95 (in mA), for which the multiplication factor is 5,000. All other resistors have their specific factors for the multiplication and their specific position for the insertion of the decimal dot.
  4. the first potentiometer is on ADC3 and regulates the light intensity of the LCD's backlight, for this the MSB of the ADC's sum is simply written to TC0's compare match A, which is in true (uninverted) PWM mode and switches OC0A on and off,
  5. the second potentiometer is on ADC4 and selects from the 16 different menu items to be displayed and activated, for the MSB is multiplied by 16 and the MSB of the result is written to rMenu, the selected menue is then displayed on line 1 of the LCD,
  6. the third potentiometer is attached to ADC5, it calculates a number for the active menu entry, the number is depending from the entry: rMenu = 0 calculates a constant for the power-supply's voltage display so that the multiplication constant can be varied over a range of +/-10% around the default value, the resulting voltage is displayed on the LCD's voltage line 2.

5.2.2 The parameters for converting voltages and currents

To convert the ADC sum to voltages and currents we need two parameters:
  1. a 16-bit multiplicator, by which the ADC sum has to be multiplied, and
  2. the position, at which the decimal separator has to be placed.
As the multiplicator can be changed by the user (to increase or decrease the displayed voltages and currents), both tables are located in SRAM, and are read from the EEPROM at start-up. The default EEPROM values are as follows:

; Tables written by powersupply_scanner.ods
EepMultTable: ; Multipliers default
  .dw 3497,5009,3333,1100,5000,2340,1341,5000
EepLimit: ; The current limits in mA
  .dw 1667,550,250,117,67,25
  .dw 10
  .dw 0
EepDecSepPos: ; Decimal separator positions
  .db 2,3,3,3,1,1,1,2

These values are copied to SRAM:

; Values to and from EEPROM
sMultTable: ; Multiplier values
  .byte 2*8 ; default: 3497,5009,3333,1100,5000,2340,1341,5000
sLimit: ; The current limits in mA
  .byte 2*6 ; default: 1667,550,250,117,67,25
  .byte 2 ; PWM resolution, 8/9/10
  .byte 2 ; Current PWM value
sDecSepPos: ; Decimal separator positions
  .byte 8 ; default: 3,4,4,4,2,2,2,3

The two relevant tables here are sMultTable and sDecSepPos: they determine the multiplication and the decimal separator setting.

Reading the multiplicator and the decimal separator The following subroutine reads those table values to registers, depending from which ADC channel (in rAdcCh) the ADC sum stems from.

; Read the multiplicator and the decimal separator
;   rmp: 0 = Voltage divider input voltage
;        1 = Resistors R1 to R6
;        2 = Voltage divider base voltage
  ldi ZH,High(sMultTable) ; Point Z to multplicator table in SRAM
  ldi ZL,Low(sMultTable)
  cpi rAdcCh,1 ; ADC channel was 1?
  brcs GetMultiplicator0 ; No, zero
  brne GetMultiplicator2 ; No, larger than 1
  ; Multiplicator for channel 1 (current measurement)
  adiw ZL,4 ; Point Z to the current multiplicators
  mov rmp,rRes
  push rmp ; Ensure that both rRes have the same value
  lsl rmp ; Multiply by 2
  add ZL,rmp ; Add to pointer, LSB
  ldi rmp,0 ; Add zero
  adc ZH,rmp ; plus carry
  ld rMulL,Z+ ; Read multiplicator, LSB
  ld rMulH,Z ; dto., MSB
  ldi ZH,High(sDecSepPos+2) ; Point to decimal separator table
  ldi ZL,Low(sDecSepPos+2)
  pop rmp ; Recall the previous rRes
  add ZL,rmp ; Add to pointer
  ldi rmp,0 ; Add zero
  adc ZH,rmp ; plus carry
  ld rDecSep,Z ; Read separator
GetMultiplicator2: ; Base voltage parameters
  ldd rMulL,Z+2 ; Read bytes 2 and 3 of the multiplicator table
  ldd rMulH,Z+3
  lds rDecSep,sDecSepPos+1) ; Read decimal separator position
GetMultiplicator0: ; Input voltage parameters
  ld rMulL,Z+ ; Read multiplicator
  ld rMulH,Z
  lds rDecSep,sDecSepPos ; and decimal position

The displayed results are derived for the voltage measurement of the power source (rAdcCh=0):

5.2.3 Multiplication of the ADC sum with the multiplicator

Multiplying two 16-bit-numbers Multiplying a 16-bit number (the ADC sum) with another 16-bit number (the multiplication factor) is a little bit tricky, even with an on-board hardware multiplier. The result is in any case 32 bits in size, even though we need only the upper 16 bits of the result. But we'll have to convert the 16-by-16-bit multiplication to a series of 8-by8-bit multiplications.

A 16-bit number consists of two 8-bit numbers: an LSB with the lower eight bits and the MSB with the higher eight bits. The MSB is by factor of 256 larger than the LSB, so we can write a 16-bit number as 256 * MSB + LSB. Here we shorten the MSB as M and the LSB as L. Two of those numbers, N1 and N2, are N1 = 256 * M1 + L1 and N2 = 256 * M2 + L2.

Registers for the multiplication These here are the registers, that are used for the multiplication. Now we arrive at higher arithmetic: Multiplication of the two numbers arrives at N1 * N2 = (256 * M1 + L1) * (256 * M2 + L2). To get the result of this we'll have to multiply each of the two terms with the other two terms and to sum up the results. That means four multiplications and four results to be added.

The first term is to multiply the two MSBs. As both have a "256 *" we arrive at 256 * 256 * M1 * M2 = 65,536 * M1 * M2. The 65,536 means: put this multiplication result of M1 and M2 to the bytes 2 and 3 of the result, which is in rRes3:rRes2:rRes1:rRes0.

The two terms L1 and L2 are even simpler to muliply: their result goes to rRes1 and rRes0, and so add the least significant two bytes to the result.

The two other terms, 256 * M1 + L2 and 256 * M2 + L1, are both added to the bytes 2 and 1 of the result. As the adding can lead to carries, ADC is used for the MSB addition. If the MSB addition leads to carry, those would increase byte 3 of the results.

Multiplication in assembler This here is the assembler-source code formulation of the 16-bit multiplication.

That was it: from a 16-by-16-bit multiplication four 8-by-8-bit multplications have been made, associated with some shifting and adding.

Multiplying the ADC sum with the voltage multiplicator The source code for this multiplication is:

	mul rMulH,rAdcH ; Multiply MSBs
	mov rRes2,R0 ; 65536 * Result to rRes
	mov rRes3,R1
    ; I4: Multiply the LSBs
	mul rMulL,rAdcL ; Multiply LSBs
	mov rRes0,R0 ; Result to rRes
	mov rRes1,R1
    ; I2: Multiply the MSB multiplier by the LSB of the ADC
	mul rMulH,rAdcL ; Multiply MSB multiplier
	add rRes1,R0 ; Add 256 * Result to rRes
	adc rRes2,R1
	brcc NoInc1
	inc rRes3 ; Overflow to next higher result byte
    ; I3: Multiply the MSB of the ADC by the LSB of the multiplier
	mul rMulL,rAdcH; Multiply MSB ADC
	add rRes1,R0 ; Add 256 * Result to rRes
	adc RRes2,R1
	brcc NoInc2
	inc rRes3 ; Overflow to next higher result byte

The ADC sum (here of 64 * 1023) is in register pair R13:R12 (0xFFC0, decimal 65,472), the multiplier in register pair R3:R2 (0x0DA9, decimal 4,497). The multiplication result finally is in the four registers R7:R6:R5:R4 (0x0DA595C0).

Only the two most significant bytes in R7:R6 are used further, R5:R4 is only used for rounding. Rounding goes as follows:

  ; Round result up
  ldi rmp,0x80
  add rRes0,rmp
  adc rRes1,rmp
  ldi rmp,0
  adc rRes2,rmp
  adc rRes3,rmp

Adding 0x80 to 0xC0 in rRes0 sets the carry and adding carry plus 0x80 to rRes1 also sets the carry, so adding carry and zero to rRes2 increases rRes2 to 0xA6. This does not set the carry, so adding zero to rRes3 leaves the 0x0D as it is.

5.2.4 Converting the result to decimal

Conversion to decimal The result is 0x0DA6, which is decimal 3,494, and looks like the maximum voltage that can be measured. But it is binary and has to be converted to a 5-digit decimal ASCII string. The following assembler code does this, and writes the result to SRAM at location sVal+2:

  ; Convert 16-bit result / 65536 to decimal
  ldi ZH,High(sVal+2) ; Pointer for result
  ldi ZL,Low(sVal+2)
  ldi rmp,High(10000)
  mov rRes1,rmp
  ldi rmp,Low(10000)
  mov rRes0,rmp
  rcall ToDecDigW ; Convert to decimal digit
  ldi rmp,High(1000)
  mov rRes1,rmp
  ldi rmp,Low(1000)
  mov rRes0,rmp
  rcall ToDecDigW ; Convert to decimal digit
  ldi rmp,High(100)
  mov rRes1,rmp
  ldi rmp,Low(100)
  mov rRes0,rmp
  rcall ToDecDigW ; Convert to decimal digit
  ldi rmp,Low(10)
  mov rRes0,rmp
  rcall ToDecDigB ; Convert to decimal digit
  ldi rmp,'0'
  add rmp,rRes2
  st Z+,rmp

The called subroutine, ToDecDigW, called here, repeatedly subtracts the decimal in rRes1:rRes0 (first 10,000, then 1,000 and then 100) wordwise from rRes3:rRes2 and counts how often this is possible without a set carry. The decimal number digit is then written to SRAM at the pointer Z and the last subraction is undone by adding the subtractor.

The last decimal, 10, is counted in ToDecDigB. It only uses rRes0 as single byte.

; Converts rRes3:rRes2 wordwise to a decimal digit
;   uses rRes1:rRes0 as digit subtractor
;   shifts result to SRAM at Z
  ldi rmp,'0'-1
  inc rmp
  sub rRes2,rRes0 ; Subtract digit
  sbc rRes3,rRes1
  brcc ToDecDigW1
  add rRes2,rRes0
  adc rRes3,rRes1
  st Z+,rmp
; Converts rRes2 bytewise to a decimal digit
;   uses rRes0 as digit subtractor
;   shifts result to SRAM at Z
  ldi rmp,'0'-1
  inc rmp
  sub rRes2,rRes0
  brcc ToDecDigB1
  add rRes2,rRes0
  st Z+,rmp

The last digit just adds the remaining rest in rRes2 with ASCII-0 and writes the result to SRAM.

Please note that in the current conversion state the decimal separator is still missing and leading zeros are still part of that string.

5.2.5 Decimal separator and leading blanks

Adding the decimal separator As can be seen here, the dimension of the number (V, A or mA) has been added at the end, followed by a tab character.

Adding the decimal separator simply goes as follows: all characters are shifted one position to the left until the decimal separator position is reached. At that position the decimal separator character is written.

The code is:

  ; Add decimal separator
  ldi ZH,High(sVal+6) ; Pointer to end of result
  ldi ZL,Low(sVal+6)
  sub ZL,rDecSep
  ldi rmp,0
  sbc ZH,rmp
  mov R0,ZL
  ldi ZH,High(sVal+2)
  ldi ZL,Low(sVal+2)
  adiw ZL,1
  ld rmp,Z
  st -Z,rmp
  adiw ZL,1
  cp ZL,R0
  brne MultiplySetSep
  ldi rmp,cDecSep
  st Z,rmp

The blanking of leading zeros is also simple:

  ldi ZH,High(sVal+2)
  ldi ZL,Low(sVal+2)
  ld rmp,Z
  cpi rmp,cDecSep
  breq MultiplyClrLeadgSet
  cpi rmp,'0'
  brne MultiplyClrLeadgDone
  ldi rmp,' '
  st Z+,rmp
  rjmp MultiplyClrLeadg
  ldi rmp,'0'
  st -Z,rmp

Note that if the decimal separator has been reached, its leading zero has to be finally restored.

5.2.6 Copying the voltage to the line buffer

Copying the voltage to the line buffer The result string has to be written to the LCD (ATmega324 version only) and has to be copied for later sending over the RS232 interface. For that, a line buffer has been added in SRAM.

As can been here, the line buffer has already been established with the time string, followed by a tab character. The routine ToLineBuf is called. with Z pointing to the string. One of the problems here was that only Z was available as pointer, because X and Y are exclusively occupied by the operation of the transmit-ring-buffer.

; Parameter that Z points to to line buffer
;   End is a tab character
  lds R0,sLineBufPos
  clr R1
  ldi rmp,Low(sLineBuf)
  add R0,rmp
  ldi rmp,High(sLineBuf)
  adc R1,rmp
  ld rmp,Z+
  cpi rmp,' '
  breq ToLineBuf1
  push ZH
  push ZL
  mov ZH,R1
  mov ZL,R0
  st Z+,rmp
  mov R1,ZH
  mov R0,ZL
  pop ZL
  pop ZH
  cpi rmp,cTab
  brne ToLineBuf1
  ldi rmp,Low(sLineBuf)
  sub R0,rmp
  sts sLineBufPos,R0

The picture shows that the voltage string has been added, followed by a tab character. Note that during copying leading blanks are not copied to reduce the burden of the transmission a little bit.

5.2.7 Converting the other measurements

The current multiplicator Converting the ADC sum on the ADC1 input pin, that measures the voltage on the emitter resistor, goes just like that. The only difference is that the current multiplier (here: 0x0D05) depends from the active resistor in rRes. The decimal separator position is now 0x03.

Multiplication, conversion, string conditioning and copying the result to the line buffer is all the same, just the "V=" is exchanged by "I=" and "V" at the end by "A" (for higher resistors: "mA" instead).

5.2.8 Copying the line buffer to the transmit-ring-buffer

Adding base voltage and copy to transmit buffer Here, the voltage on the base of the Darlington has been added to the line buffer.

The line buffer now has to be copied to the ring-buffer for transmit. The following code does this.

First a carriage return and a line feed character is been added. The buffer is then copied byte-by-byte to the ring-buffer by calling InBuff with the character in rmp.

  ldi ZH,High(sLineBuf)
  ldi ZL,Low(sLineBuf)
  lds rmp,sLineBufPos
  add ZL,rmp
  ldi rmp,0
  adc ZH,rmp
  ldi rmp,cCR
  st Z+,rmp
  ldi rmp,cLF
  st Z, rmp
  ldi ZH,High(sLineBuf)
  ldi ZL,Low(sLineBuf)
  ld rmp,Z+
  cpi rmp,cLF
  breq AdcHnd2b
  rcall InBuff
  rjmp AdcHnd2a
  rcall InBuff
  clr rmp
  sts sLineBufPos,rmp

InBuff automatically starts the RS232 transmit process.

Top Schematics Mounting Operation Software Functioning

5.3 Functioning of the serial interface

The serial interface uses the internally built-in USART. This provides all necessary hardware for transmission and reception.

5.3.1 Clocking of the serial interface

To transmit and receive with a correct baud rate, the controller has to work with a suitable clock rate. The internal Baud rate generator divides the controller's clock by 16 and by a programmable integer value between 1 and 4,096: This Baud rate divider constant minus one is, during startup, written to the internal Baud rate register.

Mostly used are Baud rates from 1,200 bits per second and upwards. 9k6 or 19k2 are sufficient for our purpose here, there is no need to run faster.

Usablility of crystals for serial interfaces The table "crystal serial" in the LibreOffice-Calc spreadsheet here lists commercially available crystals (and a few other frequencies) and checks if those are usable here. For the given Baud rates, the UBRR constant is calculated. Constants with a remainder of 0 are useable (green). If the remainder is below 0.100 or higher than 0.900, you can check if that works fine in your environment (yellow). Crystals with a too low frequency (divider smaller than 0.9) are red. White background says: this crystal can in no case be used.

As can be seen from the table, many crystals can be used for that purpose, and you are free to select from those. In my case I used a 2.4576-MHz crystal at a Baud rate of 19k4, which is set as Default in the source code.

Top Schematics Mounting Operation Software Functioning

5.3.2 Organization of data to be transmitted in a ring buffer

This device produces a lot of different messages to be send over the serial interface. To not having to program all those messages individually and to wait for complete transmit, I have constructed a ring buffer. All data is written to this area in the SRAM, and is transmitted from there. Write and read operations are completely separated, so that no delays are necessary during all operation phases. For Freaks: this is a First-In-First-Out storage.

The buffer uses two pointers to store data and to transmit it. If the buffer pointers reach the end of the buffer, they restart from the beginning. Therefore both buffer pointers have to check, whether the end has been reached. At start-up, and later if all data has been sent to the transmitter, the output pointer points to the input position and the transmitter is switched off (after all data has been sent). Every input to the buffer starts the transmission, if this hasn't been already started.

The ring buffer pointer for input uses the X register pair, for output the Y register pair. As those registers aren't needed for other purposes in this application, the exclusive use for this purpose is justified (otherwise those would have to be placed to SRAM locations, which causes a slightly higher instruction count and access times, see the beginner's course for such an alternative solution here.)

Top Schematics Mounting Operation Software Functioning

5.3.3 Input to the ring buffer

Input of data to the ring buffer This is the routine by which data is written into the ring buffer. The data byte to be written is in rmp.

It starts with writing the byte to the buffer at the input buffer pointer location in X, by which X is increased to the next position. If X now points to beyond the end of the buffer, the X pointer is set to the buffer start.

Now it is checked whether the increased X pointer points to the output pointer in Y. That would be the case if the whole buffer has been written, but hasn't been sent. To not overwrite the buffer's content in that case and to provoke a buffer overflow, the X pointer has to be decreased by 1. If this happens when the increased pointer points to the buffer start, the X pointer has to go back to the last buffer position.

Finally, the UDR-empty-interrupt-enable bit of the UART is set to one, to start the transmission. As far as the transmit input byte storage is empty, the UDR interrupt executes, reads the next byte from the SRAM position that Y points to and transmits it (see the next chapter).

Top Schematics Mounting Operation Software Functioning

5.3.4 Transmitting from the ring buffer

Transmitting from the ring buffer Sending data from the buffer to the UART's data register works with the UDR-empty-interrupt-enable bit. If this is set, and if the data register of the UART is empty, the interrupt executes.

The routine starts with checking, if the transmit pointer in Y is equal to the input pointer in X. If this the case, then all data has been sent. In that case no further data is sent and the transmit complete interrupt bit TXCIE is set. This waits until all data bits and the two stop bits have been transmitted and interrupts then. The later TXCIE interrupt finally switches the transmitter off. The UDRE interrupt is switched off to avoid further interrupts of this type.

If both pointers are still different, the next byte is read from the Y position and Y is increased by 1. The byte is written to the UART's data register. Now it is checked whether the increased Y pointer points to the outside of the buffer. If yes, the Y is re-started at the buffer's start address. Then the interrupt is finalized.

Top Schematics Mounting Operation Software Functioning

5.4 Functioning of the LCD

The ATmega324PA version has an LCD, which is controlled by the include file The main assembler file defines the LCD's properties and settings, such as the ports and portpins. All operations involving the LCD, such as initialization, setting output positions, writing characters to the LCD and defining special characters on the LCD such as arrows, is performed by routines in this include file. All available routines of are described on top of the include source code, together with the associated calling conventions.

5.4.1 Initializing the LCD

All that the LCD include needs are the parameters that characterize the LCD in this application. A template for these parameters is included in, just copy it and fill it in.

The parameters here are:
  1. The LCD's size is 4 lines with 20 characters each.
  2. The hardware interface uses a bidirectional data port with busy detection and three control pins.
  3. Neither the decimal nor the hexadecimal conversion are used here.
  4. The simulation in SRAM is not used.
The source code for this looks like this:

  ; LCD parameters
  ; LCD size:
  .equ LcdLines = 4 ; Number of lines (1, 2, 4)
  .equ LcdCols = 20 ; Number of characters per line (8..24)
  ; LCD bus interface
  .equ LcdBits = 8 ; Bus size (4 or 8)
  ; If 4 bit bus:
    ;.equ Lcd4High = 1 ; Bus nibble (1=Upper, 0=Lower)
  .equ LcdWait = 0 ; Access mode (0 with busy, 1 with delay loops)
  ; LCD data ports
  .equ pLcdDO = PORTC ; Data output port
  .equ pLcdDD = DDRC ; Data direction port
  ; LCD control ports und pins
  .equ pLcdCEO = PORTB ; Control E output port
  .equ bLcdCEO = PORTB2 ; Control E output portpin
  .equ pLcdCED = DDRB ; Control E direction port
  .equ bLcdCED = DDB2 ; Control E direction portpin
  .equ pLcdCRSO = PORTB ; Control RS output port
  .equ bLcdCRSO = PORTB0 ; Control RS output portpin
  .equ pLcdCRSD = DDRB ; Control RS direction port
  .equ bLcdCRSD = DDB0 ; Control RS direction portpin
  ; If LcdWait = 0:
  .equ pLcdDI = PINC ; Data input port
  .equ pLcdCRWO = PORTB ; Control RW output port
  .equ bLcdCRWO = PORTB1 ; Control RW output portpin
  .equ pLcdCRWD = DDRB ; Control RW direction port
  .equ bLcdCRWD = DDB1 ; Control RW direction portpin
  ; If you need binary to decimal conversion:
  ;.equ LcdDecimal = 1 ; If defined: include those routines
  ; If you need binary to hexadecimal conversion:
  ;.equ LcdHex = 1 ; If defined: include those routines
  ; If simulation in the SRAM is desired:
  ;.equ avr_sim = 1 ; 1=Simulate, 0 or undefined=Do not simulate

Note that the parameter CLOCK has also to be defined, because uses it for two purposes:
  1. Sending data and commands to the LCD requires setting the LCD's enable pin E high for at least 1 µs long. uses CLOCK to determine how many NOPs have to be inserted after setting E and later clearing E to achieve this minimum active time (in this case: two NOPs).
  2. At start-up and during the first mode setting of the LCD exact delay times are needed (LCD initial phase = 50 ms, etc.). As these delays require the CLOCK parameter for their timing, it is vital to set this.
Note that all port and portpin info is for the configuration shown in the schematic above. checks automatically if all necessary paramters are defined and throws error messages if one is missing.

An RCALL of the routine "LcdInit" sets all those parameters for the LCD and initiates the data and control pins.

5.4.2 Special characters for the LCD

The output of menu entries uses the following arrow characters: ↓, →, ↔, ←. These characters are not part of the standard LCD character set. Those have to be defined in the initial stage.

Special characters Those characters are generated by the spreadsheet "LcdSpecChar" in the LibreOffice-Calc file powerscanner.ods. For each of the seven characters we'll pick the white pixels and set these to 1. On the right side of that sheet we'll get the assembler source code table for these characters, to be copied to the source code.

To define those characters in the LCD, we'll point the Z register to the doubled address in the table "CodeChars" and call the routine LcdSpec. That is already it. For conventient use, we'll define ch-constants for these characters and construct a list of the 11 menu entries.

    rcall LcdInit
    ldi ZH,High(2*CodeChars)
    ldi ZL,Low(2*CodeChars)
    rcall LcdSpec
	; ...
	; and so on
	; ...
  ; Table of LCD codes
  .db 64,0,0,4,8,31,8,4,0,0 ; C = 0, LeftArrow
  .db 72,0,0,4,2,31,2,4,0,0 ; C = 1, RightArrow
  .db 80,0,0,4,14,21,4,4,4,0 ; C = 2, UpArrow
  .db 88,0,0,4,4,4,21,14,4,0 ; C = 3, DownArrow
  .db 96,0,0,4,10,31,10,4,0,0 ; C = 4, HoldArrow
  .db 104,0,0,7,3,5,8,16,0,0 ; C = 5, NEArrow
  .db 112,0,0,0,16,8,5,3,7,0 ; C = 6, SEArrow
  .db 0,0 ; End of table
  ; Characters
  .equ chLeftArrow = 0
  .equ chRightArrow = 1
  .equ chUpArrow = 2
  .equ chDownArrow = 3
  .equ chHoldArrow = 4
  .equ chNEArrow = 5
  .equ chSEArrow = 6
  ; Menu entries
  .db 0,chUpArrow,"Voltage  ",0xFE ; Increase voltage on power supply output
  .db 1,chDownArrow,"Voltage  ",0xFE ; Decrease voltage on power supply output
  .db 2,chUpArrow,"Base vtg ",0xFE ; Increase voltage on Darlington base
  .db 3,chDownArrow,"Base vtg ",0xFE ; Decrease voltage on Darlington base
  .db 4,chUpArrow,"Current  ",0xFE ; Increase current
  .db 5,chDownArrow,"Current  ",0xFE ; Decrease current
  .db 6,chUpArrow,"Limit cur",0xFE ; Increase limit
  .db 7,chDownArrow,"Limit cur",0xFE ; Decrease limit
  .db 8,chUpArrow,"Pwm resol",0xFE ; Increase PWM resolution
  .db 9,chDownArrow,"Pwm resol",0xFE ; Decrease PWM resolution
  .db 10,chUpArrow,"Pwm value",0xFE ; Increase PWM value
  .db 11,chDownArrow,"Pwm value",0xFE ; Decrease PWM value
  .db 12,chRightArrow,"Start sc ",0xFE ; Start the scan
  .db 13,chHoldArrow,"Hold scan",0xFE ; Hold scan
  .db 14,chUpArrow,"Contin.up",0xFE ; Continue the scan upwards
  .db 15,chDownArrow,"Cont.down",0xFE ; Continue the scan downwards
  .db 16,chLeftArrow,"Stop scan",0xFE ; Stop the scan

All menu entries are 10 characters wide, start with the number of the entry, the text of the entry and the hex FE character for the end of the line. Those fit well into the right side of line 1 of the LCD.

5.4.3 Displaying text on the LCD

After initiation of the LCD and defining the special characters a first display is shown on the LCD. The template for those texts in 4-by-20 size can be copied from The table for it looks like this:

  .db "Power-Supply Scanner",0x0D,0xFF
  .db "    ATmega324PA     ",0x0D,0xFF
  .db "   V1.0 June 2022   ",0x0D,0xFF
  .db " (C)2022 by DG4FAC  ",0xFE,0xFF
  ;    01234567890123456789

This text entry in the flash is copied to the LCD by setting the Lcd's output position to line 1 column 1 (by calling LcdPos with Z = 0), by placing the doubled address of LcdOpenText into Z and calling the routine LcdText. The character 0x0D in this text sets the LCD's output position to the beginning of the next line, the 0xFE means end of the text, while 0xFF is simply ignored and does nothing.

The further init then waits for a pre-defined delay period (by default: 60 * 50 ms = 3 seconds) and outputs the following text:

  .db "Powerscan Menu entry",0x0D,0xFF
  .db "PS voltage=  33.33 V",0x0D,0xFF
  .db "Base voltg=   1.234V",0x0D,0xFF
  .db "Current   =   3.333A",0xFE,0xFF
  ;    01234567890123456789

5.5 Menus and their selection

5.5.1 Menu over the RS232 interface


5.5.2 Menu over the LCD

A little bit different works the menu selection over the LCD. Two cases can initiate a menu entry selection here:
  1. Via the potentiometer: the user has turned the menue potentiometer. That means the selected menu item is to be calculated from the ADC sum on ADC channel 4 and is to be displayed on line 1 of the LCD.
  2. Via key 1: the user has pressed key 1, or alternatively, the user has pressed key 2 while key 1 is also pressed. In the first case the menu entry in rMenu is to be increased by one position, in the second case it is to be decreased by two (because pressing key 1 before already increased the active menu entry). In both cases the active menu entry is to be displayed in liné 1 of the LCD.
The second case would have to block the first case: the first key pressing has to switch off the potentiometer selection via the potentiometer (because this would constantly overwrite rMenu). This is blocked from the key 1 and turned on again if key 2 is pressed (without key 1 pressed).

5.6 Keys and switches

5.6.1 Keys

The two key input pins are only active in the ATmega324-version. Those keys S1 and S2 are connected to Port D's pins 3 and 4. These pins are configured as inputs and they raise the INT0 and INT1 interrupt on pin changes.

To ensure that all reactions to INT0 and INT1 interrupts happen only once, even if further ints occur, it is required to debounce those. So whenever an INT0 or INT1 happens, it is first checked If one of those conditions is not true, the debounce counter is restarted. If both conditions are true, a flag is set, that triggers further actions.

As both keys force different actions, and as the reaction to key 2 is depending from the state of key 1 (not pressed: execute the current menu item, pressed: decrease menu item), both debounce counters are necessary.

The debounce counters are decremented in the TC1's PWM compare match A interrupt service routine. If the counters are not zero, they are decreased. If both are zero, TC1's compare match A interrupt is disabled.

The TC1's PWM compare match interrupt depends from the PWM's resolution: by default it is ten bits, it can be reduced to nine or eight bits. So the compare match interrupt occurs either every 210 (10 bit PWM), 29 (9 bit PWM) or 28 (8 bit PWM) clock cycles. At the default clock frequency of 2.457600 MHz these are times of either 0.417 ms, 0.208 ms or 0.104 ms. To achieve a 20 ms long debounce period, the counter is either set to 48 (10 bit), to 96 (9 bit) or to 192 (8 bit).

If the user changes the PWM resolution, both the debounce counter values as well as the default debounce value have to be adjusted accordingly.

5.6.2 Switches

The six switches of the rotary switch encode the emitter resistor, which is always in register rRes and influences the current calculation. The respective port pins of which the rotary switches are connected to fire their PCINT interrupt whenever the polarity of the input pin changes.

These switches are only active on negative edges. They do not need debouncing, as repeated occurrances of pulses do not confuse the rRes setting. Repeated occurrance only sets the same value to rRes.

The following table shows the affected PCI pins. As those are specific for the controller type, note the two different type colums.

PortpinMask bit(s)PCICRPortpinMask bit(s)PCICR

The algorithms are simple: search for the portpin with a low input voltage, if found set rRes to the respective resistor number:

  sbis In-Port,Portbit-In
  ldi rRes,N

If the same PCINT-Interrupt can be caused by two or more portpins, add all these with those two additional lines. The interrupt service routine ends with RETI, as there are no instructions that influence flags skip the save/restore of SREG in those service routines.

No further actions are necessary. rRes is only read by the current calculation subroutine, to calculate the current from the ADC's voltage measurement.

As rRes is read in a specific routine during the initiation phase, it isn't necessary to store it in EEPROM (which would be a potential error source when the user rotates the rotary switch while the controller isn't active).

As the interrupt service routines are very short and execute within less than 20 clock cycles, there is no need for debouncing.

Top Schematics Mounting Operation Software Functioning

©2022 by