There are three ways to program the MSP430 series microcontrollers: using the JTAG interface, using the BSL firmware, and using user-defined upgrade firmware. Since the method of using the customized upgrade firmware for program upgrade is more flexible and has a wide range of uses, this article will focus on it.
1. Using the JTAG interface
The MSP430 series microcontrollers are all integrated with the JTAG interface, which implements the test access port state machine (TAP Controller) that complies with the IEEE STD1149.1 specification. It uses a four-wire serial interface (TEST is used for chips with fewer pins). Data or instructions are shifted in from TDI (test data input); serial data is shifted out from TDO (test data output); TCK (test clock) is used as the clock signal input; TMS (test mode select) signal controls the state of the TAP controller. This interface can be used to shift in instructions and data, thereby controlling the address lines and data lines of the target chip, achieving the purpose of reading and writing the target chip FLASH and simulation debugging [1]. In addition, TI has now launched a new debugging interface - SPY-BI-WIRE, which uses a two-wire system, one of which is a data line (bidirectional) and the other is a clock line.
The advantage of using this interface is that there is no need to design additional circuits and programs, and the program can be downloaded using an emulator. The disadvantage is that once the user burns the JTAG fuse to ensure the security of the code, the interface will be permanently destroyed and cannot be used anymore.
2. Using the BSL interface
BSL is the abbreviation of Bootstrap Loader, and its Chinese name is program loader. It is essentially a communication program solidified in the chip (occupying the address space of 0C00h-1000h), which can be used to erase, read and write FLASH. Since it is solidified in the chip, there is no need to worry about it being changed or lost.
The interface uses 5 wires: GND, TX (P1.1/P1.0), RX (P2.2/P1.1), RST, TCK (TEST). By adding specific level timing signals to RST and TCK (TEST), the BSL program can be started to achieve communication with the target chip. The character format of the communication is 8 data bits, a stop bit and an even parity bit. The starting baud rate is 9600bps (the BSL 1.6 version can change the baud rate to 38400bps). The BSL protocol requires that an 80h character be accepted first for clock synchronization, and then a response character 90h is sent. Then 8 characters are accepted and the corresponding processing routine is jumped according to the command. The C language description of the BSL program is as follows
Void main()
{
Byte B, bArray[8];
LOCKSTATE = LOCK;
InitPorts();
While(1)
{
B=ReceiveSyncByte();
SyncTimer(B);
SendAck();
For( I = 0; I < 8; i++) bArray = ReceiveByte();
Switch(bArray)
{
0x12: //Write FLASH
For(I = 0; Array[7] ; i++)
{
B=ReceiveByte();
If(LOCKSTATE ==UNLOCK) WriteByte(B);
}
Break;
0x10://Receive passwordReceive32Byte
();
If(PassWordCorrect) LOCKSTATE = UNLOCK;
Break;
0x18..0xn:
If(PassWordCor) DoTask();
}
If(NoError) SendNak();
else Ifrect
If(TxData) Data();
else
SendAck();
Its
implementation details may vary from version to version. If the user wants to use it to implement program upgrade, please refer to the literature [2][3]. The advantage of using the BSL program for upgrade is that it saves code space, the user does not need to implement their own upgrade firmware, and there are many ready-made BSL upgrade tools; the disadvantage is that the BSL interface needs to be reserved and on-site wiring is required.
3. Using custom upgrade firmware
The FLASH memory module of the MSP430 series microcontroller is an independently operable physical storage unit. All modules are arranged in the same linear address space, and the memory is divided into multiple 512-byte segments (the information segment size is 128/64 bytes). Each segment can be erased separately, and the program can erase and write the FLASH under normal operating voltage, so it is particularly suitable for online program upgrade (in-system program).
Custom upgrade firmware is to build a code for upgrading the application into the program, so that the existing communication interface can be used for remote code upgrade. The implementation principle is to place two code segments in the target chip: one for the application and one for the upgrade program. The address segments of the two do not overlap, so the upgrade program can be used to erase the application and write new code.
3.1 Boot program
After reset, the boot program is entered first, which determines whether to enter the upgrade program or the application program. The significance of the boot program is that when the application does not exist or is wrong, it can directly enter the upgrade program, so as to ensure that the upgrade can be repeated if the upgrade fails.
The description of the boot program is as follows:
Void main()
{
While(1)
{
If(ResetVectorValid()) Application();
Updata();
}
}
The ResetVectorValid() function is used to detect whether the application exists or is valid. The implementation can be to detect whether the entry address of EnterApplication is legal. A simple implementation is
#define ResetVectorValid() (ResetVector !=FFFF)
ResetVector is the entry address of the application, which is usually placed in a fixed address. The entry address is modified after the program is upgraded. Application() is the application. It will not return if it is executed normally. It will only return after receiving the upgrade command. The Return statement can be used in Application() to enter the upgrade program.
Updata() is the upgrade program. The detection instruction must be added at its entry to confirm that it is a normal entry into the upgrade program. After entering the upgrade program, the communication end should first send an erase instruction to erase the original code, and then send the upgrade code to update FLASH. If there is an external expansion memory or the user program is small, the entire program segment can be received first. If the verification is correct, it can be written, which will be more reliable.
Here is a strategy to erase the block containing ResetVector first, and finally write the value of ResetVector, so as to try to ensure that an incomplete application will not be entered.
3.2 Application Writing
There is no major change in the writing of the application. It is necessary to add a custom upgrade command in the communication protocol to enter the upgrade program. In addition, you need to change the link file (*.XCL) and specify the address range of the application. The following takes the application address range of 2500-F7DC as an example (the ones commented out with // are the default settings)
// Code
//-Z(CODE)CSTART=2500-FFDF
//-Z(CODE)CODE=2500-FFDF -Z(
CODE)CSTART=2500-F7DF
-Z(CODE)CODE =2500-F7DF
// Constant data
//-Z(CONST)DATA16_C,DATA16_ID,DIFUNCT,CHECKSUM=2500-FFDF
-Z(CONST)DATA16_C,DATA16_ID,DIFUNCT,CHECKSUM=2500-F7DF
// Interrupt vectors
//-Z(CONST)INTVEC=FFE0-FFFF
-Z(CONST)INTVEC=F7E0-F7FF
After the modification, add the file to the project. The compiled code can be used as the upgrade code.
3.3 Writing the upgrade program
Create a new project, and use the above method to locate the upgrade code to an area that does not overlap with the application, such as F800-FFFF. In this case, do not modify
-Z(CONST)INTVEC=FFE0-FFFF.
In the upgrade program, map all interrupts except the reset interrupt to the application. One way is to embed the assembly and use the assembly positioning instruction ORG; or write 15 interrupt mapping functions, as follows
//Remap the interrupt vector address
#pragma vector=0x0
__interrupt void intvec_0(void)
{
asm("br & 0F7E0h"); //Assume that the address of interrupt 15 is stored in F7E0
}
In addition, you can also use the method of dynamically determining the interrupt entry address, that is, put the interrupt vector address into the agreed RAM, as follows
__no_init void (*intvec1[16])() @ 0x200; //Define an array pointing to a function pointer to map a new interrupt vector
//Remap the interrupt vector address
#pragma vector=0x0 //
__interrupt void intvec_0(void)
{
asm("push R15");
asm("mov #0x200,R15");
asm("call @R15");
asm("pop R15");
}
Then map the interrupt vector in the application, such as
intvec1[TIMERA0_VECTOR/2]=Timer_A_0;
that is, execute the Timer_A_0() function when TIMERA0 interrupts. The advantage of this is that the entry of the interrupt function can be dynamically determined at runtime, just like the virtual function in a high-level language.
When these two function blocks are written, you can perform engineering testing.
3.4 Application and upgrade program are completed at the same time
Maybe you also want to complete the two functions in one project. In addition to modifying the link file, you also need to pay attention to the following points:
(1) Locate all functions of the upgrade program to the upgrade program space, that is, add a positioning instruction
#pragma location="UPDATECODE" in front of the function // UPDATECODE is the name of the segment where the upgrade program is located
(2) Modify the routine called by the function return. When the function returns, the default routine for popping registers will be called, and these routines may not be in the address space of the upgrade program. One solution is to use the LST file (assembly code) generated by the compilation environment to modify the pop-up register routine called when the function returns one by one, so that the two codes can be guaranteed to be independent. The disadvantage of this is that every time the C language code is changed, the assembly code must be modified again, which is cumbersome. Another method is to consider that what the upgrade program does is to receive and send data, and generally does not need to use interrupts. In this way, you can add the __monitor compilation instruction in front of the upgrade function to indicate that the function is an atomic operation. At the entry of this type of function, SR is first pushed and interrupts are disabled. When returning, RETI is used to return. At this time, the compiler does not call the routine to pop the saved registers, but pops the registers one by one according to the stack push situation.
(3) Change the SWITCH statement. When using the SWITCH statement, the compiler will also generate a default routine call, which is difficult to block, so the only way is to modify SWITCH into multiple judgment statements.
|