Hi, I mentioned last week (in "problem with putchar()" thread) that I found my code acting strange when a variable called counter1 keeps incrementing while printing values to the serial port during simulation, even though it is not coded to do so. Basically my program is set to generate analog-to-digital conversions every millisecond (1kHz) on a 100Hz sine wave I have connected to ADC0 input on the Microconverter SAR A3 evaluation board from analog devices which uses an ADuC841 MCU. In the ADC interrupt routine, I basically save the first 12-bit ADC value in a structure and for the next 254 values, I subtract them from the previous sample and save them in the same structure. When my structure is full (i.e. has 255 values and a counter value, counter1), I write the values stored in the structure to the serial port. The problem is when I use printf() or putchar(), the value of my counter increments when I simulate my program and I cant see why, since the program execution does not jump to my ADC ISR. I thought maybe this is a problem with the simulator but when I download the code to the evaluation board and view the output on Hyperterminal, the count values do not match up with the number of values printed. Obviously in the simulator I cannot see the type of values I'm getting as the ADCDATAH/L are always 0. I have tried to look at the trace results when placing a breakpoint at the counter1 variable but am still not sure whats happening. I am new to the whole MCU environment and was wodering if anyone can help. I have placed my code below in case I have not clarified my problem. Thanks, Lisa
#include <stdio.h> #include <ADuC841.h> #define MAX_COUNT 255 sbit LED = 0xB4; unsigned char a = 5; unsigned char channel_to_convert = 0; unsigned char count, counter1 = 2; // doesn't need to go above 255 for now! Initialize to 1 to skip member count in structure unsigned char error = 0; unsigned char finished = 0; bit buf = 0; short adc_value, temp1, temp2; unsigned char i; //int t1,t2; struct record1 { unsigned char cnt; short base_value; char x[253]; }; struct record1 rec1[2]; // array variable rec1 of type struct record1 // ADC Interrupt Service Routine */ void adc_int() interrupt 6 { LED =0; // Store the first value into the second element of one array if (counter1 == 2) { counter1++; rec1[buf].base_value = (ADCDATAL | ((ADCDATAH & 0x0F) << 8)); temp1 = rec1[buf].base_value; } else { if (counter1 < MAX_COUNT) { adc_value = (ADCDATAL | ((ADCDATAH & 0x0F) << 8)); temp2 = adc_value - temp1; temp1 = adc_value; if((temp2 >> 8) == 0) // if dpcm values are <= 1 byte { rec1[buf].x[counter1-3] = (char) temp2; counter1++; } else { error = 1; // error will be 1 if a dpcm value (temp2) > 8 bits } } else { adc_value = (ADCDATAL | ((ADCDATAH & 0x0F) << 8)); temp2 = adc_value - temp1; if((temp2 >> 8) == 0) // if dpcm values are <= 1 byte { rec1[buf].x[counter1-3] = (char) temp2; } else { error = 1; } finished = 1; // exit the Interrupt and print to serial port in main function } } TF2 = 0; // clear overflow flag LED =1; return; } int main(void) { //t1 = &counter1; //t2 = &i; // Set up UART at 115,200 Baud -> may have to alter */ T3CON = 0x82; // DIV=2 T3FD = 0x20; // T3FD = 32 SCON = 0x52; /* Configure ADC */ ADCCON1 = 0x9E; // power up ADC ADCCON2 = channel_to_convert; // select channel to convert RCAP2L = 0xCD; // sample period = 1 ms RCAP2H = 0xD4; TL2 = 0xCD; TH2 = 0xD4; EA = 1; // enable interrupts EADC = 1; // enable ADC interrupt //ET2 = 1; // If timer 2 interrupt is present, enable it TR2 = 1; // run Timer2 while(1) { if(finished) { rec1[buf].cnt = counter1; // store counter1 value in 1st element of array buf ^= 1; // set alternative buffer up so values wont be overwritten during printing counter1 = 2; // reset counter1 to original value // print out whole structure printf("%02BX ",rec1[!buf].cnt); printf("%02BX ",(char) (rec1[!buf].base_value >> 8)); // need to split integer into 2 chars printf("%02BX ",(char) (rec1[!buf].base_value)); // putchar(rec1[!buf].cnt); // putchar((char) (rec1[!buf].base_value >> 8)); // putchar((char) (rec1[!buf].base_value)); for(i = 0; i < MAX_COUNT-2; i++) { printf("%02BX ",rec1[!buf].x[i]); //printf("%02BX ", i); //putchar(rec1[!buf].x[i]); } printf("\n"); finished = 0; // reset finished condition } if(error) { rec1[buf].cnt = counter1; // store counter1 value in 1st element of array count = counter1; buf ^= 1; // set alternative buffer up counter1=3; // want to start back at first if loop in first else loop in interrupt rec1[buf].base_value = temp1; // keep the current value of temp1 as base_value for next structure // print out whole structure printf("%02BX ",rec1[!buf].cnt); printf("%02BX ",(char) (rec1[!buf].base_value >> 8)); printf("%02BX ",(char) (rec1[!buf].base_value)); for(i = 0; i < count; i++) { printf("%02BX ",rec1[!buf].x[i]); // printf("%02BX ", i); //putchar(rec1[!buf].x[i]); } printf("\n"); error = 0; // reset the error condition } } }
I haven't studied your code in detail but a few comments: counter1 does seem to be incremented at two places in the ISR. This could definitely be a problem as you read and modify counter1 several times in main(). Remember that interrupts are asyncronous with respect to the execution of the non-ISR code. Although this doesn't seem to be the problem here, all varaibles shared between ISR and non-ISR code should be declared 'volatile'. As a general rule always use unsigned char when dealing with bytes containing data that is not ASCII. I'm not sure that printf() understands %BX - I am sure however that it understands %bX. I would make all modifications to the contents of the struct and the buf variable in the ISR, no modifications in main unless you are sure that the ISR cannot access these variables at the same time, ie either the interrupt is disabled or the ISR code 'masked' with a bitflag. Remember that printf() is a large and slow function, also that you appear to be using polled serial comms which will slow it down a great deal more particularly at lower baud rates. You need to ensure that the code in main() has time to complete before the next batch of conversions are complete. I seem to vaguely remember a thread recently where it was stated that the simulator will not trace into an ISR? (I don't know much about the simulator as I rarely use it - it confuses me too much!). Get another terminal emulator - you'll never be sure whether problems are caused by hyperterminal or your code.
I realize that interrupts are asynchronous and hence why I have included the buffer bit "buf", so that in the main function, this bit is toggled and therefore if an interrupt occurs, the new values will be loaded into the second array structure while the first array structure is being printed and vice versa. Maybe this is not the way to overcome the problem but it seems reasonable to me. Since I have set up Timer2 to overflow in 1 millisecond, the ISR should happen sometime after this, which it seems to do in the simulator as expected. Maybe during printing in the main function, the simulator doesn't jump to the ISR in the foreground but perhaps in the background and may be why my counter variable is incrementing. However, a week ago, it did jump to the ISR while printing and incremented both in the ISR and while printing. The simulator really confuses me. Maybe both can happen. Also, the %BX seems fine to me when printing. Lisa
You need to consider what might happen if an interrupt occurs at any point during execution of this code:
if(finished) { rec1[buf].cnt = counter1; // store counter1 value in 1st element of array buf ^= 1; // set alternative buffer up so values wont be overwritten during printing counter1 = 2; // reset counter1 to original value
Hi Lisa, In your ISR, it seems possible to me for both finsihed and error to both be TRUE, which outside the ISR when you come to print the values could cause problems. Consider 'if (finished)' is true it selects the alterntive buffer to print and sets counter1 to something and say takes about 50ms to print out all the data. But'if error' is also true it reselects the original buffer resets counter1 (wrongly because it has been incremented during the ISR) and you now start printing from the wrong buffer (which is currently in use by the ISR). I would try and confirm that this is not happening by initially just changing your code to just print (or include printing) 'good' whenever finished is true and 'bad' whenever error is true. Mark.
Counter1 keeps incrementing when your are printing as you reset it to a value of two in your main line code. The interrupts then keep incrementing it. Just a couple of points: Try to keep your processing in the interrupt routines as lean as possible. Try not to use too much stuff with structures in the interrupt routine - 8051's are not very efficient when doing this. If you want local variables in the interrupt routine - declare them static. ie: static char test1; Otherwise weird things happen! Where you have shared variables between main line code and an interrupt, in the main line code disable interrupts before reading the shared variable then re-enable interrupts. That way you can be assured that the iterrupt code will not execute at that instant. Don't do it around a printf as a printf takes too long to execute - copy the value first then printf using the copy.
I will keep all this in mind. Thank you all for your help. Lisa