Actions

ATmega documentation: Difference between revisions

From HacDC Wiki

(to be rewritten)
(rewritten)
Line 1: Line 1:
This is an attempt to write documentation for the ATmega328P family of microcontrollers (and learn it myself as I go along). Send any corrections, suggestions, or other comments to [email protected]
This is an attempt to write documentation for the ATmega328P family of microcontrollers (and learn it myself as I go along). Send any corrections, suggestions, or other comments to [email protected]
<nowiki>
<nowiki>
TO BE REWRITTEN
The ATmega328P Microcontroller - 1 Setting up the I/O pins - Sept. 19, 2019 version
The ATmega328P Microcontroller - 1 Setting up the I/O pins - Sept. 9, 2019 version
Note: In the following, the more negative voltage (Gnd) is 0 & the more positive voltage (Vcc) is 1. Addresses & data are given in hexadecimal.
Note: In the following, the more negative voltage (Gnd) is 0 & the more positive voltage (Vcc) is 1. Addresses & data are given in hexadecimal.
The ATmega328P is one of a large family of similar 8-bit microcontrollers and can operate on 1.8 to 5.5 V. The data sheet is available at ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf (in the following the section describing an item is often given afterwards in parentheses, i.e. SREG(7.3.1)); the instruction set manual for this family is available at ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf (note that the 328P does not have all of the instructions listed) & also see en.wikipedia.org/wiki/Atmel_AVR_instruction_set.  Other members differ in various ways; consult their data sheets for details. The ATmega328P has 32 8-bit general purpose registers (which can also be addressed as data memory locations 0000-001F), 224 8-bit I/O registers (which can also be addressed as data memory locations 0020-00FF; note that 20 must therefore be added to the I/O register number when addressing it as memory), 2kx8 data RAM(8.3) (data memory locations 0100-08FF), a separate 16kx16 program flash memory(8.2) (note that while it is an 8-bit chip the instructions are 16 bits), & a separate 1kx8 EEPROM(8.4).
The ATmega328P is one of a large family of similar 8-bit microcontrollers and can operate on 1.8 to 5.5 V. The data sheet is available at ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf (in the following the section describing an item is often given afterwards in parentheses, i.e. SREG(7.3.1)); the instruction set manual for this family is available at ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf (note that the 328P does not have all of the instructions listed) & also see en.wikipedia.org/wiki/Atmel_AVR_instruction_set.  Other members differ in various ways; consult their data sheets for details. The ATmega328P has 32 8-bit general purpose registers (which can also be addressed as data memory locations 0000-001F), 224 8-bit I/O registers (which can also be addressed as data memory locations 0020-00FF; note that 20 must therefore be added to the I/O register number when addressing it as memory), 2kx8 data RAM(8.3) (data memory locations 0100-08FF), a separate 16kx16 program flash memory(8.2) (note that while it is an 8-bit chip the instructions are 16 bits), & a separate 1kx8 EEPROM(8.4).
Line 39: Line 38:
0A DDRD
0A DDRD
0B PORTD
0B PORTD
Suppose that pin 1 is reset, 2-6 & 9 are to be inputs with the pullup resistors on for 2-3, 15-19 & 23-27 are to be outputs with 15-16 having an initial value of 0 & 17-18 having an initial value of 1, & 10-14 & 28 are unused. Here is the port bit map (the port bit is given first followed by the corresponding pin, the value of the DDR for that position, the value (if the value doesn't matter it is generally set to 0 in these examples) of the PORT for that position, & a description; after the last bit of a port the hexadecimal value of DDR & PORT is shown):
Suppose that pin 1 is reset, 2-6 are outputs, 9-13 are unused, 14-19 are inputs with the pullup resistors on for 14-15, 23 is an output set to 0, 24 is unused, & 25-28 are outputs with 25-27 set to 1. Also suppose that setting pin 23 to 1 (except under special conditions) could damage the circuit; to reduce the chance of this happening this output is placed next to Gnd (pin 22), a resistor (~5k ohms suggested) is connected from the pin to Gnd, & the other adjacent pin (24) is unused & is connected thru a "pulldown" resistor (1k suggested) to Gnd instead of using the internal pullup. Here is the port bit map (the port bit is given first followed by the corresponding pin, the value of the DDR for that position, the value (if the value doesn't matter it is generally set to 0 in these examples) of the PORT for that position, & a description; after the last bit of a port the hexadecimal value of DDR & PORT is shown):
B7 10 0 1 unused (set PORT to 1 for unused pins to turn on pullup resistor)
B7 10 0 1 unused (set PORT to 1 for unused pins to turn on pullup resistor)
B6  9 0 0 in
B6  9 0 1 unused
B5 19 1 0 out
B5 19 0 0 in
B4 18 1 1 out, =1
B4 18 0 0 in
B3 17 1 1 out, =1
B3 17 0 0 in
B2 16 1 0 out, =0
B2 16 0 0 in
B1 15 1 0 out, =0
B1 15 0 1 in, pullup on
B0 14 0 1 unused DDRB=3E PORTB=99
B0 14 0 1 in, pullup on DDRB=00 PORTB=C3
C6  1 0 0 reset
C6  1 0 0 reset
C5 28 0 1 unused
C5 28 1 0 out
C4 27 1 0 out
C4 27 1 1 out, =1
C3 26 1 0 out
C3 26 1 1 out, =1
C2 25 1 0 out
C2 25 1 1 out, =1
C1 24 1 0 out
C1 24 0 0 unused, resistor to Gnd
C0 23 1 0 out  DDRC=1F PORTC=20
C0 23 1 0 out, =0, must not be 1, resistor to Gnd DDRC=3D PORTC=1C
D7 13 0 1 unused
D7 13 0 1 unused
D6 12 0 1 unused
D6 12 0 1 unused
D5 11 0 1 unused
D5 11 0 1 unused
D4  6 0 0 in
D4  6 1 0 out
D3  5 0 0 in
D3  5 1 0 out
D2  4 0 0 in
D2  4 1 0 out
D1  3 0 1 in, pullup on
D1  3 1 0 out
D0  2 0 1 in, pullup on DDRD=00 PORTD=E3
D0  2 1 0 out DDRD=1F PORTD=E0
The following code fragment sets up the pins (in all code examples the program memory address is given first (4 hexadecimal digits) followed by the opcode (4 digits), the instruction, & an explanation). When power is turned on the processor resets and starts at address 0000(11.1). However, other things can cause execution to start nearby (for example, INT0 can cause execution to start at 0002(12.4)) so the first instruction is often a jump to get out of the way.
The following code fragment sets up the pins (in all code examples the program memory address is given first (4 hexadecimal digits) followed by the opcode (4 digits), the instruction, & an explanation). When power is turned on the processor resets and starts at address 0000(11.1). However, other things can cause execution to start nearby (for example, INT0 can cause execution to start at 0002(12.4)) so the first instruction is often a jump to get out of the way.
0000 940C JMP
0000 940C JMP
0001 1000 1000
0001 1000 1000
...
...
1000 E919 LDI R17,99 Load value to turn on pullup resistors for unused pins 14 & 10, set pins 15-16 to 0, & pins 17-18 to 1 into temporary register
1000 E13C LDI R19,1C Load value to set pin 23=0 & pins 25-27=1 into arbitrarily chosen temporary register
1001 B915 OUT 05,R17 Store into PORTB; note that this will immediately turn on the pullup resistors on pins 17-18 (which should not cause a problem since those pins are about to be set to 1 (positive))
1001 B938 OUT 08,R19 Store into PORTC (note that this will immediately turn on the pullup resistors for pins 25-27 which shouldn't matter since they are about to be set to 1)
1002 E32E LDI R18,3E Load value to make pins 15-19 outputs
1002 E34D LDI R20,3D Load value to make pins 23 & 25-28 outputs
1003 B924 OUT 04,R18 Store into DDRB
1003 B947 OUT 07,R20 Store into DDRC
1004 E230 LDI R19,20 Load value to turn on pullup for unused pin 28
1004 EE50 LDI R21,E0 Load value to turn on pullups for unused pins 11-13
1005 B938 OUT 08,R19 Store into PORTC
1005 B95B OUT 0B,R21 Store into PORTD
1006 E14F LDI R20,1F Load value to make pins 23-27 outputs
1006 E16F LDI R22,1F Load value to make pins 2-6 outputs
1007 B947 OUT 07,R20 Store into DDRC
1007 B96A OUT 0A,R22 Store into DDRD
1008 EE53 LDI R21,E3 Load value to turn on pullups for pins 2-3 & unused pins 11-13
1008 EC13 LDI R17,C3 Load value to turn on pullups for pins 14-15 & unused pins 9-10
1009 B95B OUT 0B,R21 Store into PORTD
1009 B915 OUT 05,R17 Store into PORTB
100A rest of program No need to load DDRD since it was reset to 00 when power was turned on
100A rest of program No need to load DDRB since it was reset to 00 when power was turned on


The ATmega328P Microcontroller - 2 Adding a self-test - Sept. 9, 2019 version
If possible, do not connect or disconnect whatever would be damaged if pin 23 was 1 & connect it to Gnd instead if applicable. Once the rest of the circuit is built, turn on a voltmeter, set it on DC volts, & select the lowest range that includes the voltage chosen to power the circuit. Before connecting power to the circuit turn on the power source, select the correct voltage, & measure the output. If the voltage is wrong make sure the power source is plugged into an outlet that has power or test the batteries & check for corroded battery contacts. If the voltage is correct verify which connection is positive & which is negative. Turn off power, connect power to the circuit MAKING SURE THE POSITIVE VOLTAGE IS CONNECTED TO VCC, turn on power, & measure the voltage at pin 7 (Vcc). If it is negative shut off power (& hope the circuit hasn't blow out) because the power is somehow connected backwards, if it is 0 check for a loose connection or short circuit, if it is low check for weak batteries or a short.
The self-test uses the fact that PORT & PIN should normally have the same value for output pins to test for excessive loads. The test should be repeated with each output pin set to both 0 & 1 to detect excessively low resistance to either Vcc or Gnd and adjacent pins should have opposite values to detect shorts between pins. Using the previous example, suppose that pin 1 is reset, 2-6 & 9 are to be inputs with the pullups on for 2-3, 15-19 & 23-28 are to be outputs with 15-16 initially being 0 & 17-18 being 1 and that pin 16 cannot be set to 1 (except under special conditions) and pin 23 is heavily loaded, with pins 10-14 unused. Pin 28 is to be a self-test output (0=fail, 1=pass). Note that the self-test momentarily sets pin 28 to 0 so this must not trigger an error response. Here is the port bit map; 3 values are given for PORTB (the first 2 being the values used for self-testing & the 3rd being the final value that sets the outputs to the required initial values) & 3 values are given for PORTC (the first 2 again being the values used for self-testing, the 2nd is also the final value if the self-test passes (there is no need to change it since pin 28 is already 1 & none of the other outputs need to be set to a specific value), & the 1st or 3rd is the final value if the self-test fails).
If Vcc is correct check the voltage at pin 23. If it is not 0 find & fix the problem and repeat the check. If it is 0 turn off power, connect the rest of the circuit, & turn on power. Check pin 23 again; if it is not 0 shut off power, find & fix the problem, & repeat the check. If it is 0 repeat the check of Vcc. If Vcc is not correct fix the problem, repeat the check of pin 23, & then test Vcc again. If Vcc is correct see if the circuit works.
B7 10 0 1 1 1 unused
 
B6  9 0 0 0 0 in
The ATmega328P Microcontroller - 2 Adding a self-test - Sept. 19, 2019 version
B5 19 0 1 0 out
The self-test works by checking whether the pins have the expected values. Note that the value of PIN should equal the value of PORT for output pins unless a pin is heavily loaded (which should generally be avoided, especially at higher voltage); this is also the case for unused pins with the pullup enabled & unused pins with the pullup off if pulled down to Gnd (i.e., pin 24). Unless it would cause something bad to happen to whatever the microcontroller is controlling, the test should be repeated with each output pin set to both 0 & 1 to detect excessively low resistance to either Vcc or Gnd and adjacent pins should have opposite values to detect shorts between pins. Using the previous example, suppose that pin 1 is reset, 2-6 are outputs, 9-12 are unused, 13 is an output, 14-19 are inputs with the pullup resistors on for 14-15, 23 is an output set to 0, 24 is unused, & 25-28 are outputs with 25-27 set to 1. Pin 6 is heavily loaded, pin 13 is the self-test output (0=fail, 1=pass), pin 15 should initially be 1 & pin 17 0, pin 18 should be 1 after the self-test sets pin 5 to 1, pin 19 should be the same as pin 16, pin 23 must not be 1 & has a resistor connected to Gnd, & pin 24 also has a resistor to Gnd. Note that the self-test momentarily sets pin 13 to 0 so this must not trigger an error response. Here is the port bit map; 3 values are given for PORTC (the first 2 being the values used for self-testing & the 3rd being the final value that sets the outputs to the required initial values) & 3 values are given for PORTD (the first 2 again being the values used for self-testing, the 2nd is also the final value if the self-test passes (there is no need to change it since pin 13 is already 1 & none of the other outputs need to be set to a specific value), & the 1st or 3rd is the final value if the self-test fails).
B4 18 1  1 0 1 out, =1
B7 10 0 1 unused
B3 17 0 1 1 out, =1
B6  9 0 1 unused
B2 16 0 0 0 out, =0, cannot be set to 1
B5 19 0 0 in, initially same as B2
B1 15 0 1 0 out, =0
B4 18 0 0 in, =1 after self-test sets pin 5=1
B0 14 0 1 1 1 unused  DDRB=3E PORTB=91,AB,99
B3 17 0 0 in, initially 0
B2 16 0 0 in, initially same as B5
B1 15 0 1 in, pullup on, initially 1
B0 14 0 1 in, pullup on  DDRB=00 PORTB=C3
C6  1 0  0 0 0 reset
C6  1 0  0 0 0 reset
C5 28 1  0 1 0 out, self-test
C5 28 1  0 1 0 out
C4 27 1  1 0 0 out
C4 27 1  1 0 1 out, =1
C3 26 1  0 1 1 out
C3 26 1  0 1 1 out, =1
C2 25 1  1 0 0 out
C2 25 1  1 0 1 out, =1
C1 24 1 0 1 1 out
C1 24 0 0 0 0 unused, resistor to Gnd
C0 23 1  1 0 0 out, heavy load  DDRC=3F PORTC=15,2A,0A
C0 23 1  0 0 0 out, =0, must not be 1, resistor to Gnd  DDRC=3D PORTC=14,28,1C
D7 13 0 1 unused
D7 13 0 1 0 out, self-test
D6 12 0  1 unused
D6 12 0  1 1 1 unused
D5 11 0  1 unused
D5 11 0  1 1 1 unused
D4  6 0 0 in
D4  6 1  1 0 0 out, heavy load
D3  5 0 0 in
D3  5 1 0 1 1 out
D2  4 0 0 in
D2  4 1  1 0 0 out
D1  3 0 1 in, pullup on
D1  3 0 1 1 out
D0  2 0 1 in, pullup on  DDRD=00 PORTD=E3
D0  2 1 1 0 0 out  DDRD=9F PORTD=75,EA,6A
The following code fragment does the self-test and sets up the pins.
The following code fragment does the self-test and sets up the pins.
0000 940C JMP
0000 940C JMP
0001 1000 1000
0001 1000 1000
...
...
1000 E911 LDI R17,91 Load value to turn on pullups for unused pins 10 & 14 and do 1st B test (B2 can't be 1)
1000 EC13 LDI R17,C3 Load value to turn on pullups for 14-15 & unused 9-10
1001 B915 OUT 05,R17 Store into PORTB
1001 B915 OUT 05,R17 Store into PORTB
1002 E32E LDI R18,3E Load value to make pins 15-19 outputs
1002 E134 LDI R19,14 Load value to do 1st C test
1003 B924 OUT 04,R18 Store into DDRB
1003 B938 OUT 08,R19 Store into PORTC
1004 E135 LDI R19,15 Load value to do 1st C test
1004 E34D LDI R20,3D Load value to make 23 & 25-28 outputs
1005 B938 OUT 08,R19 Store into PORTC
1005 B947 OUT 07,R20 Store into DDRC
1006 E34F LDI R20,3F Load value to make pins 23-28 outputs
1006 E755 LDI R21,75 Load value to do 1st D test
1007 B947 OUT 07,R20 Store into DDRC
1007 B95B OUT 0B,R21 Store into PORTD
1008 EE53 LDI R21,E3 Load value to turn on pullups for pins 2-3 & unused pins 11-13
1008 E96F LDI R22,9F Load value to make 2-6 & 13 outputs
1009 B95B OUT 0B,R21 Store into PORTD
1009 B96A OUT 0A,R22 Store into DDRD
100A B176 IN R23,06 Load temporary register from PINC
100A B176 IN R23,06 Load temporary register from PINC
100B 2773 EOR R23,R19 Clear bits where PINC=PORTC
100B 2773 EOR R23,R19 Clear bits where PINC=PORTC
100C 737E ANDI R23,3E Clear heavily loaded C0 (& the bits that aren't outputs, but they're already 0 anyway)
100C F4E1 BRNE 1C PINC not same as PORTC, self-test failed, pin 13 already 0, go to 1029 (note that the branch distance is relative to the following instruction so this jumps ahead 1D)
100D F489 BRNE 11 PINC outputs besides C0 not same as PORTC, self-test failed, pin 28 already 0, go to 101F (note that the branch distance is relative to the following instruction so this jumps ahead 12)
100D B179 IN R23,09 Load from PIND
100E B173 IN R23,03 Load from PINB
100E 2775 EOR R23,R21 Clear bits where PIND=PORTD
100F 2771 EOR R23,R17 Clear bits where PINB=PORTB
100F 7E7F ANDI R23,EF Clear heavily loaded D4
1010 2372 AND R23,R18 Clear bits that aren't outputs (ANDI R23,3E would work equally well)
1010 F401 BRNE 18 PIND outputs not same as PORTD besides D4, fail, pin 13 already 0, go to 1029
1011 F469 BRNE 0D PINB outputs not same as PORTB, fail, pin 28 already 0, go to 101F
1011 B173 IN R23,03 Load from PINB
1012 EA1B LDI R17,AB Load value that reverses B output pins except pin 16
1012 7E7E ANDI R23,EE Clear unpredictable bits B0 & B4
1013 B915 OUT 05,R17 Store into PORTB
1013 3C72 CPI R23,C2 Test for pass with B2 & B5=0
1014 E23A LDI R19,2A Load value that reverses C output pins
1014 F011 BREQ 02 Pass, go to 1017
1015 B938 OUT 08,R19 Store into PORTC
1015 3E76 CPI R23,E6 Test for pass with B2 & B5=1
1016 B173 IN R23,03 Load from PINB (note that PINC can't be tested yet because of 1 instruction delay)
1016 F491 BRNE 12 PORTB wrong, fail, pin 13 already 0, go to 1029
1017 2771 EOR R23,R17 Clear bits where PINB=PORTB
1017 E238 LDI R19,28 Load value to reverse C outputs except C0
1018 2372 AND R23,R18 Clear bits that aren't outputs
1018 B938 OUT 08,R19 Store into PORTC
1019 F421 BRNE 04 PINB outputs not same as PORTB, fail, go to 101E
1019 EE5A LDI R21,EA Load value to reverse D outputs
101A B176 IN R23,06 Load from PINC
101A B95B OUT 0B,R21 Store into PORTD
101B 2773 EOR R23,R19 Clear bits where PINC=PORTC
101B B176 IN R23,03 Load from PINC (note that PIND couldn't be tested yet because of 1 instruction delay)
101C 737E ANDI R23,3E Clear bit C0
101C 2773 EOR R23,R19 Clear bits where PINC=PORTC
101D F009 BREQ 01 PINC=PORTC, pass, no need to test port D since none of the pins are outputs, pin 28 already 1, go to 101F
101D F451 BRNE 0A PINC outputs not same as PORTC, fail, go to 1028
101E 9845 CBI 08,5 Set pin 28 to 0
101E B179 IN R23,09 Load from PIND
101F E919 LDI R17,99 Load value to turn on pullups for unused pins 10 & 14, set 15-16 to 0, & 17-18 to 1
101F 2775 EOR R23,R21 Clear bits where PIND=PORTD
1020 B915 OUT 05,R17 Store into PORTB
1020 7E7F ANDI R23,EF Clear heavily loaded bit D4
1021... rest of program
1021 F431 BRNE 06 PIND not same as PORTD, fail, go to 1028
To turn on an LED if the self-test passes (recommended because it indicates that the initialization routine executed) connect the positive LED lead to pin 28 & the negative lead thru a resistor (the value depends on the voltage used) to Gnd.
1022 B173 IN R23,03 Load from PINB
To do more than just set pin 28 if the self-test fails, add additional code after 101E & change the value of BREQ at 101D to jump past the end of the added code. For example, this alternate ending will halt the program at the point the self-test failed to make it easier to find (as described below) the problem:
1023 7F7E ANDI R23,FE Clear unpredictable bit B0
101D F011 BREQ 02 Pass, go to 1020 (increased by 1 since 1 instruction added)
1024 3D72 CPI R23,D2 Test for pass with B2 & B5=0
101E 9845 CBI 08,5 Set pin 28 to 0
1025 F019 BREQ 03 Self-test passes, pin 13=1, go to 1029
101F CFFF RJMP FFF Go to 101F (halt by going into infinite loop)
1026 3F76 CPI R23,F6 Test for pass with B2 & B5=1
1020 E919 LDI R17,99 Load B value
1027 F009 BREQ 01 Self-test passes, pin 13=1, go to 1029
1021 B915 OUT 05,R17 Store into PORTB
1028 985F CBI 0B,7 Set pin 13 to 0
1022... rest of program
1029 E13C LDI R19,1C Load value to set 23=0 & 25-27=1
If the self-test does not pass, check for other problems. Measure the voltage (unless otherwise stated, always measure voltages relative to Gnd (pins 8 & 22)) on pin 7 (Vcc); if it is negative power is connected backwards, if it is 0 measure the outputs of the power source directly. If the power source voltage is 0 make sure it is turned on and test the batteries & check for corroded battery contacts or make sure it is plugged in to an outlet that has power. If the power source voltage is correct there is a wiring error or the microcontroller board is faulty. If the pin 7 voltage is correct measure the voltage at pin 28; if it is not 0 test the resistor & LED, make sure the LED isn't backwards, & check for a wiring error. If pin 28 is 0 connect a 1k resistor from pin 28 to Vcc; if it now reads >1V the pin apparently was not initialized to an output, check the program for errors (beware typos) & make sure it was loaded into the microcontroller correctly. If it still reads 0 the self-test really did fail.
102A B938 OUT 08,R19 Store into PORTC
Check the voltages on all of the output pins. Suppose that the supply is 3V & the voltages are 0,3,0,3,0,1 at pins 28-23 & 3,3,0,0,0 at 19-15. The odd value at 23 is not unexpected because of the heavy load and 28-24 match the 1st test value for port C indicating that the first part of the self-test (at 100D) passed (but the test failed before PORTC was reloaded at 1015). Pins 19-15 match the 1st value for port B except for pin 19, which should be 0 but was 3V. This indicates that pin 19 is shorted to Vcc, check the wiring to that pin. On the other hand, if 19-15 were 2,2,0,0,0 that would indicate that pins 19 & 18 are shorted together.
102B... rest of program
To turn on an LED if the self-test passes (recommended because it indicates that the initialization routine executed) connect the positive LED lead to pin 28 & the negative lead thru a resistor (the value depends on the voltage used) to Gnd. However, the LED may be too dim to see if the voltage is under 2V (test by connecting it directly from Vcc to Gnd (no resistor needed if under 2V)).
To do more than just set pin 13 if the self-test fails, add additional code after 1028 & change the value of BREQ at 1025 & 1027 to jump past the end of the added code. For example, this alternate ending will halt the program at the point the self-test failed to make it easier to find (as described below) the problem:
1025 F021 BREQ 04 Pass, go to 102A (increased by 1 since 1 instruction added)
1026 3E76 CPI R23,E6 Test for pass with B2 & B5=1
1027 F011 BREQ 02 Pass, go to 102A (increased by 1)
1028 985F CBI 0B,7 Set pin 13 to 0
1029 CFFF RJMP FFF Go to 1029 (halt by going into infinite loop)
102A E13C LDI R19,1C Load C value
102B B938 OUT 08,R19 Store into PORTC
102C... rest of program
(In this particular case only, it makes sense to also change the BRNE at 100C,1010,& 1016 to F7F9 BRNE 7F to halt the program at those points.)
 
After all the tests described previously pass, if the self-test does not pass measure the voltage at pin 28; if it is not 0 test the resistor & LED, make sure the LED isn't backwards, & check for a wiring error. If pin 28 is 0 connect a 2.7k resistor from pin 28 to Vcc; if it now reads >1V the pin apparently was not initialized to an output, check the program for errors (beware typos) & make sure it was loaded into the microcontroller correctly. If it still reads 0 the self-test really did fail.
Check the voltages on all of the pins. Suppose that the supply is 3V & the voltages on pins 2-6 are 3,0,0,0,2, on 9-19 are 3,3,3,3,0,0,3,3,0,0,3, & 23-28 are 0,0,3,0,3,0. The odd value at pin 6 is not unexpected because of the heavy load and 23-28 match the 1st test value for port C indicating that the first part of the self-test (at 100C) passed (but the test failed before PORTC was reloaded at 1018). Pins 2-6 & 11-13 match the 1st value for port B except for pin 4 (B2), which should be 3V but was 0. This indicates that pin 4 is shorted to Gnd, check the wiring to that pin. On the other hand, if 2-6 were 3,1,1,0,2 that would indicate that pins 3 & 4 are shorted together. Fix the problem & try again.
If the self-test passes see if the circuit works.
</nowiki>
</nowiki>

Revision as of 02:37, 20 September 2019

This is an attempt to write documentation for the ATmega328P family of microcontrollers (and learn it myself as I go along). Send any corrections, suggestions, or other comments to [email protected] The ATmega328P Microcontroller - 1 Setting up the I/O pins - Sept. 19, 2019 version Note: In the following, the more negative voltage (Gnd) is 0 & the more positive voltage (Vcc) is 1. Addresses & data are given in hexadecimal. The ATmega328P is one of a large family of similar 8-bit microcontrollers and can operate on 1.8 to 5.5 V. The data sheet is available at ww1.microchip.com/downloads/en/DeviceDoc/ATmega48A-PA-88A-PA-168A-PA-328-P-DS-DS40002061A.pdf (in the following the section describing an item is often given afterwards in parentheses, i.e. SREG(7.3.1)); the instruction set manual for this family is available at ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf (note that the 328P does not have all of the instructions listed) & also see en.wikipedia.org/wiki/Atmel_AVR_instruction_set. Other members differ in various ways; consult their data sheets for details. The ATmega328P has 32 8-bit general purpose registers (which can also be addressed as data memory locations 0000-001F), 224 8-bit I/O registers (which can also be addressed as data memory locations 0020-00FF; note that 20 must therefore be added to the I/O register number when addressing it as memory), 2kx8 data RAM(8.3) (data memory locations 0100-08FF), a separate 16kx16 program flash memory(8.2) (note that while it is an 8-bit chip the instructions are 16 bits), & a separate 1kx8 EEPROM(8.4). The ATmega328P is available as a 28-pin DIP (more exotic packages are also avilable) with 23 general purpose I/O pins which can also have specialized functions(14.3.1-14.3.3); in particular, pin 1 is an external reset(11.4) unless RSTDISBL(28.2) has been programmed to 0. Altho each pin can be individually controlled, they are grouped into the 8 bit port B (B0-B7), 7 bit port C (C0-C6), & 8 bit port D (D0-D7). 1=C6 B7=10 2=D0 B6=9 3=D1 B5=19 4=D2 B4=18 5=D3 B3=17 6=D4 B2=16 9=B6 B1=15 10=B7 B0=14 11=D5 C6=1 12=D6 C5=28 13=D7 C4=27 14=B0 C3=26 15=B1 C2=25 16=B2 C1=24 17=B3 C0=23 18=B4 D7=13 19=B5 D6=12 23=C0 D5=11 24=C1 D4=6 25=C2 D3=5 26=C3 D2=4 27=C4 D1=3 28=C5 D0=2 There are 3 I/O registers associated with each port: DDR(14.4.3, 14.4.6, 14.4.9) (data direction register), PORT(14.4.2, 14.4.5, 1.4.8), & PIN(14.4.4, 14.4.7, 14.4.10). The DDR determines whether the pins are inputs or outputs; if a bit in the DDR is 0 the corresponding pin is an input; if 1 it is an output. When the chip is reset (which happens automatically when power is turned on) all the DDR & PORT bits are cleared making all pins inputs; bit 6 if pin 1 is reset & unused bit 7 of DDRC, PORTC, & PINC are always 0. The PORT register contains the value that is output on the corresponding pins that are set as outputs. Note that if a bit corresponding to an input pin is set to 1 an internal "pullup" resistor is connected from the positve voltage to that pin unless PUD(14.4.1) has been set to 1. Reading the PIN register gives the value of the corresponding pins (regardless of whether they are an input or output). Note that unconnected inputs are not defined and may give erratic values when read. Unconnected inputs can also cause high power consumption so unused inputs(14.2.6) should be connected to something; this is easily done by turning on the pullup resistors. Also note that there is a 1 instruction delay(14.2.4) between writing data to the PORT and having it appear in the PIN register. Other than that, the value of PORT should be the same as PIN at those positions that are outputs except for pins that are heavily loaded (which is best avoided, at least at higher operating voltages). This can be used to do a limited self-test of the circuit. Note that while writing 0 to a PIN bit does nothing, writing a 1 will cause the corresponding bit in PORT to change state(14.2.2) (from 1 to 0 or from 0 to 1). Here are the I/O addresses for the various registers: 03 PINB 04 DDRB 05 PORTB 06 PINC 07 DDRC 08 PORTC 09 PIND 0A DDRD 0B PORTD Suppose that pin 1 is reset, 2-6 are outputs, 9-13 are unused, 14-19 are inputs with the pullup resistors on for 14-15, 23 is an output set to 0, 24 is unused, & 25-28 are outputs with 25-27 set to 1. Also suppose that setting pin 23 to 1 (except under special conditions) could damage the circuit; to reduce the chance of this happening this output is placed next to Gnd (pin 22), a resistor (~5k ohms suggested) is connected from the pin to Gnd, & the other adjacent pin (24) is unused & is connected thru a "pulldown" resistor (1k suggested) to Gnd instead of using the internal pullup. Here is the port bit map (the port bit is given first followed by the corresponding pin, the value of the DDR for that position, the value (if the value doesn't matter it is generally set to 0 in these examples) of the PORT for that position, & a description; after the last bit of a port the hexadecimal value of DDR & PORT is shown): B7 10 0 1 unused (set PORT to 1 for unused pins to turn on pullup resistor) B6 9 0 1 unused B5 19 0 0 in B4 18 0 0 in B3 17 0 0 in B2 16 0 0 in B1 15 0 1 in, pullup on B0 14 0 1 in, pullup on DDRB=00 PORTB=C3 C6 1 0 0 reset C5 28 1 0 out C4 27 1 1 out, =1 C3 26 1 1 out, =1 C2 25 1 1 out, =1 C1 24 0 0 unused, resistor to Gnd C0 23 1 0 out, =0, must not be 1, resistor to Gnd DDRC=3D PORTC=1C D7 13 0 1 unused D6 12 0 1 unused D5 11 0 1 unused D4 6 1 0 out D3 5 1 0 out D2 4 1 0 out D1 3 1 0 out D0 2 1 0 out DDRD=1F PORTD=E0 The following code fragment sets up the pins (in all code examples the program memory address is given first (4 hexadecimal digits) followed by the opcode (4 digits), the instruction, & an explanation). When power is turned on the processor resets and starts at address 0000(11.1). However, other things can cause execution to start nearby (for example, INT0 can cause execution to start at 0002(12.4)) so the first instruction is often a jump to get out of the way. 0000 940C JMP 0001 1000 1000 ... 1000 E13C LDI R19,1C Load value to set pin 23=0 & pins 25-27=1 into arbitrarily chosen temporary register 1001 B938 OUT 08,R19 Store into PORTC (note that this will immediately turn on the pullup resistors for pins 25-27 which shouldn't matter since they are about to be set to 1) 1002 E34D LDI R20,3D Load value to make pins 23 & 25-28 outputs 1003 B947 OUT 07,R20 Store into DDRC 1004 EE50 LDI R21,E0 Load value to turn on pullups for unused pins 11-13 1005 B95B OUT 0B,R21 Store into PORTD 1006 E16F LDI R22,1F Load value to make pins 2-6 outputs 1007 B96A OUT 0A,R22 Store into DDRD 1008 EC13 LDI R17,C3 Load value to turn on pullups for pins 14-15 & unused pins 9-10 1009 B915 OUT 05,R17 Store into PORTB 100A rest of program No need to load DDRB since it was reset to 00 when power was turned on If possible, do not connect or disconnect whatever would be damaged if pin 23 was 1 & connect it to Gnd instead if applicable. Once the rest of the circuit is built, turn on a voltmeter, set it on DC volts, & select the lowest range that includes the voltage chosen to power the circuit. Before connecting power to the circuit turn on the power source, select the correct voltage, & measure the output. If the voltage is wrong make sure the power source is plugged into an outlet that has power or test the batteries & check for corroded battery contacts. If the voltage is correct verify which connection is positive & which is negative. Turn off power, connect power to the circuit MAKING SURE THE POSITIVE VOLTAGE IS CONNECTED TO VCC, turn on power, & measure the voltage at pin 7 (Vcc). If it is negative shut off power (& hope the circuit hasn't blow out) because the power is somehow connected backwards, if it is 0 check for a loose connection or short circuit, if it is low check for weak batteries or a short. If Vcc is correct check the voltage at pin 23. If it is not 0 find & fix the problem and repeat the check. If it is 0 turn off power, connect the rest of the circuit, & turn on power. Check pin 23 again; if it is not 0 shut off power, find & fix the problem, & repeat the check. If it is 0 repeat the check of Vcc. If Vcc is not correct fix the problem, repeat the check of pin 23, & then test Vcc again. If Vcc is correct see if the circuit works. The ATmega328P Microcontroller - 2 Adding a self-test - Sept. 19, 2019 version The self-test works by checking whether the pins have the expected values. Note that the value of PIN should equal the value of PORT for output pins unless a pin is heavily loaded (which should generally be avoided, especially at higher voltage); this is also the case for unused pins with the pullup enabled & unused pins with the pullup off if pulled down to Gnd (i.e., pin 24). Unless it would cause something bad to happen to whatever the microcontroller is controlling, the test should be repeated with each output pin set to both 0 & 1 to detect excessively low resistance to either Vcc or Gnd and adjacent pins should have opposite values to detect shorts between pins. Using the previous example, suppose that pin 1 is reset, 2-6 are outputs, 9-12 are unused, 13 is an output, 14-19 are inputs with the pullup resistors on for 14-15, 23 is an output set to 0, 24 is unused, & 25-28 are outputs with 25-27 set to 1. Pin 6 is heavily loaded, pin 13 is the self-test output (0=fail, 1=pass), pin 15 should initially be 1 & pin 17 0, pin 18 should be 1 after the self-test sets pin 5 to 1, pin 19 should be the same as pin 16, pin 23 must not be 1 & has a resistor connected to Gnd, & pin 24 also has a resistor to Gnd. Note that the self-test momentarily sets pin 13 to 0 so this must not trigger an error response. Here is the port bit map; 3 values are given for PORTC (the first 2 being the values used for self-testing & the 3rd being the final value that sets the outputs to the required initial values) & 3 values are given for PORTD (the first 2 again being the values used for self-testing, the 2nd is also the final value if the self-test passes (there is no need to change it since pin 13 is already 1 & none of the other outputs need to be set to a specific value), & the 1st or 3rd is the final value if the self-test fails). B7 10 0 1 unused B6 9 0 1 unused B5 19 0 0 in, initially same as B2 B4 18 0 0 in, =1 after self-test sets pin 5=1 B3 17 0 0 in, initially 0 B2 16 0 0 in, initially same as B5 B1 15 0 1 in, pullup on, initially 1 B0 14 0 1 in, pullup on DDRB=00 PORTB=C3 C6 1 0 0 0 0 reset C5 28 1 0 1 0 out C4 27 1 1 0 1 out, =1 C3 26 1 0 1 1 out, =1 C2 25 1 1 0 1 out, =1 C1 24 0 0 0 0 unused, resistor to Gnd C0 23 1 0 0 0 out, =0, must not be 1, resistor to Gnd DDRC=3D PORTC=14,28,1C D7 13 1 0 1 0 out, self-test D6 12 0 1 1 1 unused D5 11 0 1 1 1 unused D4 6 1 1 0 0 out, heavy load D3 5 1 0 1 1 out D2 4 1 1 0 0 out D1 3 1 0 1 1 out D0 2 1 1 0 0 out DDRD=9F PORTD=75,EA,6A The following code fragment does the self-test and sets up the pins. 0000 940C JMP 0001 1000 1000 ... 1000 EC13 LDI R17,C3 Load value to turn on pullups for 14-15 & unused 9-10 1001 B915 OUT 05,R17 Store into PORTB 1002 E134 LDI R19,14 Load value to do 1st C test 1003 B938 OUT 08,R19 Store into PORTC 1004 E34D LDI R20,3D Load value to make 23 & 25-28 outputs 1005 B947 OUT 07,R20 Store into DDRC 1006 E755 LDI R21,75 Load value to do 1st D test 1007 B95B OUT 0B,R21 Store into PORTD 1008 E96F LDI R22,9F Load value to make 2-6 & 13 outputs 1009 B96A OUT 0A,R22 Store into DDRD 100A B176 IN R23,06 Load temporary register from PINC 100B 2773 EOR R23,R19 Clear bits where PINC=PORTC 100C F4E1 BRNE 1C PINC not same as PORTC, self-test failed, pin 13 already 0, go to 1029 (note that the branch distance is relative to the following instruction so this jumps ahead 1D) 100D B179 IN R23,09 Load from PIND 100E 2775 EOR R23,R21 Clear bits where PIND=PORTD 100F 7E7F ANDI R23,EF Clear heavily loaded D4 1010 F401 BRNE 18 PIND outputs not same as PORTD besides D4, fail, pin 13 already 0, go to 1029 1011 B173 IN R23,03 Load from PINB 1012 7E7E ANDI R23,EE Clear unpredictable bits B0 & B4 1013 3C72 CPI R23,C2 Test for pass with B2 & B5=0 1014 F011 BREQ 02 Pass, go to 1017 1015 3E76 CPI R23,E6 Test for pass with B2 & B5=1 1016 F491 BRNE 12 PORTB wrong, fail, pin 13 already 0, go to 1029 1017 E238 LDI R19,28 Load value to reverse C outputs except C0 1018 B938 OUT 08,R19 Store into PORTC 1019 EE5A LDI R21,EA Load value to reverse D outputs 101A B95B OUT 0B,R21 Store into PORTD 101B B176 IN R23,03 Load from PINC (note that PIND couldn't be tested yet because of 1 instruction delay) 101C 2773 EOR R23,R19 Clear bits where PINC=PORTC 101D F451 BRNE 0A PINC outputs not same as PORTC, fail, go to 1028 101E B179 IN R23,09 Load from PIND 101F 2775 EOR R23,R21 Clear bits where PIND=PORTD 1020 7E7F ANDI R23,EF Clear heavily loaded bit D4 1021 F431 BRNE 06 PIND not same as PORTD, fail, go to 1028 1022 B173 IN R23,03 Load from PINB 1023 7F7E ANDI R23,FE Clear unpredictable bit B0 1024 3D72 CPI R23,D2 Test for pass with B2 & B5=0 1025 F019 BREQ 03 Self-test passes, pin 13=1, go to 1029 1026 3F76 CPI R23,F6 Test for pass with B2 & B5=1 1027 F009 BREQ 01 Self-test passes, pin 13=1, go to 1029 1028 985F CBI 0B,7 Set pin 13 to 0 1029 E13C LDI R19,1C Load value to set 23=0 & 25-27=1 102A B938 OUT 08,R19 Store into PORTC 102B... rest of program To turn on an LED if the self-test passes (recommended because it indicates that the initialization routine executed) connect the positive LED lead to pin 28 & the negative lead thru a resistor (the value depends on the voltage used) to Gnd. However, the LED may be too dim to see if the voltage is under 2V (test by connecting it directly from Vcc to Gnd (no resistor needed if under 2V)). To do more than just set pin 13 if the self-test fails, add additional code after 1028 & change the value of BREQ at 1025 & 1027 to jump past the end of the added code. For example, this alternate ending will halt the program at the point the self-test failed to make it easier to find (as described below) the problem: 1025 F021 BREQ 04 Pass, go to 102A (increased by 1 since 1 instruction added) 1026 3E76 CPI R23,E6 Test for pass with B2 & B5=1 1027 F011 BREQ 02 Pass, go to 102A (increased by 1) 1028 985F CBI 0B,7 Set pin 13 to 0 1029 CFFF RJMP FFF Go to 1029 (halt by going into infinite loop) 102A E13C LDI R19,1C Load C value 102B B938 OUT 08,R19 Store into PORTC 102C... rest of program (In this particular case only, it makes sense to also change the BRNE at 100C,1010,& 1016 to F7F9 BRNE 7F to halt the program at those points.) After all the tests described previously pass, if the self-test does not pass measure the voltage at pin 28; if it is not 0 test the resistor & LED, make sure the LED isn't backwards, & check for a wiring error. If pin 28 is 0 connect a 2.7k resistor from pin 28 to Vcc; if it now reads >1V the pin apparently was not initialized to an output, check the program for errors (beware typos) & make sure it was loaded into the microcontroller correctly. If it still reads 0 the self-test really did fail. Check the voltages on all of the pins. Suppose that the supply is 3V & the voltages on pins 2-6 are 3,0,0,0,2, on 9-19 are 3,3,3,3,0,0,3,3,0,0,3, & 23-28 are 0,0,3,0,3,0. The odd value at pin 6 is not unexpected because of the heavy load and 23-28 match the 1st test value for port C indicating that the first part of the self-test (at 100C) passed (but the test failed before PORTC was reloaded at 1018). Pins 2-6 & 11-13 match the 1st value for port B except for pin 4 (B2), which should be 3V but was 0. This indicates that pin 4 is shorted to Gnd, check the wiring to that pin. On the other hand, if 2-6 were 3,1,1,0,2 that would indicate that pins 3 & 4 are shorted together. Fix the problem & try again. If the self-test passes see if the circuit works.