Path:
Home =>
AVR overview =>
Applications => Power supply scanner
Diese Seite in Deutsch:
 |
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:
- The schematics of the project are provided in the LibreOffice-Draw file
here for download.
- Calculations and tables of the project are in the LibreOffice-Calc file
here for download.
- Flow diagrams of the project are in the LibreOffice-Draw file
here downloadbar.
- The software for download here.
For testing the hardware a few short programs are available for download
there as well.
The power scanner comes in two versions:
- The version with the ATmega48 has the RS232 interface only, so can only
work with those commands.
- 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
The schematic with the ATmega48 consists of the following parts:
- 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,
- 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,
- an AD-converter with an internal reference voltage of 1.1 Volt,
which is externally cleaned with a capacitor on the AREF pin, and
- measures on ADC0 the voltage of the power supply, which is divided
down to the reference voltage level with two resistors,
- measures on ADC1 the emitter voltage of the Darlington (voltage range
0.0 to 1.1 V, 10-bit-ADC),
- measures on ADC2 the base voltage of the Darlingtons via a resistor
divider and controls the RC- filter,
- 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,
- 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),
- 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,
- the ISP6 interface, over which the flash can be programmed and the internal
EEPROM receives its initial start values, and
- 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
The ATmega324PA version has all that the ATmega48 version has, but on different
pins. Additionally this version has
- 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,
- additional potentiometers
- on ADC3 for adjusting the background light intensity of the LCD,
- on ADC4 for selecting the menue entry, and
- on ADC5 for adjusting numbers,
- 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
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
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 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.
2.1 Parts list for the ATmega48 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
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
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:
- 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Ω.
- 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:
- The RC filter is on an output voltage of more than 2.6 V,
and
- the power supply can deliver more than 10A current.
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:
- The switch 1 on INT0 (black) changes to the next input mode.
- The switch 2 on INT1 (red), while key 1 is off, executes the displayed menu entry.
- The switch 2 on INT1 (red), while key 1 is pressed, changes to the previous input mode.
Available are the following adjustment modes:
# | Switch 1 | LCD output | Switch 2 executes | RS232 command | Number input | Default numbers |
0 | Voltage on power supply output | increase | ↑Voltage | increase voltage display | V+, V++ | V=number | V=3497 |
1 | decrease | ↓Voltage | decrease voltage display | V-, V-- |
2 | Voltage on the transistor base | increase | ↑Base | inrease base voltage display | B+, B++ | B=number | B=5009 |
3 | decrease | ↓Base | decrease base voltage display | B-, B-- |
4 | Current display | increase | ↑Current | increase current display | I+, I++ | I=number | I=3333/1100/5000/ 2340/1341/5000 |
5 | decrease | ↓Current | decrease current display | I-, I-- |
6 | Upper scan limit | increase | ↑Limit | increase current maximum | L+, L++ | L=number | L=Imax/2 |
7 | decrease | ↓Limit | decrease current maximum | L-, L-- |
8 | PWM resolution | increase | ↑PwmRes | increase resolution of the PWM | R+, R++ | R=8/9/10 | R=10 |
9 | decrease | ↓PwmRes | decrease resolution of the PWM | R-, R-- |
10 | PWM value | increase | ↑PwmVal | increase PWM value | P+, P++ | P=number | P=0 |
11 | decrease | ↓PwmVal | decrease PWM value | P-, P-- |
12 | Scan | Start | →Scan | start the scan | S | - | - |
13 | hold | ↔Hold | keep the scan on hold | H |
14 | continue up | ↗Scan-up | continue the scan upwards | U |
15 | continue dwn | ↘Scan-dwn | continue the scan downwards | D |
16 | end | ←Stop | end the scan | E or Z |
Note that lower case characters over the serial interface are converted
to upper case.
- Assembler source code downloads
- Entries in the sourcecode
- Fuse settings
- Test programs for the hardware
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.
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:
- cBaud sets the baud rate for transmiting and receiving on
the serial interface. Baud rates up to 39k4 are tested and work.
- cEN = 1 turns the language to english, this also changes
the decimal separator from ',' to '.'. If your serial interface
software collects the scan values and you convert the .CSV file
from ASCII to a spreadsheet, this can be decisive.
ATmega48 TBD
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.
For the stepwise mounting of the device in the following row
- crystal and ISP6 connection,
- RC filter and Darlington-transistor driver,
- serial interface, and
- in the ATmega324PA version: the LCD,
I have written the following short programs to test the hardware.
Hardware to test | ATmega48 | ATmega324PA |
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:
- It switches the OC1B pin to be output.
- It initiates the timer TC1 in 10-, 9- or 8-bit-mode as PWM,
with two selectable PWM puls widths in percent, and
- it switches after a selectable pause time in seconds between
both pulse widthes.
.
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.
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
- initiate the serial interface with the baud rate that can
configured on top (default is 19k2),
- transmit in polling-mode 40 times the ASCII character "U"
to synchronize the serial receiver, and
- 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
lcd.inc 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.
- Functioning of the current adjustment
- Calculation of voltages and currents
- Functioning of the serial interface
- Clocking of the serial interface
- Ring buffer organization
- Ring buffer input
- Ring buffer output
- Functioning of the LCD
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
- at 10 bit resolution 2,400 Hz,
- at 9 bit resolution 4,800 Hz, and
- at 8 bit resolution 9.600 Hz.
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).
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.
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.
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).
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:
- 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,
- 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.
- 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.
- 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,
- 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,
- 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:
- a 16-bit multiplicator, by which the ADC sum has to be
multiplied, and
- 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
EepPwmRet:
.dw 10
EepPwmVal:
.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
sPwmRes:
.byte 2 ; PWM resolution, 8/9/10
sPwmVal:
.byte 2 ; Current PWM value
sDecSepPos: ; Decimal separator positions
.byte 8 ; default: 3,4,4,4,2,2,2,3
sEepEnd:
The two relevant tables here are sMultTable and sDecSepPos: they
determine the multiplication and the decimal separator setting.
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
GetMultiplicator:
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
ret
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
ret
GetMultiplicator0: ; Input voltage parameters
ld rMulL,Z+ ; Read multiplicator
ld rMulH,Z
lds rDecSep,sDecSepPos ; and decimal position
ret
The displayed results are derived for the voltage measurement
of the power source (rAdcCh=0):
- the multiplicator is in R3:R2 (0x0DA9, decimal 3,497),
- the decimal separator is in R12 (0x02).
5.2.3 Multiplication of the ADC sum with the multiplicator
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.
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.
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.
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
NoInc1:
; 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
NoInc2:
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
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
ToDecDigW:
ldi rmp,'0'-1
ToDecDigW1:
inc rmp
sub rRes2,rRes0 ; Subtract digit
sbc rRes3,rRes1
brcc ToDecDigW1
add rRes2,rRes0
adc rRes3,rRes1
st Z+,rmp
ret
; Converts rRes2 bytewise to a decimal digit
; uses rRes0 as digit subtractor
; shifts result to SRAM at Z
ToDecDigB:
ldi rmp,'0'-1
ToDecDigB1:
inc rmp
sub rRes2,rRes0
brcc ToDecDigB1
add rRes2,rRes0
st Z+,rmp
ret
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
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)
MultiplySetSep:
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)
MultiplyClrLeadg:
ld rmp,Z
cpi rmp,cDecSep
breq MultiplyClrLeadgSet
cpi rmp,'0'
brne MultiplyClrLeadgDone
ldi rmp,' '
st Z+,rmp
rjmp MultiplyClrLeadg
MultiplyClrLeadgSet:
ldi rmp,'0'
st -Z,rmp
MultiplyClrLeadgDone:
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
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
ToLineBuf:
lds R0,sLineBufPos
clr R1
ldi rmp,Low(sLineBuf)
add R0,rmp
ldi rmp,High(sLineBuf)
adc R1,rmp
ToLineBuf1:
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
ret
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
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
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)
AdcHnd2a:
ld rmp,Z+
cpi rmp,cLF
breq AdcHnd2b
rcall InBuff
rjmp AdcHnd2a
AdcHnd2b:
rcall InBuff
clr rmp
sts sLineBufPos,rmp
InBuff automatically starts the RS232 transmit process.
The serial interface uses the internally built-in USART. This provides
all necessary hardware for transmission and reception.
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.
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.
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.)
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).
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.
The ATmega324PA version has an LCD, which is controlled by the
include file lcd.inc. 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 lcd.inc 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 lcd.inc, just copy it and fill it in.
The parameters here are:
- The LCD's size is 4 lines with 20 characters each.
- The hardware interface uses a bidirectional data port
with busy detection and three control pins.
- Neither the decimal nor the hexadecimal conversion are
used here.
- 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
lcd.inc uses it for two purposes:
- Sending data and commands to the LCD requires setting
the LCD's enable pin E high for at least 1 µs
long. lcd.inc 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).
- 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.
lcd.inc 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.
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
Codechars:
.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
Menus:
.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 lcd.inc. The table for it
looks like this:
LcdOpenText:
.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:
LcdOperationText:
.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
TBD
A little bit different works the menu selection over the LCD.
Two cases can initiate a menu entry selection here:
- 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.
- 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.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 the input pin is low (high-to-low), and
- if the debounce period is over (the debounce counter is
zero).
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.
N | RN | ATmega48 | ATmega324 |
Portpin | Mask bit(s) | PCICR | Portpin | Mask bit(s) | PCICR |
0 | 0.33 Ω | PB0 | PCINT0 in PCMSK0 | PCIE0 | PB4 | PCINT12 in PCMSK1 | PCIE1 |
1 | 1 Ω | PB1 | PCINT1 in PCMSK0 | PD5 | PCINT29 in PCMSK3 | PCIE3 |
2 | 2.2 Ω | PC4 | PCINT12 in PCMSK1 | PCIE1 | PD6 | PCINT30 in PCMSK3 |
3 | 4.7 Ω | PC5 | PCINT13 in PCMSK1 | PD7 | PCINT31 in PCMSK3 |
4 | 10 Ω | PD6 | PCINT22 in PCMSK2 | PCIE2 | PA6 | PCINT6 in PCMSK0 | PCIE0 |
5 | 22 Ω | PD7 | PCINT23 in PCMSK2 | PA7 | PCINT7 in PSMSK0 |
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.
©2022 by http://www.avr-asm-tutorial.net