When using DMA with UART, you should periodically poll the error bits (UxSTAT0 & 0x78) and if any of them is set, do two dummy reads of UxRXD. Two reads are needed to clear OE, and it's faster to do two in a row than to test specifically for OE.
Since the DMA transfer is essentially stuck as soon as errors occur, you have to do this polling somewhere in your loop so that things get un-stuck . Errors usually occur when you connect/disconnect things.
Nominally, the error flag checking can be done in the interrupt. You'd set RDAIRQ# (in UxCTL1) to 1 so that only receiver errors generate a real interrupt, while data transfers only strobe the DMA.
I made a small mistake in my firmware design. I used an interrupt driven communications model to communicate with the PC host. Of course that is problematic since the flash loader can't use any of the interrupt vectors; they must remain available for use by the application when it gets loaded.
There's an easy way around this limitation: use DMA. There's no need to handle any DMA interrupts, just keep a big enough buffer and you'll be fine. Interrogate DMAxHS (see below) to see what's the current position of the write 'head'.
| CODE |
#define DMA0HS (*(volatile far unsigned int*)&DMA0H) #define DMA1HS (*(volatile far unsigned int*)&DMA1H)
... far char * head;
asm("ATM"); head = DMAxHS & 0x0FFF; // this translates to decent assembly
|
note that this is a flash programmer, not boot loader -- as pointed out by someone, the boot loader uses a flash programmer but provides much more beyond that.
| CODE |
; 386 void near programFlash(far char * src, rom char * dst, UINT count) ; 388 UCHAR zero = 0; CLR R1 ; 394 page = (UCHAR)((UINT)dst >> 9); LD R0,_1_programFlash SRL R0 ; 395 FCTL = zero; LDX 4088,R1 ; 396 FPS = page; FCTL = 0x73; FCTL = 0x8C; FPS = page; LDX 4089,R0 LDX 4088,#115 LDX 4088,#140 LDX 4089,R0 ; 397 FCTL = 0x95; // erase the page LDX 4088,#149 ; 398 FCTL = zero; LDX 4088,R1 ; 399 FPS = page; FCTL = 0x73; FCTL = 0x8C; FPS = page; LDX 4089,R0 LDX 4088,#115 LDX 4088,#140 LDX 4089,R0 ; 400 // program ; 401 s = src; d = dst; LD R0,_2_programFlash LD R1,_2_programFlash+1 LD R2,_1_programFlash LD R3,_1_programFlash+1 ; 402 do { _10_L_210: ; 403 *d = *s; LDX R4,@RR0 LDC @RR2,R4 ; 404 ++ d; INCW RR2 ; 405 ++ s; INCW RR0 ; 406 -- count; SUB _0_programFlash+1,#1 SBC _0_programFlash,#0 ; 407 } while (nonzero(count)); LD R4,_0_programFlash OR R4,_0_programFlash+1 JR NE,_10_L_210 ; 408 FCTL = 0; //relock LDX 4088,#-0 RET
|
The code below should work with ZDS II 4.9.5. It will most likely give very sloppy code when run under any other version, so DON'T
Ah, don't be fooled -- this is written in assembly. Yes, assembly. You put it into a .c file and run a compiler on it, but it's still assembly. You'll see what I mean when you look at the .src that the compiler generates (enable it in project options: Compiler->Listings). The whole thing is a big workaround for optimizer deficiencies, but it should be easy to see what I did and why.
If any of you can get the resultant .src any slimmer than what it already is while still feeding readable C code to the compiler, I'd like to see it. It's always nice to learn new tricks.
Note that for near data and/or counts <= 255, you copy count into a register as well, and obtain code that is literally as good handwritten assembly.
| CODE |
#include <ez8.h> #include <defines.h>
// Do not compile with anything else but ZDS II 4.9.5 // if you want the code to perform reasonably well. // DO NOT change the code without verifying the .src // output first, otherwise you WILL get bitten.
// This is a workaround for some compiler // writers not knowing some easy assembly tricks.;) #define nonzero(i) ((UCHAR)(i >> 8) | (UCHAR)i)
// programs one page of flash void near programFlash(far char * src, rom char * dst, UINT count) { UCHAR zero = 0; UCHAR page; far char * s; rom char * d;
// unlock & wipe page page = (UCHAR)((UINT)dst >> 9); FCTL = zero; FPS = page; FCTL = 0x73; FCTL = 0x8C; FPS = page; FCTL = 0x95; // erase the page FCTL = zero; FPS = page; FCTL = 0x73; FCTL = 0x8C; FPS = page; // program s = src; d = dst; do { *d = *s; ++ d; ++ s; -- count; } while (nonzero(count)); // relock FCTL = 0; }
|