MICROCHIP AN247 Technical data

AN247
A CAN Bootloader for PIC18F CAN Microcontrollers
Author: Ross M. Fosler
Microchip Technology Inc.

INTRODUCTION

Among the many features built into Microchip’s Enhanced FLASH Microcontroller devices is the capa­bility of the program memory to self-program. This very useful feature has been deliberately included to give the user the ability to perform bootloading operations. Devices like the PIC18F458 are designed with a desig­nated “boot block”, a small section of protectable pro­gram memory allocated specifically for bootload firmware.
This application note demonstrates a simple boot­loader implementation for the PIC18F families of micro­controllers with a CAN module. The goals of this implementation are to stress maximum performance and functionality, while requiring a minimum of code space. For users developing CAN enabled systems, it provides a low level framework that can be used with higher level network protocols to develop more complex and custom-tailored systems.

CONSIDERATIONS FOR FIELD PROGRAMMING OVER THE CAN BUS

The combination of FLASH technology and robust net­work communication capability in a single device makes over-the-network programmability a very desir­able option. However, this makes bootloading on a CAN bus network a very different challenge from more typical uses, such as using a bootloader to program a single FLASH device in isolation. Let’s consider some of the key issues in over-the-network programming.

Single or Group Programming

Providing bootloading capability over a CAN bus net­work takes some forethought. For example, a system with a number of nodes may have identical firmware in several nodes. Since every node on a CAN bus can see all passing data, it may be more efficient to program these identical nodes in a single pass.
However, in other cases where a node or many nodes are unique, it may only be necessary to open peer-to-peer communications to program the device. This can be the simplest programming system, because the programming source could contain all the intelligence and freely manipulate the target memory.
The drawback to this is a lack of efficiency, as directly manipulating the target memory and manually verifying data takes significant time on the CAN bus.
To make the operation more efficient, the programming target could be given some intelligence, like self­verification. This would make communications unidirectional, essentially cutting the time on the CAN bus in half.
Overall, the best savings is to design all the nodes in the system with similar, modular firmware. Each node could then use only those modules required for its task, but the entire group of nodes could be updated simul­taneously. The sacrifice here is program memory over­head, since some nodes may have resident firmware that is not used.

Programming a Running System

An interesting situation is bootloading in an active and functioning system. In this instance, one or more of the nodes are taken off-line to update their firmware, yet the functionality of the entire system is not completely disabled. This, of course, requires that the target node or nodes have some functional independence from other parts of the networked system.
There are priority issues to contend with when pro­gramming in an active system. For example, what pri­ority can be given to the bootloader without affecting the critical communications in the system? If higher pri­ority is given to nodes running the bootloader than other nodes running their normal application, then it may take time for data to be received when data is being streamed to the programming target. Thus, criti­cal systems that require relatively low latency for data transmission or reception may fail to function as expected. In the opposite situation, assigning the pro­gramming target with a priority that is too low could lead to extremely long programming times, simply because the programming source and target are continually waiting for an IDLE bus to pass data.
In an active network, planning is necessary to provide sufficient bus time for programming. One solution is simply to give relatively high priority to bootloader pro­gramming operations, then design the programming source to “inject” time for other applications while streaming data on the CAN bus. Thus, bus time is always available and controlled by the programming source.
2003 Microchip Technology Inc. DS00247A-page 1
AN247
Even with careful planning, there may be situations where safety is actually compromised as a result of bus contention. In these cases, the best option may be to put all nodes in the network into a “Configuration” mode and shut down all system functions.

Boot Mode Entry

Boot mode entry is determined by an event. This could be a hardware event, such as pressing one or more buttons after a device RESET. It could also be a net­work event, such as a special set of data that tells a device to enter Boot mode. One example is a network boot ID that is mapped directly into the CAN ID. Then the key, along with specific target information, could be embedded in the data field of a CAN frame. The key information could put one or more nodes into Boot mode.

BOOTLOADER FIRMWARE

Basic Operation Overview

An overview of the CAN bootloader’s operation is shown in Figure 1. A CAN Message Identifier and data is received through the CAN module. One bit in the identifier is used to indicate whether to PUT or GET data. Another is used to determine if the message is to be interpreted as data to be programmed or bootloader control information. Writing data automatically invokes the appropriate function to write to memory (FLASH, Data EEPROM, or Configuration Memory). Writing to the Control registers sets the operation of the bootloader.
The bootloader can be configured at build time to sup­port one of two mutually exclusive modes of operation. In P Mode (or Put-only) mode, the microcontroller only accepts PUT commands, and never “talks back” to the source. In PG Mode, both PUT and GET commands are accepted, allowing the source to both read from and write to the target’s memory.
A more detailed explanation is provided in subsequent sections.
FIGURE 1: BOOTLOADER FUNCTIONAL
BLOCK DIAGRAM
TXRX
Bootloader Firmware
CAN
Module
(Msg Identifier)
Configuration
Data
CONTROL/DATA
PUT/GET
FLASH
Program
Memory
EE
Data
Memory
Memory
Control/Data Buffer
Bootloader
Control
Registers
D8
D0
Memory I/O
Logic
FIGURE 2: PROGRAM MEMORY MAP OF
THE PIC18F458
Boot Program
RESET Vector
High Priority Interrupt Vector
Low Priority Interrupt Vector
0000h
0200h
0208h
0218h

Memory Organization

Program Memory
User Memory Space

PROGRAM MEMORY USAGE

Currently, PIC18F devices reserve the first 512 bytes of Program Memory as the boot block. Future devices may expand this, depending on application require­ments for these devices. This bootloader is designed to occupy the current designated boot block of 512 bytes (or 256 words) of memory using the recommended options. Note, however, some compile time options can grow the bootloader beyond the boot block. Figure 2
Note: Memory areas not shown to scale.
shows a memory map of the PIC18F458. The boot area can be code protected to prevent accidental overwriting of the boot program.
DS00247A-page 2 2003 Microchip Technology Inc.
7FFFh
AN247

REMAPPED MEMORY AND VECTORS

Since the hardware RESET and interrupt vectors lie within the boot area and cannot be edited if the block is protected, they are remapped through software to the nearest parallel locations outside the boot block. Remapping is simply a branch for interrupts, so PIC18F users should note an additional latency of 2 instruction cycles to handle interrupts. Upon RESET, there are some boot condition checks, so the RESET latency is an additional 10 instruction cycles (as seen in the example source code).
Notice the memory regions do not necessarily correlate to the physical addresses in the device (see Figure 3). For example, EEDATA is located at F00000h; however, in the PIC18 device, EEDATA operates as a separate module and is not located in the device memory map. In addition, the regions only define where the boot­loader operates and the type of memory that it operates on. This should not be interpreted as meaning that writable memory is available over the entire defined memory areas.
FIGURE 3: BOOTLOADER MEMORY
REGIONS
Boot Program (000h - 1FFh)
Program Memory
Config Memory
Unused
000000h
2FFFFFh 300000h
3FFFFFh
FIGURE 4: DATA MEMORY MAP
000h
EE Data
Memory
Boot Control Byte
XXXh

COMMUNICATION AND CONTROL PROTOCOL

From the functional view in Figure 1, the bootloader looks and behaves like a hardware module. This is mostly because the bootloader’s operation is dictated by two “commands” derived from single bit values, as well as a set of defined Control registers.

Basic Bootloader Commands

There are essentially two data control commands: PUT and GET. These commands are implemented through a single bit passed via the CAN Message Identifier field (in this version, bit 1 of the 18-bit Extended Identifier field); the command is PUT when the bit is ‘0’, and GET when it is ‘1’. PUT or GET can operate on either a type of memory or the Control register set. GET commands are ignored if P-Mode is specified.
The CONTROL/DATA bit, also defined in the Identifier field (in this version, bit 0 of the Extended Identifier), indicates the destination of the frame data. When the bit is ‘0’, the data is interpreted as Control register content; when it is ‘1’, the data is programming data.
The bit assignments for PUT/GET and CONTROL/DATA are arbitrary, and are defined by compile time definitions. The user may change the locations of these bits in the identifier as the application requires.
F00000h
EEPROM Data
FFFFFFh
Note: Memory areas not shown to scale.

Control Registers

There are eight Control registers, which represent the maximum number of bytes that can be contained in the data field of a single CAN frame. The registers are shown in order in Figure 5.

FIGURE 5: CONTROL REGISTERS

DATA MEMORY USAGE

The last location in Data Memory of the device (Figure 4) is reserved as a non-volatile Boot mode flag. This location contains FFh by default, which indicates Boot mode. Any other value in this location indicates normal Execution mode.
2003 Microchip Technology Inc. DS00247A-page 3
Address Low
Address High
Address Upper
(Reserved)
Control Bits
Command
Data A Data B
D0 D1 D2 D3 D4 D5 D6 D7
AN247
The Control registers are:
Address Low (D0): This contains the low order byte of the address pointer.
Address High (D1): This register contains the middle byte of the address pointer.
Address Upper (D2): This register contains the high order byte of the address pointer.
Reserved (D3): This register is reserved for expanded addressing.
Control Bits (D4): This register contains bits that define the basic operation of the bootloader.
Command (D5): This location contains the imme­diate bootloader command. This is available to allow special functions.
Data A and Data B (D6 and D7): These Data registers are reserved for expansions of the bootloader command set.

Address Information

Control registers, D0 through D2, contain a 24-bit address which can point to anywhere in the device’s address space. Figure 3 shows the defined regions.

Control Bits

The five control bits in register D4 define how the bootloader functions at run time. They are:
WRITE_UNLOCK: Set this bit to unlock write operations. This bit is provided to insure any write operations to memory are intentional. This bit is automatically cleared after a RESET.
ERASE_ONLY: Set this bit to allow erase opera- tions on Program Memory, but not write opera­tions. This is useful when it is only necessary to erase a section of memory. The address must be on a 64-byte boundary to erase.
AUTO_ERASE: Set this bit to automatically erase while writing to memory. On every 64-byte bound­ary, the bootloader will automatically erase before writing. This is useful when writing large sequen­tial blocks of data over old data in Program Memory.
AUTO_INC: Set this to automatically increment the address after each write or read operation. This is useful when writing large sequential blocks of data.
ACK: In PG mode, set this bit to force the boot­loader to send Acknowledgement of every PUT command received. An Acknowledgement is simply an empty CAN frame. This is useful in sys­tems that require fully synchronized flow between the source and the target.

TABLE 1: SUMMARY OF CONTROL BITS (REGISTER D4)

Bit # Bit Name Description
0 WRITE_UNLOCK 0 = Prevent writing (default).
1 = Allow write to any memory.
1 ERASE_ONLY 0 = Allow write after erase (default).
1 = Don’t write after erase.
2 AUTO_ERASE 0 = Don’t automatically erase before writing.
1 = Erase if on 64-byte border, then write (default).
3AUTO_INC 0 = Update pointer manually.
1 = Increment pointer automatically after operation (default).
4ACK 0 = Don’t send Acknowledgement.
1 = Send an empty CAN frame after every PUT command (PG mode only)(default).
DS00247A-page 4 2003 Microchip Technology Inc.
AN247

Command and Data

There are four commands defined to add functionality and reliability to the bootloader. These are summarized in Table 2. They are:
NOP: No operation. This is supplied to allow writing Control registers without issuing a command.
RESET: Reset the device via the RESET instruction.
INIT_CHK: Initialize the checksum and verify reg­isters. This clears the internal 16-bit checksum and clears the verify flags.
CHK_RUN: Test the checksum and verify registers; if valid, then clear the last location of EEPROM. Data is passed through the Data registers in the Control register set and added to the checksum; the program checks for zero. The internal self-verify flag is also tested for zero.
Note that these are not the only commands that may be implemented. Users may expand on this basic set by using the high order bits of register D4, or using combi­nations of bits to define an expanded command set. Registers D5 through D7 may also be used to define additional parameters in combination with commands. The basic 8-register structure for control allows users to expand the command sets to their own needs.

TABLE 2: SUMMARY OF SPECIAL COMMANDS (COMMAND REGISTER D5)

Command Code Description
NOP 00h No operation.
RESET 01h Issue a Software Reset to the device.
INIT_CHK 02h Initialize internal checksum and verify registers.
CHK_RUN 03h Test the checksum and verify, then clear the last location of EEPROM if valid.
All other commands 04h - FFh Undefined - operate as NOP.

BOOTLOADER DETAILS

Reading/Writing/Erasing Program Memory

For writing to FLASH Program Memory, the Control register address must point to memory region 000000h to 2FFFFFh. Read operations occur at the byte level. Write operations are performed on multiples of 8 bytes (one block), while erase operations are performed on 64 bytes (one row).
Writing is an immediate operation. When the PUT “DATA” command is received, the address already stored in the Control registers is decoded and the data is written to the target’s Program Memory. Data is only written if the write operation has been unlocked.
When writing Program Memory, the memory should be erased first. Either the auto erase or erase only options can be used to erase memory on every 64-byte border. The default operation is that bits can only be cleared when written to. An erase operation is the only action that can be used to set bits in Program Memory. Thus, if the bootloader protection bits are not set up in the Configuration registers, operations on memory from 000h to 1FFh could partially or completely disable the bootloader firmware.
User IDs (starting at address 200000h) are considered to be part of Program Memory and are written and erased like normal FLASH Program Memory.

Reading/Writing EEPROM Data Memory

For writing to EEPROM Data Memory, the Control reg­ister address must point to memory region F00000h to FFFFFFh. Read and write operations occur at the byte level. Write operations must be unlocked before any write operation can take place.
Note that the last location of the Data Memory is used as a boot flag. Writing anything other than FFh to the last location indicates normal code execution.

Configuration Bits

PIC18F devices allow access to the device configura­tion bits (addresses starting at 300000h) during normal operation. In the bootloader, the Control register address must point to memory region 300000h to 3FFFFFh to provide Configuration Memory access. Data is read one byte at a time and, unlike Program Memory, is written one byte at a time. Since configura­tion bits are automatically erased before being written, the erase control bit will have no affect on Configuration Memory.
Having access to configuration settings is very power­ful; it is also potentially very dangerous. For example, assume that the system is designed to run in HS mode with a 20 MHz crystal. If the bootloader changes the oscillator setting to LP mode, the system will cease to function - including the bootloader! Basically, the system has been killed by improperly changing one bit.
2003 Microchip Technology Inc. DS00247A-page 5
AN247
It is also important to note some configuration bits are single direction bits in Normal mode; they can only be changed to one state, and cannot be changed back. The code protection bits in Configuration registers 5L and 5H are a good example. If any type of code protec­tion is enabled for a block, it cannot be disabled without a device programmer. Essentially, the bootloader cannot reverse code protection.
The Device ID (addresses 3FFFFEh and 3FFFFFh) is also considered Program Memory. While they can be accessed, however, they are read only and cannot be altered.

Write Latency

When writing data, there is a specific time that the pro­gramming source must wait for to complete the pro­gramming operation. Fortunately, the CAN module actually buffers received data; therefore, receiving can actually overlap memory write operations (Figure 6). In general, it takes about 2 ms for Program Memory write operations, while EEDATA takes about 4 ms. Not all PIC18F devices have the same time specifications, so it is important to verify the write times for the specific device to be used.
FIGURE 6: CAN RECEIVE VS.
MEMORY WRITE
CAN
Message
Receive
Memory
Write
CAN Msg

Writing in Assembly

When writing in assembly, the boot block and new vec­tors must be considered. For modular code, this is gen­erally just a matter of changing the linker script file for the project. An example is given in Appendix C. If an absolute address is assigned to a code section, the address must point somewhere above the boot block.
For those who write absolute assembly, all that is nec­essary to remember is that the new RESET vector is at 200h, and the interrupt vectors are at 208h and 218h. No code except the bootloader should reside in the boot block.

Writing in C

When using the MPLAB® C18 C compiler to develop PIC18F firmware for an application, the standard start-up object (c018.o or c018i.o) must be rebuilt with the new RESET vector. Like modular assembly, the linker file must be changed to incorporate the pro­tected boot block and new vectors. Appendix C shows an example linker file.
Users of other compilers should check with the com­piler’s software user guide to determine how to change the start-up code and vectors.

Bootloader Re-Entry

If the need exists to re-enter Boot mode from the appli­cation (and it usually does), the last location of the data EEPROM must be set to FFh. The code in Example 1 demonstrates how this might be done in an application. Since the bootloader assumes RESET conditions, a RESET instruction should be initiated after setting the last location.

WRITING CODE

The bootloader operates as a separate entity, which means that an application can be developed with very little concern about what the bootloader is doing. This is as it should be; the bootloader should be dormant code until an event initiates a boot operation. Under ideal circumstances, bootloader code should never be running during an application’s intended normal operation.
When developing an application with a resident bootloader, some basic principles must be kept in mind.
DS00247A-page 6 2003 Microchip Technology Inc.
EXAMPLE 1: SETTING THE LAST
LOCATION OF THE DATA MEMORY
SETF EEADR ; Point to the last byte SETF EEADRH SETF EEDATA ; Bootmode control byte MOVLW b'00000100 ; Setup for EEData MOVWF EECON1 MOVLW 0x55 ; Unlock MOVWF EECON2 MOVLW 0xAA MOVWF EECON2 BSF EECON1, WR ; Start the write NOP BTFSC EECON1, WR ; Wait BRA $ - 2 RESET
AN247

Debugging

For most situations, it is not necessary to have the bootloader firmware in memory to do debugging of an application with either the MPLAB ICD 2 or ICE devices. However, branch statements must be inserted at the hardware vectors to get to the new designated vectors. It may also be useful to have the start-up tim­ing match exactly to the bootloader entry. When devel­opment of the application is finished, either remove the branches and rebuild the project, or export only the memory above the boot block. This code can then be distributed to those who are updating their firmware.

COMPILE TIME OPTIONS

Compile time options are available to provide initial set­tings as well as features. Some features require more memory than others. Compiling certain combinations of options can actually generate code that is larger than the designated boot block.

Modes of Operation (Compile Time)

The bootloader can be built to support either one of two mutually exclusive modes of operation:
• P Mode - Only PUT commands are accepted. The device will never ‘talk back’ to the source.
• PG Mode - Both PUT and GET are allowed. The source can actually read out of the target’s memory as well as write to the target’s memory.
The compile time definition, ALLOW_GET_CMD, selects the mode.

Self-Verification

The definition, MODE_SELF_VERIFY, enables a self-verification feature. With this feature, the firmware reads back the data written to any type of memory (not Control registers), and it compares the read data with the source (received) data. A flag is set in a register if verification failed.

Vectors

The RESET and interrupt vectors can be set to any location using the following definitions:
RESET_VECT
HIGH_INT_VECT
LOW_INT_VECT
The default values reside at addresses 200h, 208h, and 218h, outside of the boot block; they parallel the default PIC18F458 interrupt vectors. If compiling some features causes the bootloader to be larger than the boot block, then these vectors must be adjusted to addresses above the used memory area. If the jump is farther than a relative branch, then the definition, NEAR_JUMP, must be removed.

Other Basic Settings

There are several other definitions that set CAN specific settings. These determine which bits of the message identifier are used for the PUT/GET and CONTROL/DATA commands, the CONTROL/DATA bit used for GET responses, as well as the filters and masks for the programming node. Refer to Table 3 for specific details.
2003 Microchip Technology Inc. DS00247A-page 7
AN247

TABLE 3: SUMMARY OF COMPILE TIME DEFINITIONS

Definition
ALLOW_GET_CMD N/A Allows GET commands. If not present, then the bootloader will only
MODE_SELF_VERIFY N/A Enables self-verification of data written to memory.
NEAR_JUMP N/A Uses BRA to jump to vectors. If not defined, then it uses GOTO.
HIGH_INT_VECT 208h Remapped high priority interrupt vector.
LOW_INT_VECT 218h Remapped low priority interrupt vector.
RESET_VECT 200h Remapped RESET vector.
CAN_CD_BIT RXB0EIDL<0> Received CONTROL/DATA select bit.
CAN_PG_BIT RXB0EIDL<1> Received PUT/GET select bit.
CANTX_CD_BIT TXB0EIDL<0> Transmitted CONTROL/DATA select bit.
CAN_TXB0SIDH 10000000 Transmitted identifier for target node.
CAN_TXB0SIDL 00001000
CAN_TXB0EIDH 00000000
CAN_TXB0EIDL 00000100
CAN_RXF0SIDH 00000000 Receive filter for target node.
CAN_RXF0SIDL 00001000
CAN_RXF0EIDH 00000000
CAN_RXF0EIDL 00000111
CAN_RXM0SIDH 11111111 Receive mask for target node.
CAN_RXM0SIDL 11100011
CAN_RXM0EIDH 11111111
CAN_RXM0EIDL 11111100
CAN_BRGCON1 11000001 CAN bit rate control.
CAN_BRGCON2 10111010
CAN_BRGCON3 00000111
CAN_CIOCON 00100000 CAN I/O control.
Value
(Default)
Description
receive CAN messages.
DS00247A-page 8 2003 Microchip Technology Inc.
AN247

TIPS FOR SUCCESSFUL FIELD PROGRAMMING

Successful programming can take several forms, depending on which compile time options are selected. In P-Mode with self-verification enabled, the pro­grammed target keeps a running 16-bit sum of all the data written to memory. In addition, every write opera­tion is verified by reading back and comparing the data, providing some assurance that all data was received and that all data was correctly written.
In PG mode without self-verification, the programming source can read as well as write data. Thus, verification is provided directly by the source.

EXAMPLE PROGRAMMING SEQUENCE (P MODE)

1. Put the programming target in Boot mode.
2. Send a control packet. Load the address with
the beginning memory. Unlock write operations. Enable auto erase. Disable erase only. Issue a Self-Verify Reset.
3. Send Program Memory data. The data must be
8 bytes and aligned on an even 8-byte address.
4. Wait an appropriate amount of time.
5. Repeat steps 3 and 4 until all Program Memory
is written.
6. Send EEPROM Data Memory data.
7. Wait an appropriate amount of time.
8. Repeat steps 6 and 7 until all EEPROM Data
Memory is written except for the last location.
9. Send one byte of Configuration Memory data.
10. Wait an appropriate amount of time.
11. Repeat steps 9 and 10 until all the desired con-
figuration settings are written. If setting memory protection, write these last.
12. Send a control packet. Send a check and run
command. Also, send the two’s compliment of the sum of all data written in the Data registers in the same packet.
13. Send a RESET command. If self-verification suc-
ceeded, then the node should be running the new application. This could be verified at the applica­tion level by the programming source, or any other node in the network that communicates with the target.

EXAMPLE PROGRAMMING SEQUENCE (PG MODE)

1. Put the programming target in Boot mode.
2. Send a control packet. Load the address with the beginning memory. Unlock write operations. Enable auto erase. Disable erase only.
3. Send Program Memory data. The data must be 8 bytes and aligned on an even 8-byte address.
4. Wait an appropriate amount of time.
5. Read back the data written and compare. If fail, then reset the pointer and try again, steps 3 and 4.
6. Repeat steps 3, 4, and 5 until all Program Memory is written.
7. Send EEPROM Data Memory data.
8. Wait an appropriate amount of time.
9. Read back the data written and compare. If fail, then reset the pointer and try again, steps 7 and 8.
10. Repeat steps 7, 8, and 9 until all EEPROM Data Memory is written except for the last location.
11. Send one byte of Configuration Memory data.
12. Wait an appropriate amount of time.
13. Read back the data written and compare. If fail, then reset the pointer and try again, steps 11 and 12.
14. Repeat steps 11, 12, and 13 until all the desired configuration settings are written. If setting memory protection, write these last.
15. Write 00h to the last location of EEPROM Data Memory.
16. Send a RESET command. Functionality should be verified at the application level by the programming source, or any other node in the network that communicates with the target.
2003 Microchip Technology Inc. DS00247A-page 9
AN247

RESOURCES

For most builds, the PIC18F CAN bootloader resides within the device’s Boot Block (000h to 1FFh), and does not impact the normal Program Memory space beyond the relocation of the interrupt vectors.
As noted, some combinations of compile time options (for example, selecting both PG mode and self-verify) will result in a bootloader that exceeds the Boot Block size. In these cases, it will be necessary to relocate any user application code and the interrupt vectors above the boundary of the bootloader, being careful to avoid code overlap. If Program Memory space is not critical, the optimal solution may be to locate all application code and the interrupt vectors above the upper bound­ary of Block 0 (1FFFh). Write protecting Block 0 to protect the bootloader is desirable, but not essential.
The bootloader uses 12 bytes of data SRAM during operation. It also uses 1 byte of data EEPROM at all times, as the normal operation/bootloader flag.

REFERENCES

W. L awren z, CAN System Engineering From Theory to Practical Applications. New York: Springer-Verlag
New York Inc., 1997.
MPLAB-CXX Compiler User’s Guide, Microchip Technology Inc., 2000 (Document number DS51217).
Microchip Technology Inc., Application Note AN851, “A FLASH Bootloader for PIC16 and PIC18 Devices” (Document number DS00851).
DS00247A-page 10 2003 Microchip Technology Inc.
Loading...
+ 22 hidden pages