This thread has been locked.
If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.
I used a MSP430G2553 to accept analog signal inputs, store the data, and send it via the UART to my computer's serial port. Everything works well except the ADC. Sometimes it works well, but more often, it does not. Often it stores a bunch of trailing zeroes on the ADC value. I know sampling too quickly is not my problem because I'm on essentially the slowest setting possible. I suspect it's a grounding/clean power supply/capacitor issue. When I run my code straight from the MSP430 LaunchPad, the ADC works flawlessly and gives accurate readings, but when I plug the uC into my own device, I will get the trailing-zeroes behavior again.
The board is two layer, and I know my ground trace from the uC to battery ground is super long. Is that what is causing the inaccurate ADC values? Farther down this post I have some test data that shows that the ADC will follow significant value changes, but not closely. I know it's not just my sensors sticking because the internal temperature sensor does the same trailing zeroes behavior. Something is causing the ADC to quit prematurely, I think.
Suggestions?
PCB layout and schematic attached
I don't think so. The patterns which clearly appear on the high pressure sensor (and partly on teh low pressure sensor) data seem to indicate a problem on the digital side. It looks like a bit is being sent as 0 where it should be 1, and on certain ranges only.Robert Snell said:I know my ground trace from the uC to battery ground is super long. Is that what is causing the inaccurate ADC values?
Alternatively, this can be a software problem. You say 'the ADC works on the LaunchPad', but I guess on the LaunchPad you only test it with a few discrete voltages. Perhaps the problem is already there but you don't see it due to the test conditions.
Thanks for the feedback. I'm confident that the RS232 isn't inherently the problem. I have collected data on the development board (while it is plugged into usb) and sent the data via (jumper wires over to my empty DIP20 to utilize) my circuit board's RS232 connection to my computer's terminal service to retrieve the data. The data sent in this way was valid. Here is a sample of the data collected on my circuit board and sent via USB (1xxx and 2xxx) that matches the graph given previously. The spreadsheet is not completely professional in appearance. 5126.20120420 pcb 3393 vacuum test.xlsx
Perhaps my circuit board's power system isn't adequate to handle the RS232 bit-banging? However, I have had successful spurts of data sent from my circuit board.
I am also quite confident that the development board does actually store different data than the circuit board, even at room temperature (in the temperature sensing example).
Is the issue perhaps a high ESR on my caps? These are the caps I used:
Ref # |
Qty |
Product |
Descriptions |
|
|
|
|
C3 C7 |
4 |
FK20X7R1C106K |
10uF Ceramic Capacitor |
C5 |
2 |
BC1038CT-ND |
47pF Capacitor |
C15 |
2 |
SR151A101JAR |
0.1nF Ceramic Capacitor (2 req.) |
C (all other) |
20 |
C320C104K5R5TA |
0.1uF Ceramic Capacitor |
As I said, the patterns shown seem to indicate that this is a digital problem (the values are shifted down by exactly the same amount or a multiple of it)
If it were an analog problem, I wouldn't expect bocks of values shifted down by exactly the same amount. I'd rather expect nonlinear scaling, completely erratic values, clipping or such.
But in your case, there are whole blocks of data which appear moved down by exactly the same amount. As if a digital bit is not being set.
It looks like the values are going, say, 0x20 to 0x2f but then instead of going to 0x30 they fall back to 0x20 because bit 4 won't set for some reason.
This is why suspected your code too. Do you send the exact ADC results, or do you convert them to voltage or pressure values before sending? Maybe there's a problem in your conversion function that leads to a folding effect in the transformation.
I agree (if this is what you're refering to) that the conversion to digital doesn't seem to be completing properly and that certain bits are only occasionally being set properly. That's the 'trailing zeroes' that I was seeing. And the program code does store the '1xxx' and '2xxx' directly. I'll include the full source code here:
;******************************************************************************* ; Title: Altitude Data Logger ; Author: Rob Snell ; Date: April 20, 2012 ; Purpose: Senior Project-Design Problems, Bob Jones University, Spring 2012 ; ; Very basic code shell was MSP320G2xx2 Demo by D. Dang, TI Inc Dec 2010- WDT, Toggle P1.0, Interval Overflow ISR, 32kHz ACLK ;******************************************************************************* .cdecls C,LIST, "msp430g2553.h" .bss Const2,2 .bss StMemBlk2,2 ; Do not use 'comma one' at the end of these .bss things. They glitched when I tried to use them for byte storage. .bss StMemBlk3,2 .bss StMemBlk4,2 .bss EndBlkChar1,2 .bss EndBlkChar2,2 .bss EndBlkChar3,2 .bss EndBlkChar4,2 ;------------------------------------------------------------------------------ .text ; Program Start ;------------------------------------------------------------------------------ RESET mov.w #0280h,SP ; Initialize stackpointer CLKSetup mov.b #LFXT1S_2,&BCSCTL3 ; Since XTS is 0 by default, this selects VLOCLK to source ACLK (12 kHz roughly) ; Using ACLK for Timer (See Timer_A_Setup) which interrupts and causes ADC clr.b &DCOCTL ; Select lowest DCOx and MODx settings (these three lines copied from pp283-4) mov.b &CALBC1_1MHZ,&BCSCTL1 ; Set range mov.b &CALDCO_1MHZ,&DCOCTL ; Set DCO step + modulation (1 MHz) WDTSetup ; mov.w #WDT_ADLY_250,&WDTCTL ; WDT 250ms, ACLK, interval timer PortSetup mov.w #WDTPW+WDTHOLD,&WDTCTL ; Turn off Watchdog Timer mov.b #00001001b,&ADC10AE0 ; Enable analog input on {now A0 (Pin 2, P1.0) and A3 (Pin 5, P1.3)}; should i enable only when sampling? bic.b #00000001b,&P2DIR ; Set P2.0 to input mode (for prompting UART transfer) bis.b #00000001b,&P2REN ; Enable P2.0 pullup/down resistor bic.b #00000001b,&P2OUT ; Set P2.0 internal resistor to pulldown bis.b #00000001b,&P2IE ; Enable P2.0 interrupt bic.b #00000001b,&P2IES ; Set P2.0 Transition Trigger to 'low-to-high' bic.b #00000001b,&P2IFG ; Reset P2.0 Interrupt Flag ; bis.b #WDTIE,&IE1 ; Enable WDT interrupt bic.b #00111000b,&P2OUT ; Turn off P2.5 & P2.3 bis.b #00111000b,&P2DIR ; Set P2.5 & (P2.4 for UART out indicator) & P2.3 to output (for indicating which sensor is being stored) ;------------------------------------------------------------------------------ Timer_A_Setup; ;------------------------------------------------------------------------------ bis.w #TASSEL_1+TAIE,&TACTL ; Select ACLK to power Timer A (12kHz) + Enable Interrupt mov.w #03393h,&TACCR0 ; Count limit is FFFF is apx 4.8 sec; 7460h for 4.5 hrs; 3393 for ~1 sample/sec ;------------------------------------------------------------------------------ ADC_Setup; Setup ADC functionality ;------------------------------------------------------------------------------ and.w #0fff0h,&ADC10CTL0 ; Allow ADC config, clear int flag, disable int// automatic on system reset // and.w #0fdffh,&ADC10CTL0 ; Turn off Reference Output to save power bis.w #ADC10SSEL_0,&ADC10CTL1 ; Select ADC10OSC for ADC clock source (~ 5 MHz) bis.w #ADC10DIV_7,&ADC10CTL1 ; Divide clock by 8 (so 625kHz) (for longer sampling period) bis.w #CONSEQ_0,&ADC10CTL1 ; Select single-channel single-conversion mode; bis.w #SREF_0,&ADC10CTL0 ; Set Vcc and Vss as limits for conversion; bis.w #ADC10ON,&ADC10CTL0 ; Enable conversion core bis.w #ADC10SHT_3,&ADC10CTL0 ; Use 64 ADC10OSC cycles for sample input (charge sampling capacitor) // Worth looking into again. p552 bis.w #INCH_0,&ADC10CTL1 ; Set ADC channel to A0 bis.w #ADC10IE,&ADC10CTL0 ; Enable ADC interrupts bis.w #ENC,&ADC10CTL0 ; End ADC config mov #0C400h,R6 ; C400; Start address for ADC data storage (Must be initialized as first address in a segment!) mov #0C400h,&Const2 ; C400; Load first segment entry boundary for flash erase procedure (Do not change!) mov #0FE00h,R7 ; End (First illegal at end) address for ADC data storage ;------------------------------------------------------------------------------ UART_Setup; Setup Serial Functionality ;------------------------------------------------------------------------------ mov #0D200h,&StMemBlk2 ; Configure Manual Memory Blocks mov #0E000h,&StMemBlk3 ; mov #0EE00h,&StMemBlk4 ; bis.b #UCSWRST,&UCA0CTL1 ; enable UART config bis.b #00000100b,&P1SEL ; Configure P1.2 for UART use bis.b #00000100b,&P1SEL2 ; ; bic.b #UCSWRST,&UCB0CTL1 ; Allow turning off UCB interrupt ; bic.b #UC7BIT,&UCA0CTL0 (default 8-bit data mode, no parity) bis.b #UCSSEL_3,&UCA0CTL1 ; Clock source is SMCLK (1 MHz) mov.b #00h,&UCA0BR1 ; Clear top bits of prescaler mov.b #104,&UCA0BR0 ; Copied from byu website. See history mov.b #06h,&UCA0BR0 ; Prescale divide by 6 (UCBRx);; mov.b #UCBRS0,&UCA0MCTL ; Copied from byu website mov.b #UCBRF_8+UCOS16,&UCA0MCTL ; Set first stage modulator (to 8) and enable oversampling baud rate generation (fast clock source) [9600 baud];; mov R6,R9 ; Set iniitial TX address bic.b #UCSWRST,&UCA0CTL1 ; end UART config ;------------------------------------------------------------------------------ Timer_A_Start; ;------------------------------------------------------------------------------ bis.w #MC_1,&TACTL ; Count up to TACCR0 bis.w #TAIE,&TACTL ; Enable Timer Interrupts ;------------------------------------------------------------------------------ Main_Prog_Run; ;------------------------------------------------------------------------------ Mainloop bis.b #00100000b,&P2OUT ; Initialize P2.5 so toggle is correct bis.w #LPM3+GIE,SR ; Enter LPM3, interrupts enabled nop ; Required only for debugger ;------------------------------------------------------------------------------ ;------------------------------------------------------------------------------ TIMER_A_ISR; Handle a Timer Overflow ;------------------------------------------------------------------------------ bic.w #TAIE,&TACTL ; Disable Timer Interrupts cmp R6,R7 ; Test for last memory location jz End_of_Mem xor.b #00101000b,&P2OUT ; Toggle P2.5 & P2.3 bis.w #ENC,&ADC10CTL0 ; End ADC config (ADC enable) bis.w #ADC10SC,&ADC10CTL0 ; Start ADC conversion bic.w #TAIFG,&TACTL ; Reset interrupt flag bis.w #TAIE,&TACTL ; Enable Timer Interrupts End_of_Mem reti ;------------------------------------------------------------------------------ ADC10_ISR; Handle an ADC Conversion Completion Event ;------------------------------------------------------------------------------ bic.w #ADC10SC,&ADC10CTL0 ; Reset ADC start condition bic.w #ENC,&ADC10CTL0 ; Disable ADC for config purposes mov.w ADC10MEM,R5 ; Copy ADC result to R5 bit.w #03000h,&ADC10CTL1 ; Test for data source (bit indicating A3) If A3 in, zf (not set)=0. ; if zf=0, was inch 3 (pin 5) Low Pressure. jnz Was_Inch_3 bis.w #01000h,R5 ; Label data sample with channel source (A0, Pin 2, High Pressure) jmp FlashErase Was_Inch_3 bis.w #02000h,R5 ; Label data sample with channel source (A3, Pin 5, Low Pressure) ;------------------------------------------------------------------------------ FlashErase; Can only erase 512Mb segments; Must erase before write; Code taken extensively from p320-323 ;------------------------------------------------------------------------------ bic.w #LPM3,SR ; Enable main clock, (interrupts turned off b/c EEIEX, EEI are reset, interrupts will be handled after flash done) NewSegTest cmp R6,&Const2 ;Compare next write address to each new segment address jeq L1 add #0200h,&Const2 cmp #0FE00h,&Const2 ; If next segment entry is the last (0FE00h), next address isn't a new segment jeq L3 jmp NewSegTest ; Flash Erase from within RAM ($03ff to 0200) L1 bit #BUSY,&FCTL3 ; Test BUSY jnz L1 ; Loop while busy mov #FWKEY+FSSEL_1,&FCTL2 ; Use MCLK for the Flash Module (1 MHz) mov #FWKEY+00002h,&FCTL2 ; Divide MCLK by 3 (FNx+1) to put freq b/w 250kHz and 450kHz req. mov #FWKEY,&FCTL3 ; Clear LOCK mov #FWKEY+ERASE,&FCTL1 ; Enable segment erase clr &Const2 ; Any memory location in that segment;***Must be in same segment as write location L2 bit #BUSY, &FCTL3 ; Test BUSY jnz L2 ; Loop while busy mov #FWKEY+LOCK,&FCTL3 ; Done, set LOCK ;------------------------------------------------------------------------------- FlashWrite; Write Data in R5 to Flash at address in R6; code taken extensively from p320ff (from within RAM-just extra safety) ;------------------------------------------------------------------------------- L3 bit #BUSY,&FCTL3 ; Test BUSY jnz L3 ; Loop while busy mov #FWKEY+FSSEL_1,&FCTL2 ; Use MCLK for the Flash Module (1 MHz) mov #FWKEY+00002h,&FCTL2 ; Divide MCLK by 3 (FNx+1) to put freq b/w 250kHz and 450kHz req. mov #FWKEY,&FCTL3 ; Clear LOCK mov #FWKEY+WRT,&FCTL1 ; Enable write mov R5,0(R6) ; Write data in R5 to the address stored in R6 ; mov @R6,R8 ; For Debugging incd R6 ; Double increment R6 L4 bit #BUSY,&FCTL3 ; Test BUSY jnz L4 ; Loop while busy mov #FWKEY,&FCTL1 ; Done. Clear WRT mov #FWKEY+LOCK,&FCTL3 ; Set LOCK xor.w #INCH_3,&ADC10CTL1 ; Toggle between ADC channel A0 and A3 bis.w #ENC,&ADC10CTL0 ; Enable ADC reti ;------------------------------------------------------------------------------ UART_ISR; Execute UART code upon button press ;------------------------------------------------------------------------------ bic.b #00000001b,&P2IE ; Disable P2.0 interrupt bic.w #ENC,&ADC10CTL0 ; Stop ADC bis.w #MC_0,&TACTL ; Stop Timer A mov #0ffffh,R4 ; Setup delay loop Delay dec R4 ; Delay resetting the interrupt to prevent keybounce jnz Delay bis.b #00010000b,&P2OUT ; Turn on P2.4 (UART Indicator) BEGIN_UART ;set sync bit bit.b #UCBUSY,&UCA0STAT ; Test for transmission in progress. jnz END_UART bis.b #UCA0TXIFG,&UC0IFG ; Clear all UCA and UCB interrupts call #ExtractNum END_UART bis.w #ENC,&ADC10CTL0 ; Enable ADC bis.w #MC_1,&TACTL ; Start Timer A bic.b #00000001b,&P2IFG ; Reset P2.0 Interrupt Flag bis.b #00000001b,&P2IE ; Enable P2.0 interrupt bic.b #00010000b,&P2OUT ; Turn off P2.4 (UART Indicator) reti ;------------------------------------------------------------------------------ ExtractNum; Turn Memory into Hex ;------------------------------------------------------------------------------ TOP_EXTRACT cmp R7,R9 ; Test for last memory address jz END_BLK_4 cmp R9,R6 ; Test for last significant memory address jz END_CUR_MEM cmp &StMemBlk2,R9 ; Test for start manual memory block 2 jz ST_BLK_2 cmp &StMemBlk3,R9 ; Test for start manual memory block 3 jz ST_BLK_3 cmp &StMemBlk4,R9 ; Test for end manual memory block 4 jz ST_BLK_4 EXTRACT_A mov @R9,R8 ; Load data to manipulate and #0f000h,R8 ; Isolate most significant dig mov #0000Dh,R4 ; Rotate data right 12 times (load loop with 13) EXTR_A_LP dec R4 jz END_A_LP ; If end of counter, exit loop clrc rrc R8 ; Push data to lowest nybble jmp EXTR_A_LP END_A_LP cmp #0000ah,R8 jge LETTER_A ; If dig is letter, manipulate as such add #00030h,R8 ; Add 30 to convert the number dig in R8 to ASCII jmp WAIT_A LETTER_A add #00037h,R8 ; Add 49 to convert the letter dig in R8 to ASCII WAIT_A bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438 jz WAIT_A ; If ready bit not set, keep waiting bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit SEND_A mov R8,&UCA0TXBUF ; Send this digit mov @R9,R8 ; Load data to manipulate and #00f00h,R8 ; Isolate second-most significant dig clrc rrc R8 ; Push data to lowest nybble rrc R8 rrc R8 rrc R8 rrc R8 rrc R8 rrc R8 rrc R8 cmp #0000ah,R8 jge LETTER_B ; If dig is letter, manipulate as such add #00030h,R8 ; Add 30 to convert the number dig in R8 to ASCII jmp WAIT_B LETTER_B add #00037h,R8 ; Add 49 to convert the letter dig in R8 to ASCII WAIT_B bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438 jz WAIT_B ; If ready bit not set, keep waiting bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit SEND_B mov R8,&UCA0TXBUF ; Send this digit mov @R9,R8 ; Load data to manipulate and #000f0h,R8 ; Isolate least significant dig clrc rrc R8 ; Push data to lowest nybble rrc R8 rrc R8 rrc R8 cmp #0000ah,R8 jge LETTER_C ; If dig is letter, manipulate as such add #00030h,R8 ; Add 30 to convert the number dig in R8 to ASCII jmp WAIT_C LETTER_C add #00037h,R8 ; Add 49 to convert the letter dig in R8 to ASCII WAIT_C bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438 jz WAIT_C ; If ready bit not set, keep waiting bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit SEND_C mov R8,&UCA0TXBUF ; Send this digit mov @R9,R8 ; Load data to manipulate and #0000fh,R8 ; Isolate least significant dig cmp #0000ah,R8 jge LETTER_D ; If dig is letter, manipulate as such add #00030h,R8 ; Add 30 to convert the number dig in R8 to ASCII jmp WAIT_D LETTER_D add #00037h,R8 ; Add 49 to convert the letter dig in R8 to ASCII WAIT_D bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438 jz WAIT_D ; If ready bit not set, keep waiting bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit SEND_D mov R8,&UCA0TXBUF ; Send this digit ;Bit test 4 or 5 at beginning of mem loc to determine if print space or not mov @R9,R8 ; Load current datum bit.w #02000h,R8 ; Check for even or odd jnz WAIT_SUFF ; If just printed first word on new line, print space before the next. WAIT_SPACE bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438 jz WAIT_SPACE bic.b #UCA0TXIFG,&IFG2 ; Wait, then give data suffix SEND_SPACE mov #020h,&UCA0TXBUF ; Send Space 0Dh incd R9 ; Move pointer to next memory location jmp TOP_EXTRACT WAIT_SUFF bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438 jz WAIT_SUFF bic.b #UCA0TXIFG,&IFG2 ; Wait, then give data suffix SEND_SUFF mov #0Dh,&UCA0TXBUF ; Send CR 0Dh as SUFFix WAIT_PSTSU bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438 jz WAIT_PSTSU bic.b #UCA0TXIFG,&IFG2 ; Wait, then give data suffix SEND_SUFF2 mov #0Ah,&UCA0TXBUF ; Send LF 0Ah as SUFFix WAIT_PSTSU2 bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438 jz WAIT_PSTSU2 incd R9 ; Move pointer to next memory location jmp TOP_EXTRACT ; Process next memory location ST_BLK_2 mov #045h,&EndBlkChar1 ; "E" End First Memory Block with "ZZZ1" mov #04Eh,&EndBlkChar2 ; "N" mov #044h,&EndBlkChar3 ; "D" mov #031h,&EndBlkChar4 ; "1" decd &StMemBlk2 ; So that on next memory output, the device will continue properly past this breakpoint jmp END_NUM_SEND ST_BLK_3 mov #045h,&EndBlkChar1 ; "E" End Second Memory Block with "ZZZ2" mov #04Eh,&EndBlkChar2 ; "N" mov #044h,&EndBlkChar3 ; "D" mov #032h,&EndBlkChar4 ; "2" decd &StMemBlk3 ; So that on next memory output, the device will continue properly past this breakpoint jmp END_NUM_SEND ST_BLK_4 mov #045h,&EndBlkChar1 ; "E" End Third Memory Block with "ZZZ3" mov #04Eh,&EndBlkChar2 ; "N" mov #044h,&EndBlkChar3 ; "D" mov #033h,&EndBlkChar4 ; "3" decd &StMemBlk4 ; So that on next memory output, the device will continue properly past this breakpoint jmp END_NUM_SEND END_BLK_4 mov #045h,&EndBlkChar1 ; "E" End Fourth Memory Block with "ZZZ4" mov #04Eh,&EndBlkChar2 ; "N" mov #044h,&EndBlkChar3 ; "D" mov #034h,&EndBlkChar4 ; "4" jmp END_NUM_SEND END_CUR_MEM mov #04Eh,&EndBlkChar1 ; "N" Memory Output Ended at Current Storage Location : Print "NOW!" mov #04Fh,&EndBlkChar2 ; "O" mov #057h,&EndBlkChar3 ; "W" mov #021h,&EndBlkChar4 ; "!" END_NUM_SEND WAIT_E bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Assimilated from p438 jz WAIT_E ; If ready bit not set, keep waiting bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit SEND_E_C1 mov &EndBlkChar1,&UCA0TXBUF ; Send EndBlkChar1 WAIT_F bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send jz WAIT_F ; bic.b #UCA0TXIFG,&IFG2 ; SEND_F_C2 mov &EndBlkChar2,&UCA0TXBUF ; Send EndBlkChar2 WAIT_G bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send jz WAIT_G ; bic.b #UCA0TXIFG,&IFG2 ; SEND_G_C3 mov &EndBlkChar3,&UCA0TXBUF ; Send EndBlkChar3 WAIT_H bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send jz WAIT_H ; bic.b #UCA0TXIFG,&IFG2 ; SEND_H_C4 mov &EndBlkChar4,&UCA0TXBUF ; Send EndBlkChar4 WAIT_I bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send jz WAIT_I ; bic.b #UCA0TXIFG,&IFG2 ; SEND_I mov #0Dh,&UCA0TXBUF ; Send CR 0Dh as SUFFix WAIT_J bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send jz WAIT_J bic.b #UCA0TXIFG,&IFG2 ; SEND_J mov #0Ah,&UCA0TXBUF ; Send LF 0Ah as SUFFix WAIT_K bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send jz WAIT_K ret ;------------------------------------------------------------------------------ ; Interrupt Vectors ;------------------------------------------------------------------------------ .sect ".reset" ; MSP430 RESET Vector .short RESET ; .sect ".int08" ; Timer_A Vector .short TIMER_A_ISR .sect ".int05" ; ADC10 Vector .short ADC10_ISR .sect ".int03" ; Port 2 Vector .short UART_ISR ; .end
Well documented code. :)
Well, it's assembly code and difficult to understand without plenty of comments, but I've seen assembly code with less than one comment per function. :(
There are, however, some things I noticed (nothing critical):
You use R6 for the storage address. You set it in main and use it inside ISRs. This is dangerous. It works in your code, because you never use R6 for somethign else in the whole project, but as soon as the project grows, this may break easily.
It's better to use a global variable for this. Besides the additional two clock cycles, it makes no difference whether you use a register or a variable. And is safer.
During interrupts, IRQs are globally disabled. So no need to clear TAIE.
You could control the ADC by hardware. Use TA0CCR1 as ADC trigger. If you do not set ADC10SHP, it can even control the sampling time.
Also, you do not need to reprogram the ADC. Just set it to sample a sequence (A4 to A0) with twice the sampling frequency and skip the A1/A2 results. The samples will come in without any need for a timer ISR. Sampling and conversion will start with the exact timer tick.
About your problem, well, I don't see an obvious reason. However, you store the values to flash and submit them ina burst on a trigger. It is possible that your flash writing somehow fails. It may be caused by an insufficient power supply (minimum flashing voltage ensured when the flashing process starts drawing its mA?)
It is possible that due to insufficient power supply, the segments aren't properly erased and soem bits remain '0' or are at the edge of skippign back to '0'.
Also, did you calculate the maximum programming time? You write to flash word by word. Depending on flash clock etc, this may exceed the maximum programing time of a segment and may cause some bits to switch from 1 to 0.
You might try to use the ram as (smaller) data buffer isntead of flash and check whether the problem persists.
Thanks for the thoughtful input. About the things you propose... I think the flash memory is default "1", so failing to finish the write would seem to leave 1's instead of 0's. The timing is really generous. The ADC has virtually the longest possible calculation time, and the flash write doesn't have to work except for once every second or more. And, the time can't be too long because it works on the development board fine.
Yes. However, if the flash hasn't been properly erased (remenber, the flash cells internally store a charge, whcih is an analog value, even if it is turned into a digital one later by sort of a comparator), it may be that cells that were 'erased' to '1' then turn back to '0' again due to e.g. leakage currents during a programming cycle. The programming voltage is applies to all cells of a flash block. If it is applied for too long, all cells my turn from 1 to 0, even if they aren't intended to be programmed and were properly erased previously.Robert Snell said:I think the flash memory is default "1", so failing to finish the write would seem to leave 1's instead of 0's
The critical part is the total time any programming voltage is applied to a block of flash cells - whether the cell is written to or not.Robert Snell said:The timing is really generous
You could try collecting one block of data in ram before writing it as a whole in a burst. Sicne your program needs to cope with a slooow erase operation if needed, the additional time of flashing a whole block (128 bytes on 5x devices, 64 on the others, IIRC) or even a whole segment (512 bytes) won't add much to the 'dead time' between two sampling operations. Even less if the flashing is done from a RAM function, so burst mode can be used.
I once tried to incrementally clear individual bits in a segment (sort of a progress counter) and ended up with bits turnign zero which weren't touched during write at all. Of course I significantly exceeded the maimum cumulative programming time.
I don't know how the flash controller works internally. I don't even know whether it is multi-level flash (using four charge 'ranges' per cell to represent two bits). Difference s in supply voltage and supply voltage stability can make a difference here, so a fragile/out-of-spec oepration may still work in one but fail in another environment. Remember, the specs are the 'safe' area. It doesn't mean that leaving this area will result in immediate and guaranteed failure. Often, things still work most of the time. Until they fail when you don't expect it. :)Robert Snell said:And, the time can't be too long because it works on the development board fine.
The reason I don't think it's a flash erase/write problem is that...when I program the uC and run it from the LaunchPad (USB powered) and send the UART out data through my ADM3202 chip to my serial cable into my computer, the data is fine. That makes me think it's not a software issue but a power supply issue.
At least it seems to indicate that the power supply is part of the problem. It can still be a software problem but it doesn't appear ont eh LaunchPads power supply. Or it is a power supply problem and your code doesn't properly detect it (e.g. checking for a programming voltage fail signal after doing the flashing, that indicated a significant change in VCC during the flash operation). Or a combination of both.Robert Snell said:That makes me think it's not a software issue but a power supply issue.
I think I finally solved it! I wasn't resetting Const2 (which was intended to tell me when I had reached a new segment of memory which needed to be erased before writing to it). On the first run through the program, Const2 got changed and never replaced. See fixed code below. I will be testing to make sure this fixes it for good.
;*******************************************************************************
; Title: Altitude Data Logger
; Author: Rob Snell
; Date: April 20, 2012 //Modified September 13, 2012 RS
; Purpose: Senior Project-Design Problems, Bob Jones University, Spring 2012
;
; Very basic code shell was MSP320G2xx2 Demo by D. Dang, TI Inc Dec 2010- WDT, Toggle P1.0, Interval Overflow ISR, 32kHz ACLK
;*******************************************************************************
.cdecls C,LIST, "msp430g2553.h"
.bss Const2,2
.bss StMemBlk2,2 ; Do not use 'comma one' at the end of these .bss things. They glitched when I tried to use them for byte storage.
.bss StMemBlk3,2
.bss StMemBlk4,2
.bss EndBlkChar1,2
.bss EndBlkChar2,2
.bss EndBlkChar3,2
.bss EndBlkChar4,2
;------------------------------------------------------------------------------
.text ; Program Start
;------------------------------------------------------------------------------
RESET mov.w #0280h,SP ; Initialize stackpointer
mov.w #WDTPW+WDTHOLD,&WDTCTL ; Turn off Watchdog Timer
CLKSetup mov.b #LFXT1S_2,&BCSCTL3 ; Since XTS is 0 by default, this selects VLOCLK to source ACLK (12 kHz roughly)
; Using ACLK for Timer (See Timer_A_Setup) which interrupts and causes ADC
clr.b &DCOCTL ; Select lowest DCOx and MODx settings (these three lines copied from pp283-4)
mov.b &CALBC1_1MHZ,&BCSCTL1 ; Set range
mov.b &CALDCO_1MHZ,&DCOCTL ; Set DCO step + modulation (1 MHz)
WDTSetup ; mov.w #WDT_ADLY_250,&WDTCTL ; WDT 250ms, ACLK, interval timer
PortSetup
mov.b #00001001b,&ADC10AE0 ; Enable analog input on {now A0 (Pin 2, P1.0) and A3 (Pin 5, P1.3)}; should i enable only when sampling?
bic.b #00000001b,&P2DIR ; Set P2.0 to input mode (for prompting UART transfer)
bis.b #00000001b,&P2REN ; Enable P2.0 pullup/down resistor
bic.b #00000001b,&P2OUT ; Set P2.0 internal resistor to pulldown
bis.b #00000001b,&P2IE ; Enable P2.0 interrupt
bic.b #00000001b,&P2IES ; Set P2.0 Transition Trigger to 'low-to-high'
bic.b #00000001b,&P2IFG ; Reset P2.0 Interrupt Flag
; bis.b #WDTIE,&IE1 ; Enable WDT interrupt
bic.b #00111000b,&P2OUT ; Turn off P2.5 & P2.3
bis.b #00111000b,&P2DIR ; Set P2.5 & (P2.4 for UART out indicator) & P2.3 to output (for indicating which sensor is being stored)
;------------------------------------------------------------------------------
Timer_A_Setup;
;------------------------------------------------------------------------------
bis.w #TASSEL_1+TAIE,&TACTL ; Select ACLK to power Timer A (12kHz) + Enable Interrupt
mov.w #01000h,&TACCR0 ; Count limit is FFFF is apx 4.8 sec; 7460h for 4.5 hrs; 3393 for ~1 sample/sec
;------------------------------------------------------------------------------
ADC_Setup; Setup ADC functionality
;------------------------------------------------------------------------------
and.w #0fff0h,&ADC10CTL0 ; Allow ADC config, clear int flag, disable int// automatic on system reset //
and.w #0fdffh,&ADC10CTL0 ; Turn off Reference Output to save power
bis.w #ADC10SSEL_1,&ADC10CTL1 ; Changed this to select ACLK (~12kHz) instead of /// Select ADC10OSC for ADC clock source (~ 5 MHz)
bis.w #ADC10DIV_7,&ADC10CTL1 ; Divide clock by 8 (so 625kHz) (for longer sampling period)
bis.w #CONSEQ_0,&ADC10CTL1 ; Select single-channel single-conversion mode;
bis.w #SREF_0,&ADC10CTL0 ; Set Vcc and Vss as limits for conversion;
bis.w #ADC10ON,&ADC10CTL0 ; Enable conversion core
bis.w #ADC10SHT_3,&ADC10CTL0 ; Use 64 ADC10OSC cycles for sample input (charge sampling capacitor) // Worth looking into again. p552
bis.w #INCH_10,&ADC10CTL1 ; Set ADC channel to temperature sensor
bis.w #ADC10IE,&ADC10CTL0 ; Enable ADC interrupts
bis.w #ENC,&ADC10CTL0 ; End ADC config
mov #0C400h,R6 ; C400; Start address for ADC data storage (Must be initialized as first address in a segment!)
mov #0C400h,&Const2 ; C400; Load first segment entry boundary for flash erase procedure (Do not change!)
mov #0FE00h,R7 ; End (First illegal at end) address for ADC data storage
;------------------------------------------------------------------------------
UART_Setup; Setup Serial Functionality
;------------------------------------------------------------------------------
mov #0D200h,&StMemBlk2 ; Configure Manual Memory Blocks
mov #0E000h,&StMemBlk3 ;
mov #0EE00h,&StMemBlk4 ;
bis.b #UCSWRST,&UCA0CTL1 ; enable UART config
bis.b #00000100b,&P1SEL ; Configure P1.2 for UART use
bis.b #00000100b,&P1SEL2 ;
; bic.b #UCSWRST,&UCB0CTL1 ; Allow turning off UCB interrupt
; bic.b #UC7BIT,&UCA0CTL0 (default 8-bit data mode, no parity)
bis.b #UCSSEL_3,&UCA0CTL1 ; Clock source is SMCLK (1 MHz)
mov.b #00h,&UCA0BR1 ; Clear top bits of prescaler
mov.b #104,&UCA0BR0 ; Copied from byu website. See history mov.b #06h,&UCA0BR0 ; Prescale divide by 6 (UCBRx);;
mov.b #UCBRS0,&UCA0MCTL ; Copied from byu website mov.b #UCBRF_8+UCOS16,&UCA0MCTL ; Set first stage modulator (to 8) and enable oversampling baud rate generation (fast clock source) [9600 baud];;
mov R6,R9 ; Set iniitial TX address
bic.b #UCSWRST,&UCA0CTL1 ; end UART config
;------------------------------------------------------------------------------
Timer_A_Start;
;------------------------------------------------------------------------------
bis.w #MC_1,&TACTL ; Count up to TACCR0
bis.w #TAIE,&TACTL ; Enable Timer Interrupts
;------------------------------------------------------------------------------
Main_Prog_Run;
;------------------------------------------------------------------------------
Mainloop bis.b #00100000b,&P2OUT ; Initialize P2.5 so toggle is correct
bic.w #OSCOFF,SR ; Confirm ACLK remains on when LPM3 set in next instruction
bis.w #LPM3+GIE,SR ; Enter LPM3, interrupts enabled
nop ; Required only for debugger
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
TIMER_A_ISR; Handle a Timer Overflow
;------------------------------------------------------------------------------
bic.w #TAIE,&TACTL ; Disable Timer Interrupts
cmp R6,R7 ; Test for last memory location
jz End_of_Mem
xor.b #00101000b,&P2OUT ; Toggle P2.5 & P2.3
bis.w #ENC,&ADC10CTL0 ; End ADC config (ADC enable)
bis.w #ADC10SC,&ADC10CTL0 ; Start ADC conversion
bic.w #TAIFG,&TACTL ; Reset interrupt flag
bis.w #TAIE,&TACTL ; Enable Timer Interrupts
End_of_Mem reti
;------------------------------------------------------------------------------
ADC10_ISR; Handle an ADC Conversion Completion Event
;------------------------------------------------------------------------------
bic.w #ADC10SC,&ADC10CTL0 ; Reset ADC start condition
bic.w #ENC,&ADC10CTL0 ; Disable ADC for config purposes
mov.w ADC10MEM,R5 ; Copy ADC result to R5
; bit.w #03000h,&ADC10CTL1 ; Test for data source (bit indicating A3) If A3 in, zf (not set)=0.
; ; if zf=0, was inch 3 (pin 5) Low Pressure.
; jnz Was_Inch_3
; bis.w #01000h,R5 ; Label data sample with channel source (A0, Pin 2, High Pressure)
; jmp FlashErase
Was_Inch_3 bis.w #02000h,R5 ; Label data with #02 for Temp Data; Label data sample with channel source (A3, Pin 5, Low Pressure)
;------------------------------------------------------------------------------
FlashErase; Can only erase 512Mb segments; Must erase before write; Code taken extensively from p320-323
;------------------------------------------------------------------------------
bic.w #LPM3,SR ; Enable main clock, (interrupts turned off b/c EEIEX, EEI are reset, interrupts will be handled after flash done)
cmp R6,&Const2 ; Compare next write address to each new segment address
jeq L1 ; If new segment, erase first.
NewSegTest add #0200h,&Const2 ; Jump to next segment boundary for testing purposes
cmp #0FE00h,&Const2 ; If next segment entry is the last (0FE00h), next address isn't a new segment
jeq L3
cmp R6,&Const2 ; Compare next write address to each new segment address
jeq L1 ; If new segment, erase first.
jmp NewSegTest
; Flash Erase from within RAM ($03ff to 0200)
L1 bit #BUSY,&FCTL3 ; Test BUSY
jnz L1 ; Loop while busy
mov #FWKEY+FSSEL_1,&FCTL2 ; Use MCLK for the Flash Module (1 MHz)
mov #FWKEY+00002h,&FCTL2 ; Divide MCLK by 3 (FNx+1) to put freq b/w 250kHz and 450kHz req.
mov #FWKEY,&FCTL3 ; Clear LOCK
mov #FWKEY+ERASE,&FCTL1 ; Enable segment erase
clr &Const2 ; //Was &Const2//Any memory location in that segment;***Must be in same segment as write location
L2 bit #BUSY, &FCTL3 ; Test BUSY
jnz L2 ; Loop while busy
mov #FWKEY+LOCK,&FCTL3 ; Done, set LOCK
;-------------------------------------------------------------------------------
FlashWrite; Write Data in R5 to Flash at address in R6; code taken extensively from p320ff (from within RAM-just extra safety)
;-------------------------------------------------------------------------------
L3 bit #BUSY,&FCTL3 ; Test BUSY
jnz L3 ; Loop while busy
mov #FWKEY+FSSEL_1,&FCTL2 ; Use MCLK for the Flash Module (1 MHz)
mov #FWKEY+00002h,&FCTL2 ; Divide MCLK by 3 (FNx+1) to put freq b/w 250kHz and 450kHz req.
mov #FWKEY,&FCTL3 ; Clear LOCK
mov #FWKEY+WRT,&FCTL1 ; Enable write
mov R5,0(R6) ; Write data in R5 to the address stored in R6
; mov @R6,R8 ; For Debugging
incd R6 ; Double increment R6
L4 bit #BUSY,&FCTL3 ; Test BUSY
jnz L4 ; Loop while busy
mov #FWKEY,&FCTL1 ; Done. Clear WRT
mov #FWKEY+LOCK,&FCTL3 ; Set LOCK
; xor.w #INCH_3,&ADC10CTL1 ; Toggle between ADC channel A0 and A3
mov #0C400h,&Const2 ; Prepare for next Flash erase by resetting the segment boundary variable
bis.w #ENC,&ADC10CTL0 ; Enable ADC
reti
;------------------------------------------------------------------------------
UART_ISR; Execute UART code upon button press
;------------------------------------------------------------------------------
bic.b #00000001b,&P2IE ; Disable P2.0 interrupt
bic.w #ENC,&ADC10CTL0 ; Stop ADC
bis.w #MC_0,&TACTL ; Stop Timer A
mov #0ffffh,R4 ; Setup delay loop
Delay dec R4 ; Delay resetting the interrupt to prevent keybounce
jnz Delay
bis.b #00010000b,&P2OUT ; Turn on P2.4 (UART Indicator)
BEGIN_UART ;set sync bit
bit.b #UCBUSY,&UCA0STAT ; Test for transmission in progress.
jnz END_UART
bis.b #UCA0TXIFG,&UC0IFG ; Clear all UCA and UCB interrupts
call #ExtractNum
END_UART bis.w #ENC,&ADC10CTL0 ; Enable ADC
bis.w #MC_1,&TACTL ; Start Timer A
bic.b #00000001b,&P2IFG ; Reset P2.0 Interrupt Flag
bis.b #00000001b,&P2IE ; Enable P2.0 interrupt
bic.b #00010000b,&P2OUT ; Turn off P2.4 (UART Indicator)
reti
;------------------------------------------------------------------------------
ExtractNum; Turn Memory into Hex
;------------------------------------------------------------------------------
TOP_EXTRACT
cmp R7,R9 ; Test for last memory address
jz END_BLK_4
cmp R9,R6 ; Test for last significant memory address
jz END_CUR_MEM
cmp &StMemBlk2,R9 ; Test for start manual memory block 2
jz ST_BLK_2
cmp &StMemBlk3,R9 ; Test for start manual memory block 3
jz ST_BLK_3
cmp &StMemBlk4,R9 ; Test for end manual memory block 4
jz ST_BLK_4
EXTRACT_A mov @R9,R8 ; Load data to manipulate
and #0f000h,R8 ; Isolate most significant dig
mov #0000Dh,R4 ; Rotate data right 12 times (load loop with 13)
EXTR_A_LP dec R4
jz END_A_LP ; If end of counter, exit loop
clrc
rrc R8 ; Push data to lowest nybble
jmp EXTR_A_LP
END_A_LP cmp #0000ah,R8
jge LETTER_A ; If dig is letter, manipulate as such
add #00030h,R8 ; Add 30 to convert the number dig in R8 to ASCII
jmp WAIT_A
LETTER_A add #00037h,R8 ; Add 49 to convert the letter dig in R8 to ASCII
WAIT_A bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_A ; If ready bit not set, keep waiting
bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit
SEND_A mov R8,&UCA0TXBUF ; Send this digit
mov @R9,R8 ; Load data to manipulate
and #00f00h,R8 ; Isolate second-most significant dig
clrc
rrc R8 ; Push data to lowest nybble
rrc R8
rrc R8
rrc R8
rrc R8
rrc R8
rrc R8
rrc R8
cmp #0000ah,R8
jge LETTER_B ; If dig is letter, manipulate as such
add #00030h,R8 ; Add 30 to convert the number dig in R8 to ASCII
jmp WAIT_B
LETTER_B add #00037h,R8 ; Add 49 to convert the letter dig in R8 to ASCII
WAIT_B bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_B ; If ready bit not set, keep waiting
bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit
SEND_B mov R8,&UCA0TXBUF ; Send this digit
mov @R9,R8 ; Load data to manipulate
and #000f0h,R8 ; Isolate least significant dig
clrc
rrc R8 ; Push data to lowest nybble
rrc R8
rrc R8
rrc R8
cmp #0000ah,R8
jge LETTER_C ; If dig is letter, manipulate as such
add #00030h,R8 ; Add 30 to convert the number dig in R8 to ASCII
jmp WAIT_C
LETTER_C add #00037h,R8 ; Add 49 to convert the letter dig in R8 to ASCII
WAIT_C bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_C ; If ready bit not set, keep waiting
bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit
SEND_C mov R8,&UCA0TXBUF ; Send this digit
mov @R9,R8 ; Load data to manipulate
and #0000fh,R8 ; Isolate least significant dig
cmp #0000ah,R8
jge LETTER_D ; If dig is letter, manipulate as such
add #00030h,R8 ; Add 30 to convert the number dig in R8 to ASCII
jmp WAIT_D
LETTER_D add #00037h,R8 ; Add 49 to convert the letter dig in R8 to ASCII
WAIT_D bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_D ; If ready bit not set, keep waiting
bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit
SEND_D mov R8,&UCA0TXBUF ; Send this digit
;Bit test 4 or 5 at beginning of mem loc to determine if print space or not
mov @R9,R8 ; Load current datum
bit.w #02000h,R8 ; Check for even or odd
jnz WAIT_SUFF ; If just printed first word on new line, print space before the next.
WAIT_SPACE bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_SPACE
bic.b #UCA0TXIFG,&IFG2 ; Wait, then give data suffix
SEND_SPACE mov #020h,&UCA0TXBUF ; Send Space 0Dh
incd R9 ; Move pointer to next memory location
jmp TOP_EXTRACT
WAIT_SUFF bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_SUFF
bic.b #UCA0TXIFG,&IFG2 ; Wait, then give data suffix
SEND_SUFF mov #0Dh,&UCA0TXBUF ; Send CR 0Dh as SUFFix
WAIT_PSTSU bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_PSTSU
bic.b #UCA0TXIFG,&IFG2 ; Wait, then give data suffix
SEND_SUFF2 mov #0Ah,&UCA0TXBUF ; Send LF 0Ah as SUFFix
WAIT_PSTSU2 bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_PSTSU2
incd R9 ; Move pointer to next memory location
jmp TOP_EXTRACT ; Process next memory location
ST_BLK_2 mov #045h,&EndBlkChar1 ; "E" End First Memory Block with "ZZZ1"
mov #04Eh,&EndBlkChar2 ; "N"
mov #044h,&EndBlkChar3 ; "D"
mov #031h,&EndBlkChar4 ; "1"
decd &StMemBlk2 ; So that on next memory output, the device will continue properly past this breakpoint
jmp END_NUM_SEND
ST_BLK_3 mov #045h,&EndBlkChar1 ; "E" End Second Memory Block with "ZZZ2"
mov #04Eh,&EndBlkChar2 ; "N"
mov #044h,&EndBlkChar3 ; "D"
mov #032h,&EndBlkChar4 ; "2"
decd &StMemBlk3 ; So that on next memory output, the device will continue properly past this breakpoint
jmp END_NUM_SEND
ST_BLK_4 mov #045h,&EndBlkChar1 ; "E" End Third Memory Block with "ZZZ3"
mov #04Eh,&EndBlkChar2 ; "N"
mov #044h,&EndBlkChar3 ; "D"
mov #033h,&EndBlkChar4 ; "3"
decd &StMemBlk4 ; So that on next memory output, the device will continue properly past this breakpoint
jmp END_NUM_SEND
END_BLK_4 mov #045h,&EndBlkChar1 ; "E" End Fourth Memory Block with "ZZZ4"
mov #04Eh,&EndBlkChar2 ; "N"
mov #044h,&EndBlkChar3 ; "D"
mov #034h,&EndBlkChar4 ; "4"
mov #0C400h, R9 ; Allows UART button to send the whole memory data serially again.
jmp END_NUM_SEND
END_CUR_MEM mov #04Eh,&EndBlkChar1 ; "N" Memory Output Ended at Current Storage Location : Print "NOW!"
mov #04Fh,&EndBlkChar2 ; "O"
mov #057h,&EndBlkChar3 ; "W"
mov #021h,&EndBlkChar4 ; "!"
END_NUM_SEND
WAIT_E bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Assimilated from p438
jz WAIT_E ; If ready bit not set, keep waiting
bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit
SEND_E_C1 mov &EndBlkChar1,&UCA0TXBUF ; Send EndBlkChar1
WAIT_F bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send
jz WAIT_F ;
bic.b #UCA0TXIFG,&IFG2 ;
SEND_F_C2 mov &EndBlkChar2,&UCA0TXBUF ; Send EndBlkChar2
WAIT_G bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send
jz WAIT_G ;
bic.b #UCA0TXIFG,&IFG2 ;
SEND_G_C3 mov &EndBlkChar3,&UCA0TXBUF ; Send EndBlkChar3
WAIT_H bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send
jz WAIT_H ;
bic.b #UCA0TXIFG,&IFG2 ;
SEND_H_C4 mov &EndBlkChar4,&UCA0TXBUF ; Send EndBlkChar4
WAIT_I bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send
jz WAIT_I ;
bic.b #UCA0TXIFG,&IFG2 ;
SEND_I mov #0Dh,&UCA0TXBUF ; Send CR 0Dh as SUFFix
WAIT_J bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send
jz WAIT_J
bic.b #UCA0TXIFG,&IFG2 ;
SEND_J mov #0Ah,&UCA0TXBUF ; Send LF 0Ah as SUFFix
WAIT_K bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send
jz WAIT_K
ret
;------------------------------------------------------------------------------
; Interrupt Vectors
;------------------------------------------------------------------------------
.sect ".reset" ; MSP430 RESET Vector
.short RESET ;
.sect ".int08" ; Timer_A Vector
.short TIMER_A_ISR
.sect ".int05" ; ADC10 Vector
.short ADC10_ISR
.sect ".int03" ; Port 2 Vector
.short UART_ISR ;
.end
Correction. I also had problems perhaps because of the way I was using my .bss variables. This code seems to be working properly after several successive runs.
;*******************************************************************************
; Title: Altitude Data Logger
; Author: Rob Snell
; Date: April 20, 2012
; Purpose: Senior Project-Design Problems, Bob Jones University, Spring 2012
;
; Very basic code shell was MSP320G2xx2 Demo by D. Dang, TI Inc Dec 2010- WDT, Toggle P1.0, Interval Overflow ISR, 32kHz ACLK
;*******************************************************************************
.cdecls C,LIST, "msp430g2553.h"
;.bss Const2,2
;.bss StMemBlk2,2 ; Do not use 'comma one' at the end of these .bss things. They glitched when I tried to use them for byte storage.
;.bss StMemBlk3,2
;;.bss StMemBlk4,2
; .bss EndBlkChar1,2
;.bss EndBlkChar2,2
; .bss EndBlkChar3,2
;.bss EndBlkChar4,2
;------------------------------------------------------------------------------
.text ; Program Start
;------------------------------------------------------------------------------
RESET mov.w #0280h,SP ; Initialize stackpointer
mov.w #WDTPW+WDTHOLD,&WDTCTL ; Turn off Watchdog Timer
CLKSetup mov.b #LFXT1S_2,&BCSCTL3 ; Since XTS is 0 by default, this selects VLOCLK to source ACLK (12 kHz roughly)
; Using ACLK for Timer (See Timer_A_Setup) which interrupts and causes ADC
clr.b &DCOCTL ; Select lowest DCOx and MODx settings (these three lines copied from pp283-4)
mov.b &CALBC1_1MHZ,&BCSCTL1 ; Set range
mov.b &CALDCO_1MHZ,&DCOCTL ; Set DCO step + modulation (1 MHz)
WDTSetup ; mov.w #WDT_ADLY_250,&WDTCTL ; WDT 250ms, ACLK, interval timer
PortSetup
mov.b #00001001b,&ADC10AE0 ; Enable analog input on {now A0 (Pin 2, P1.0) and A3 (Pin 5, P1.3)}; should i enable only when sampling?
bic.b #00000001b,&P2DIR ; Set P2.0 to input mode (for prompting UART transfer)
bis.b #00000001b,&P2REN ; Enable P2.0 pullup/down resistor
bic.b #00000001b,&P2OUT ; Set P2.0 internal resistor to pulldown
bis.b #00000001b,&P2IE ; Enable P2.0 interrupt
bic.b #00000001b,&P2IES ; Set P2.0 Transition Trigger to 'low-to-high'
bic.b #00000001b,&P2IFG ; Reset P2.0 Interrupt Flag
; bis.b #WDTIE,&IE1 ; Enable WDT interrupt
bic.b #00111000b,&P2OUT ; Turn off P2.5 & P2.3
bis.b #00111000b,&P2DIR ; Set P2.5 & (P2.4 for UART out indicator) & P2.3 to output (for indicating which sensor is being stored)
;------------------------------------------------------------------------------
Timer_A_Setup;
;------------------------------------------------------------------------------
bis.w #TASSEL_1+TAIE,&TACTL ; Select ACLK to power Timer A (12kHz) + Enable Interrupt
mov.w #00100h,&TACCR0 ; Count limit is FFFF is apx 4.8 sec; 7460h for 4.5 hrs; 3393 for ~1 sample/sec
;------------------------------------------------------------------------------
ADC_Setup; Setup ADC functionality
;------------------------------------------------------------------------------
and.w #0fff0h,&ADC10CTL0 ; Allow ADC config, clear int flag, disable int// automatic on system reset //
and.w #0fdffh,&ADC10CTL0 ; Turn off Reference Output to save power
bis.w #ADC10SSEL_1,&ADC10CTL1 ; Changed this to select ACLK (~12kHz) instead of /// Select ADC10OSC for ADC clock source (~ 5 MHz)
bis.w #ADC10DIV_7,&ADC10CTL1 ; Divide clock by 8 (so 625kHz) (for longer sampling period)
bis.w #CONSEQ_0,&ADC10CTL1 ; Select single-channel single-conversion mode;
bis.w #SREF_0,&ADC10CTL0 ; Set Vcc and Vss as limits for conversion;
bis.w #ADC10ON,&ADC10CTL0 ; Enable conversion core
bis.w #ADC10SHT_3,&ADC10CTL0 ; Use 64 ADC10OSC cycles for sample input (charge sampling capacitor) // Worth looking into again. p552
bis.w #INCH_10,&ADC10CTL1 ; Set ADC channel to temperature sensor
bis.w #ADC10IE,&ADC10CTL0 ; Enable ADC interrupts
bis.w #ENC,&ADC10CTL0 ; End ADC config
mov #0C400h,R6 ; C400; Start address for ADC data storage (Must be initialized as first address in a segment!)
mov #0C400h,R10 ; C400; Load first segment entry boundary for flash erase procedure (Do not change!)
mov #0FE00h,R7 ; End (First illegal at end) address for ADC data storage
;------------------------------------------------------------------------------
UART_Setup; Setup Serial Functionality
;------------------------------------------------------------------------------
;mov #0D200h,&StMemBlk2 ; Configure Manual Memory Blocks
;mov #0E000h,&StMemBlk3 ;
;mov #0EE00h,&StMemBlk4 ;
bis.b #UCSWRST,&UCA0CTL1 ; enable UART config
bis.b #00000100b,&P1SEL ; Configure P1.2 for UART use
bis.b #00000100b,&P1SEL2 ;
; bic.b #UCSWRST,&UCB0CTL1 ; Allow turning off UCB interrupt
; bic.b #UC7BIT,&UCA0CTL0 (default 8-bit data mode, no parity)
bis.b #UCSSEL_3,&UCA0CTL1 ; Clock source is SMCLK (1 MHz)
mov.b #00h,&UCA0BR1 ; Clear top bits of prescaler
mov.b #104,&UCA0BR0 ; Copied from byu website. See history mov.b #06h,&UCA0BR0 ; Prescale divide by 6 (UCBRx);;
mov.b #UCBRS0,&UCA0MCTL ; Copied from byu website mov.b #UCBRF_8+UCOS16,&UCA0MCTL ; Set first stage modulator (to 8) and enable oversampling baud rate generation (fast clock source) [9600 baud];;
mov R6,R9 ; Set iniitial TX address
bic.b #UCSWRST,&UCA0CTL1 ; end UART config
;------------------------------------------------------------------------------
Timer_A_Start;
;------------------------------------------------------------------------------
bis.w #MC_1,&TACTL ; Count up to TACCR0
bis.w #TAIE,&TACTL ; Enable Timer Interrupts
;------------------------------------------------------------------------------
Main_Prog_Run;
;------------------------------------------------------------------------------
Mainloop bis.b #00100000b,&P2OUT ; Initialize P2.5 so toggle is correct
bic.w #OSCOFF,SR ; Confirm ACLK remains on when LPM3 set in next instruction
bis.w #LPM3+GIE,SR ; Enter LPM3, interrupts enabled
nop ; Required only for debugger
;------------------------------------------------------------------------------
;------------------------------------------------------------------------------
TIMER_A_ISR; Handle a Timer Overflow
;------------------------------------------------------------------------------
bic.w #TAIE,&TACTL ; Disable Timer Interrupts
cmp R6,R7 ; Test for last memory location
jz End_of_Mem
xor.b #00101000b,&P2OUT ; Toggle P2.5 & P2.3
bis.w #ENC,&ADC10CTL0 ; End ADC config (ADC enable)
bis.w #ADC10SC,&ADC10CTL0 ; Start ADC conversion
bic.w #TAIFG,&TACTL ; Reset interrupt flag
bis.w #TAIE,&TACTL ; Enable Timer Interrupts
End_of_Mem reti
;------------------------------------------------------------------------------
ADC10_ISR; Handle an ADC Conversion Completion Event
;------------------------------------------------------------------------------
bic.w #ADC10SC,&ADC10CTL0 ; Reset ADC start condition
bic.w #ENC,&ADC10CTL0 ; Disable ADC for config purposes
mov.w ADC10MEM,R5 ; Copy ADC result to R5
; bit.w #03000h,&ADC10CTL1 ; Test for data source (bit indicating A3) If A3 in, zf (not set)=0.
; ; if zf=0, was inch 3 (pin 5) Low Pressure.
; jnz Was_Inch_3
; bis.w #01000h,R5 ; Label data sample with channel source (A0, Pin 2, High Pressure)
; jmp FlashErase
Was_Inch_3 bis.w #02000h,R5 ; Label data with #02 for Temp Data; Label data sample with channel source (A3, Pin 5, Low Pressure)
;------------------------------------------------------------------------------
FlashErase; Can only erase 512Mb segments; Must erase before write; Code taken extensively from p320-323
;------------------------------------------------------------------------------
bic.w #LPM3,SR ; Enable main clock, (interrupts turned off b/c EEIEX, EEI are reset, interrupts will be handled after flash done)
cmp R6,R10 ; Compare next write address to each new segment address
jeq L1 ; If new segment, erase first.
NewSegTest add #0200h,R10 ; Jump to next segment boundary for testing purposes
cmp #0FE00h,R10 ; If next segment entry is the last (0FE00h), next address isn't a new segment
jeq L3
cmp R6,R10 ; Compare next write address to each new segment address
jeq L1 ; If new segment, erase first.
jmp NewSegTest
; Flash Erase from within RAM ($03ff to 0200)
L1 bit #BUSY,&FCTL3 ; Test BUSY
jnz L1 ; Loop while busy
mov #FWKEY+FSSEL_1,&FCTL2 ; Use MCLK for the Flash Module (1 MHz)
mov #FWKEY+00002h,&FCTL2 ; Divide MCLK by 3 (FNx+1) to put freq b/w 250kHz and 450kHz req.
mov #FWKEY,&FCTL3 ; Clear LOCK
mov #FWKEY+ERASE,&FCTL1 ; Enable segment erase
mov #00000h,0(R10) ; Any memory location in that segment;***Must be in same segment as write location
L2 bit #BUSY, &FCTL3 ; Test BUSY
jnz L2 ; Loop while busy
mov #FWKEY+LOCK,&FCTL3 ; Done, set LOCK
;-------------------------------------------------------------------------------
FlashWrite; Write Data in R5 to Flash at address in R6; code taken extensively from p320ff (from within RAM-just extra safety)
;-------------------------------------------------------------------------------
L3 bit #BUSY,&FCTL3 ; Test BUSY
jnz L3 ; Loop while busy
mov #FWKEY+FSSEL_1,&FCTL2 ; Use MCLK for the Flash Module (1 MHz)
mov #FWKEY+00002h,&FCTL2 ; Divide MCLK by 3 (FNx+1) to put freq b/w 250kHz and 450kHz req.
mov #FWKEY,&FCTL3 ; Clear LOCK
mov #FWKEY+WRT,&FCTL1 ; Enable write
mov R5,0(R6) ; Write data in R5 to the address stored in R6
; mov @R6,R8 ; For Debugging
incd R6 ; Double increment R6
L4 bit #BUSY,&FCTL3 ; Test BUSY
jnz L4 ; Loop while busy
mov #FWKEY,&FCTL1 ; Done. Clear WRT
mov #FWKEY+LOCK,&FCTL3 ; Set LOCK
; xor.w #INCH_3,&ADC10CTL1 ; Toggle between ADC channel A0 and A3
mov #0C400h,R10 ; Prepare for next Flash erase by resetting the segment boundary variable
bis.w #ENC,&ADC10CTL0 ; Enable ADC
reti
;------------------------------------------------------------------------------
UART_ISR; Execute UART code upon button press
;------------------------------------------------------------------------------
bic.b #00000001b,&P2IE ; Disable P2.0 interrupt
bic.w #ENC,&ADC10CTL0 ; Stop ADC
bis.w #MC_0,&TACTL ; Stop Timer A
mov #0ffffh,R4 ; Setup delay loop
Delay dec R4 ; Delay resetting the interrupt to prevent keybounce
jnz Delay
bis.b #00010000b,&P2OUT ; Turn on P2.4 (UART Indicator)
BEGIN_UART ;set sync bit
bit.b #UCBUSY,&UCA0STAT ; Test for transmission in progress.
jnz END_UART
bis.b #UCA0TXIFG,&UC0IFG ; Clear all UCA and UCB interrupts
call #ExtractNum
END_UART bis.w #ENC,&ADC10CTL0 ; Enable ADC
bis.w #MC_1,&TACTL ; Start Timer A
bic.b #00000001b,&P2IFG ; Reset P2.0 Interrupt Flag
bis.b #00000001b,&P2IE ; Enable P2.0 interrupt
bic.b #00010000b,&P2OUT ; Turn off P2.4 (UART Indicator)
reti
;------------------------------------------------------------------------------
ExtractNum; Turn Memory into Hex
;------------------------------------------------------------------------------
TOP_EXTRACT
cmp R7,R9 ; Test for last memory address
jz END_BLK_4
cmp R9,R6 ; Test for last significant memory address
jz END_CUR_MEM
;cmp &StMemBlk2,R9 ; Test for start manual memory block 2
;jz ST_BLK_2
;cmp &StMemBlk3,R9 ; Test for start manual memory block 3
;jz ST_BLK_3
;cmp &StMemBlk4,R9 ; Test for end manual memory block 4
;jz ST_BLK_4
EXTRACT_A mov @R9,R8 ; Load data to manipulate
and #0f000h,R8 ; Isolate most significant dig
mov #0000Dh,R4 ; Rotate data right 12 times (load loop with 13)
EXTR_A_LP dec R4
jz END_A_LP ; If end of counter, exit loop
clrc
rrc R8 ; Push data to lowest nybble
jmp EXTR_A_LP
END_A_LP cmp #0000ah,R8
jge LETTER_A ; If dig is letter, manipulate as such
add #00030h,R8 ; Add 30 to convert the number dig in R8 to ASCII
jmp WAIT_A
LETTER_A add #00037h,R8 ; Add 49 to convert the letter dig in R8 to ASCII
WAIT_A bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_A ; If ready bit not set, keep waiting
bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit
SEND_A mov R8,&UCA0TXBUF ; Send this digit
mov @R9,R8 ; Load data to manipulate
and #00f00h,R8 ; Isolate second-most significant dig
clrc
rrc R8 ; Push data to lowest nybble
rrc R8
rrc R8
rrc R8
rrc R8
rrc R8
rrc R8
rrc R8
cmp #0000ah,R8
jge LETTER_B ; If dig is letter, manipulate as such
add #00030h,R8 ; Add 30 to convert the number dig in R8 to ASCII
jmp WAIT_B
LETTER_B add #00037h,R8 ; Add 49 to convert the letter dig in R8 to ASCII
WAIT_B bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_B ; If ready bit not set, keep waiting
bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit
SEND_B mov R8,&UCA0TXBUF ; Send this digit
mov @R9,R8 ; Load data to manipulate
and #000f0h,R8 ; Isolate least significant dig
clrc
rrc R8 ; Push data to lowest nybble
rrc R8
rrc R8
rrc R8
cmp #0000ah,R8
jge LETTER_C ; If dig is letter, manipulate as such
add #00030h,R8 ; Add 30 to convert the number dig in R8 to ASCII
jmp WAIT_C
LETTER_C add #00037h,R8 ; Add 49 to convert the letter dig in R8 to ASCII
WAIT_C bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_C ; If ready bit not set, keep waiting
bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit
SEND_C mov R8,&UCA0TXBUF ; Send this digit
mov @R9,R8 ; Load data to manipulate
and #0000fh,R8 ; Isolate least significant dig
cmp #0000ah,R8
jge LETTER_D ; If dig is letter, manipulate as such
add #00030h,R8 ; Add 30 to convert the number dig in R8 to ASCII
jmp WAIT_D
LETTER_D add #00037h,R8 ; Add 49 to convert the letter dig in R8 to ASCII
WAIT_D bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_D ; If ready bit not set, keep waiting
bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit
SEND_D mov R8,&UCA0TXBUF ; Send this digit
;Bit test 4 or 5 at beginning of mem loc to determine if print space or not
mov @R9,R8 ; Load current datum
bit.w #02000h,R8 ; Check for even or odd
jnz WAIT_SUFF ; If just printed first word on new line, print space before the next.
WAIT_SPACE bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_SPACE
bic.b #UCA0TXIFG,&IFG2 ; Wait, then give data suffix
SEND_SPACE mov #020h,&UCA0TXBUF ; Send Space 0Dh
incd R9 ; Move pointer to next memory location
jmp TOP_EXTRACT
WAIT_SUFF bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_SUFF
bic.b #UCA0TXIFG,&IFG2 ; Wait, then give data suffix
SEND_SUFF mov #0Dh,&UCA0TXBUF ; Send CR 0Dh as SUFFix
WAIT_PSTSU bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_PSTSU
bic.b #UCA0TXIFG,&IFG2 ; Wait, then give data suffix
SEND_SUFF2 mov #0Ah,&UCA0TXBUF ; Send LF 0Ah as SUFFix
WAIT_PSTSU2 bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Copied from p438
jz WAIT_PSTSU2
incd R9 ; Move pointer to next memory location
jmp TOP_EXTRACT ; Process next memory location
ST_BLK_2 ;mov #045h,&EndBlkChar1 ; "E" End First Memory Block with "ZZZ1"
;mov #04Eh,&EndBlkChar2 ; "N"
;mov #044h,&EndBlkChar3 ; "D"
;mov #031h,&EndBlkChar4 ; "1"
;decd &StMemBlk2 ; So that on next memory output, the device will continue properly past this breakpoint
jmp END_NUM_SEND
ST_BLK_3 ;mov #045h,&EndBlkChar1 ; "E" End Second Memory Block with "ZZZ2"
;mov #04Eh,&EndBlkChar2 ; "N"
;mov #044h,&EndBlkChar3 ; "D"
;mov #032h,&EndBlkChar4 ; "2"
;decd &StMemBlk3 ; So that on next memory output, the device will continue properly past this breakpoint
jmp END_NUM_SEND
ST_BLK_4 ;mov #045h,&EndBlkChar1 ; "E" End Third Memory Block with "ZZZ3"
;mov #04Eh,&EndBlkChar2 ; "N"
;mov #044h,&EndBlkChar3 ; "D"
;mov #033h,&EndBlkChar4 ; "3"
;decd &StMemBlk4 ; So that on next memory output, the device will continue properly past this breakpoint
jmp END_NUM_SEND
END_BLK_4 ;mov #045h,&EndBlkChar1 ; "E" End Fourth Memory Block with "ZZZ4"
;mov #04Eh,&EndBlkChar2 ; "N"
;mov #044h,&EndBlkChar3 ; "D"
;mov #034h,&EndBlkChar4 ; "4"
;mov #0C400h, R9 ; Allows UART button to send the whole memory data serially again.
jmp END_NUM_SEND
END_CUR_MEM ;mov #04Eh,&EndBlkChar1 ; "N" Memory Output Ended at Current Storage Location : Print "NOW!"
;mov #04Fh,&EndBlkChar2 ; "O"
;mov #057h,&EndBlkChar3 ; "W"
;mov #021h,&EndBlkChar4 ; "!"
END_NUM_SEND
WAIT_E ;bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send // Assimilated from p438
;jz WAIT_E ; If ready bit not set, keep waiting
;bic.b #UCA0TXIFG,&IFG2 ; Reset ready bit
SEND_E_C1 ;mov &EndBlkChar1,&UCA0TXBUF ; Send EndBlkChar1
WAIT_F ;bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send
;jz WAIT_F ;
;bic.b #UCA0TXIFG,&IFG2 ;
SEND_F_C2 ;mov &EndBlkChar2,&UCA0TXBUF ; Send EndBlkChar2
WAIT_G ;bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send
;jz WAIT_G ;
;bic.b #UCA0TXIFG,&IFG2 ;
SEND_G_C3; mov &EndBlkChar3,&UCA0TXBUF ; Send EndBlkChar3
WAIT_H ;bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send
;jz WAIT_H ;
;bic.b #UCA0TXIFG,&IFG2 ;
SEND_H_C4 ;mov &EndBlkChar4,&UCA0TXBUF ; Send EndBlkChar4
WAIT_I ;bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send
;jz WAIT_I ;
;bic.b #UCA0TXIFG,&IFG2 ;
SEND_I ;mov #0Dh,&UCA0TXBUF ; Send CR 0Dh as SUFFix
WAIT_J ;bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send
;jz WAIT_J
;bic.b #UCA0TXIFG,&IFG2 ;
SEND_J ;mov #0Ah,&UCA0TXBUF ; Send LF 0Ah as SUFFix
WAIT_K ;bit.b #UCA0TXIFG,&IFG2 ; Test for ready for next send
;jz WAIT_K
ret
;------------------------------------------------------------------------------
; Interrupt Vectors
;------------------------------------------------------------------------------
.sect ".reset" ; MSP430 RESET Vector
.short RESET ;
.sect ".int08" ; Timer_A Vector
.short TIMER_A_ISR
.sect ".int05" ; ADC10 Vector
.short ADC10_ISR
.sect ".int03" ; Port 2 Vector
.short UART_ISR ;
.end
**Attention** This is a public forum