average using bit shift and array in ADC interrupt - c

I have an ADC interrupt that I'd like to sample the channel (ADCBUF0) 8 times, then take the average of the samples. My code utilizes flags to jump out of the if statement. The code compiles and my variables are initialized elsewhere. Could someone please tell me why I am not receiving a value for SpeedADC???
///////Global////////////
int SpeedADCcount=0;
/////////////////////////
SpeedADCflag=1;
if(SpeedADCflag==1) //The following is meant to take a an average of the incoming ADC voltages
{
SpeedADCcount++;
for(i = SpeedADCcount; i < 16; i++)
{
while(!ADCON1bits.SAMP); //Sample Done?
ADCON1bits.SAMP=0; //Start Converting
while(!ADCON1bits.DONE); //Conversion Done? Should be on next Tcy cycle
SpeedADCarray[i] = ADCBUF0;
SpeedADCflag=0;
}
}
if(SpeedADCcount==15)
{
SpeedADC=SpeedADCarray[i]>>4;
SpeedADCcount=0;
// Re-enable the motor if it was turned off previous
if((SpeedADC>246) && Flags.RunMotor==0){RunMotor();}
/*Go through another stage of "filtering" for any analog input voltage below 1.25volts

You need to get the right downshift amount (to avoid dividing), such that 8 -> 3, 16 -> 4, etc. For 8 samples, you only need to downshift 3 (3 bits).
And you need to sum all of the values in a single value, not put them in separate array entries.
SpeedADCarray += ADCBUF0; /* accumulate in a single integer, not an array */

Related

Problem with second digit value on Seven Segment Display for MCU voltmeter

I am building a Voltmeter for a PIC18F using an XC8 compiler on MPLAB and I am having trouble getting my second digit value to update as I rotate my potentiometer. A potentiometer is connected to PORTA, the two digit seven segment display is connected to two 74LS47 decoders. and those two decoders are connected to PORTS B0-B7. PORT B0-B3 is attached to the first decoder, and B4-B7 the second decoder which drives the second digit of the display. When I rotate the potentiometer, the first digit changes accordingly, but the second digit just stays at zero.
I've tried multiplying the voltage by 10 then using mod(%) to extract the second digit value, but it seems the compiler doesn't use %. I have tried using a work around to mod(in the code below) to see if it would work and nothing has so far. Also, I've tested the second digit by outputting "firstdig" to PORTS B4-B7 to make sure my circuit is built correctly and it was. Any guidance or suggestions would be greatly appreciated. I've been stuck on this for quite a while.
int VREF = 5;
float VOLTS = ((num*VREF)/1024);
int firstdig = floor(VOLTS);
int seconddig = floor(VOLTS * 10) - (firstdig * 10);
int LSB = firstdig;
int MSB = seconddig;
MSB = MSB<<4;
PORTB = LSB | MSB;
__delay_ms(50); ```

Why do I get garbage values when I input 't' two times?

I am attempting to implement a temperature sensor on the MSP430G2553 that reads and outputs temp readings and also timestamps. The basic requirements are as follows:
• t: Show the current time of the system. Output to the serial terminal the current time of the system using the form hhmmss, where hh is the two digit hour in 24-hour format, mm is the two digit minute in the hour, and ss is the two digit second within the minute.
• s: Set the current time of the system. Set the current time of the system to the provided argument. There will be no space between the command and its argument. The argument will be on the form hhmmss, where hh is the two digit hour in 24-hour format, mm is the two digit minute in the hour, and ss is the two digit second within the minute.
• o: Show the oldest temperature reading and its timestamp. Output to the serial terminal the oldest temperature reading and its timestamp. The output must have the form hhmmss: T, where hh is the two digit hour in 24-hour format, mm is the two digit minute in the hour, ss is the two digit second within the minute, and T is the measured temperature. The displayed entry must be removed from the list. If no readings have been performed, the message “No temperatures recorded.” must be displayed.
• l: Show all the temperature readings and their timestamps. Output to the serial terminal all the temperature readings and their timestamps. The output should be in chronological order, oldest first. The output must have the form hhmmss: T, where hh is the two digit hour in 24-hour format, mm is the two digit minute in the hour, ss is the two digit second within the minute, and T is the measured temperature. All entries must be removed from the list. If no readings have been performed, the message “No temperatures recorded.” must be displayed.
Your system must also meet the following requirements:
• You must capture a new temperature reading every five (5) minutes and store 32 temperature readings at a minimum. If you have reached the maximum amount of temperature readings, discard the oldest one before storing a new one.
An error occurs after inputting 't' 2 times. The next time I input 't', It outputs 3 or 4 garbage values. My 't' case in the switch statement runs perfectly fine when I comment out the 's' case or change 's' to a different variable but that case for the assignment needs to be in the case 's' is input into the uart. What I've tried so far includes: changing the variable, commenting out the second case, changing the value of the S case to make sure the garbage values were resulting because of that case, playing with bracketing of certain parts of code, revamped the entire first part of the t case to be more efficient and still came out with the same error,etc. Any suggestions would be very helpful. Picture of Garbage Values
#include "msp430g2553.h"
volatile short unsigned int i=0,j=0,k=0;//This the integer that track the number of cycles
volatile short unsigned int timercount=0;//This the integer that track the number of cycles
volatile unsigned int GT=2600;//variable that sets the global time in seconds of the system
volatile char inputcharacters[16];//Input character storage array
volatile unsigned int tempature[32];//
volatile unsigned int tempaturetime[32];
volatile unsigned int d[6]={0,0,0,0,0,0},test[7];
volatile unsigned int h=0;
volatile unsigned int m=0;
volatile unsigned int ss=0;
int temp=0;
void main(void)
{
WDTCTL = WDTPW + WDTHOLD;//Disabling watchdogtimer
P1DIR |= BIT0;//setting p1 bit 1 to outy
P1OUT &= ~BIT0;//Shutting off the LED
/* UART config for 9600 baud with SMCLK*/
IE2 = 0x00;//interrupts off on UART
UCA0CTL1 = 0x81;//using SMCLK(1Mhz) and software reset enable
UCA0CTL0 = 0x00;//eight data bits, no parity, one stop bit
UCA0MCTL = 0x02;//Setting UCBRSx to 1 to set clock rate to 9600
UCA0BR0 = 0x68;//set clock rate to 9600 from 1Mhz
UCA0BR1 = 0x00;//set clock rate to 9600
UCA0STAT = 0x00;//error check and parity, and frame error check all off
TACTL = 0x1D0;// sets timer a to use the 32.768kh clock with 8 divisor and in up mode
TACCR0 = 0x1001;// setting the timer to count up to 4097 to get a 1hz(1s) oscillation
TACCTL0 &= 0xFFEF;// disable capture compare interrupts
P1SEL|=0X06;// According to slas735j both p1.1(0X02) and p1.2(0X04) need to be set on to receive and send on the UART
P1SEL2|=0X06;
ADC10CTL0 &= ENC;
ADC10CTL0 = SREF_1 | ADC10SHT_3 | REFON | ADC10ON;
ADC10CTL1 = INCH_10 | ADC10DIV_2 | ADC10SSEL_3;
//ADC10CTL0 = 0x3030;
//ADC10CTL1 = 0xA038;
ADC10CTL0 &= 0xFFFC;
/* Enable USCI logic and Enable interrupts */
UCA0CTL1 &= 0xFE;//turning on USCI logic
IE2 = 0x01;// Sets UART to receive interrupt
TACCTL0 = CCIE;// set timerA interupts on done last
__bis_SR_register(LPM3_bits + GIE);//found on TI wiki
}
#pragma vector=USCIAB0RX_VECTOR// This vector name was pulled from the webcources code
__interrupt void uartinput(void)//this form found on TI's wiki
{
inputcharacters[i] = UCA0RXBUF;
UCA0TXBUF = UCA0RXBUF;
if((i>14)||(inputcharacters[i]=='\r'))
{if(inputcharacters[i]=='\r'){inputcharacters[i]=0;}//to reduce i, the number of inputs so that we dont count the \r
for(j=0;j<i;j++){switch(inputcharacters[j]) {//switch statement that check the input characters and outputs the correct morse
case 't' :
h=GT/3600;
m=(GT-h*3600)/60;
ss=GT-h*3600-m*60;
d[0]=(h/10);
d[1]=(h-(d[0]*10));
d[2]=(m/10);
d[3]=(m-(d[2]*10));
d[4]=(ss/10);
d[5]=(ss-(d[4]*10));
for(k=0;k<6;k++)
{while((IFG2 & 0x02)==0)
{;}
UCA0TXBUF=d[k]+48;
test[k]=d[k]+48;}
while((IFG2 & 0x02)==0)
{;}
UCA0TXBUF='\r';
break;
case 'x' ://If this break case is not set to 's' it will not cause the error in the 't' case, no idea why
GT=0;
temp=(inputcharacters[j+1]-0x30)*10;//
temp+=(inputcharacters[j+2]-0x30);
GT+=(temp*3600);
temp=(inputcharacters[j+3]-0x30)*10;
temp+=(inputcharacters[j+4]-0x30);
GT+=(temp*60);
temp=(inputcharacters[j+5]-0x30)*10;
temp+=(inputcharacters[j+6]-0x30);
GT+=temp;
j+=7;
break;
case 'o' :
{;}
break;
case 'l' :
{;}
break;
case '\r' :
{;}
break;
default :
{i++;}
}}
i=0;
j=0;
}
else{i++;}}
#pragma vector=TIMER0_A0_VECTOR
__interrupt void Timer_interupt(void)
{
if(GT<86459)//24:00:59 in seconds
{GT++;}//add a second
else
{GT=60;}//00:01:00
if(timercount<299)//300s=5min
{timercount++;}
else{timercount=0;
ADC10CTL0 |= 0x3;// turn on ADC
while(ADC10CTL1 & ADC10BUSY)//loop to alow for ADC to finish sampeling
{;;}
ADC10CTL0 & 0xFFFE;//Shuting off the ADC after sampeling
tempature[i]=ADC10MEM;//Storing the tempature
tempaturetime[i]=GT;// storing the time
if(i<31)
{i++;}
else
{i=0;}
}
}
one very obvious problem with the posted code is that when the currently being checked input char is found to be x is the new date stamp must have been entered (as 6 digits) after the user entered x but before the user entered a \r this could be made to work, but there is no code enforcing that sequence of input.
in general, the uart input function should contain several 'states and sub states' where a command char causes the code to switch to a 'state' and that would have sub states for the actual time stamp. so when the user inputs a command, like x a mode is set, where the next 6 digits entered are the new timestamp.
suggest designing a 'context sensitive' state diagram then implementing that diagram.
amongst other things, the size of the input buffer would reduced to 1 byte
amongst the many sequencing problems there is the fact that when accumulating some number, saying things like
int temp = 0;
temp += inputchar[x] - 0x30;
temp += inputchar[x+1] - 0x30;
...
will not generate the expected results.
what would generate the expected results would be:
int temp = 0;
temp += inputchar[x] - 0x30;
temp = temp*10 + (inputchar[x+1] - 0x30);
....
temp = temp*10 + (inputchar[x+5] - 0x30);
GT = temp;
and of course, test each inputchar using if( isdigit( inputchar[x] ) before trying to apply that input to the calculation of temp
for ease of development, strongly suggest only try to implement one command at a time and when that command is working properly (make extensive use of a debugger to make the testing easy. such as a jtag debugger for that chip.) Then move on to the next command to implement.
the variable i is being modified in both interrupt handlers. So when one interrupt handler modifies i, the value is corrupted for the other interrupt.
one full day (24 hours) is 878400 seconds so the value 86459 is not correct. Also, this is a 'magic' number in the code. magic numbers have no basis. 'magic' numbers make the code much more difficult to understand, debug, maintain. Suggest using a enum statement or a #define statement to give that 'magic' number a meaningful name, then use that meaningful name throughout the code. Note: resetting the GT counter to 60 is not correct, in the posted code it should be reset to 0.
Similar considerations exist for the 'magic' number 300.
the posted code has no action when the input command is l so we cannot analyze such (non existent) code to determine what is wrong with it.
in the handling of the t command:
the GT variable is subject to modification between access. Suggest saving to a 'new' variable and do all calculations from that new variable.
the calculation of h, m, and s is performing a lot of re-calculation of the same intermediate variables. Suggest:
int myGT = GT;
h = myGT / 3600;
myGT %= 3600;
m = myGT / 60;
s = myGT % 60;
The array test[] is set but not used. This is a waste of resources in production code. Suggest its' existence and setting be determined by some -D compiler parameter.

calculation in C for C2000 device

I'm having some trouble with my C code. I have an ADC which will be used to determine whether to shut down (trip zone) the PWM which I'm using. But my calculation seem to not work as intended, because the ADC shuts down the PWM at the wrong voltage levels. I initiate my variables as:
float32 current = 5;
Uint16 shutdown = 0;
and then I calculate as:
// Save the ADC input to variable
adc_info->adc_result0 = AdcRegs.ADCRESULT0>>4; //bit shift 4 steps because adcresult0 is effectively a 12-bit register not 16-bit, ADCRESULT0 defined as Uint16
current = -3.462*((adc_info->adc_result0/1365) - 2.8);
// Evaluate if too high or too low
if(current > 9 || current < 1)
{
shutdown = 1;
}
else
{
shutdown = 0;
}
after which I use this if statement:
if(shutdown == 1)
{
EALLOW; // EALLOW protected register
EPwm1Regs.TZFRC.bit.OST = 1; // Force a one-shot trip-zone interrupt
EDIS; // end write to EALLOW protected register
}
So I want to trip the PWM if current is above 9 or below 1 which should coincide with an adc result of <273 (0x111) and >3428 (0xD64) respectively. The ADC values correspond to the voltages 0.2V and 2.51V respectively. The ADC measure with a 12-bit accuracy between the voltages 0 and 3V.
However, this is not the case. Instead, the trip zone goes off at approximately 1V and 2.97V. So what am I doing wrong?
adc_info->adc_result0/1365
Did you do integer division here while assuming float?
Try this fix:
adc_info->adc_result0/1365.0
Also, the #pmg's suggestion is good. Why spending cycles on calculating the voltage, when you can compare the ADC value immediately against the known bounds?
if (adc_info->adc_result0 < 273 || adc_info->adc_result0 > 3428)
{
shutdown = 1;
}
else
{
shutdown = 0;
}
If you don't want to hardcode the calculated bounds (which is totally understandable), then define them as calculated from values which you'd want to hardcode literally:
#define VOLTAGE_MIN 0.2
#define VOLTAGE_MAX 2.51
#define AREF 3.0
#define ADC_PER_VOLT (4096 / AREF)
#define ADC_MIN (VOLTAGE_MIN * ADC_PER_VOLT) /* 273 */
#define ADC_MAX (VOLTAGE_MAX * ADC_PER_VOLT) /* 3427 */
/* ... */
shutdown = (adcresult < ADC_MIN || adcresult > ADC_MAX) ? 1 : 0;
/* ... */
When you've made sure to grasp the integer division rules in C, consider a little addition to your code style: to always write constant coefficients and divisors with a decimal (to make sure they get a floating point type), e.g. 10.0 instead of 10 — unless you specifically mean integer division with truncation. Sometimes it may also be a good idea to specify float literal precision with appropriate suffix.

Understanding Logic Using Iteration for Master/Slave

I took over a project from unknown predecessor who had gone without proper documents and comments.
Now I am trying to analyze his codes, but it is hard to follow up.
Basically, there are 32 channels hook up with a micro controller. What he seems like trying to do is find slave channels between 32 channels once those information is sent from a server.
call nextslave()
for (scan=0 ; (scan = nextslave(chan, scan)) != -1 ; scan++)
nextslave() looks like below
/**
* nextslave - gets the channel number of the next slave channel
* associated with the master. returns -1 if no more slaves.
* channel and start are zero-based.
*/
short nextslave(short channel, short start)
{
short mask, major, minor;
unsigned char *p;
/* fix-up the slaveflag[] index values */
major = start / 8;
minor = start % 8;
/* init a pointer into the slaveflag[] array */
p = &(chparam[channel].slaveflag[major]);
/* now let us find the next slave channel (if any) */
for (; major < (NUMCHANS / 8) ; major++, p++)
{
minor &= 0x07;
for (mask = (0x01 << minor) ; minor < 8 ; mask <<= 1, minor++)
{
if (*p & mask)
{
/* found one so calculate channel# and return */
return ((major * 8) + minor);
}
}
}
/* if we reach here then there are no (more) slaves */
return (-1);
}
What I have analyzed so far is:
start variable keeps iterating until 32 in nextslave().
when the start var is 0~7, major var is 0 and minor var changes from 0 to 7,also mask var changes 1,2,4,8,16...
When the start var is 8 ~15, major var is 1, and minor var still keep changing from 0 to 7
Keep iterating until major var becomes 4.(I don't know the meaning of major var in his codes)
In second for loop, return something if *p(pointer to values from server) & mask is true
I am not clear about general idea about what he intended to for this process. Especially, in the second for loop, if there is no match up in if(*p&mask), then go back to the first for loop. However, minor variable was being increased without clearing out like 0. So once the code hits minor &= 0x07, the processor will do bitwise with the last value of minor although major var keeps increasing.
For instance, the range of minor var is 0 to 7 and there is no match up value, so ends up becoming 7 in the second for loop. Get out of the loop and go back to the first loop and increases major var by 1. But minor var is still 7, so the second loop will start like mask =(0x01<<minor) with minor =7.
I feel like need to reset minor =0 whenever getting out the second for loop, but I don't know what he was aiming for. He just wrote down "master/slave technique".
My Questions are:
Is it correct with my analysis?
How the master/slave technique is used for 32 channels getting ADC?
Is the reset code for minor var required? whenever getting out the second for loop.
If you have any idea, please answer anything that helps me out to understand his codes.
Thanks,
Jin

Identifying a trend in C - Micro controller sampling

I'm working on an MC68HC11 Microcontroller and have an analogue voltage signal going in that I have sampled. The scenario is a weighing machine, the large peaks are when the object hits the sensor and then it stabilises (which are the samples I want) and then peaks again before the object roles off.
The problem I'm having is figuring out a way for the program to detect this stable point and average it to produce an overall weight but can't figure out how :/. One way I have thought about doing is comparing previous values to see if there is not a large difference between them but I haven't had any success. Below is the C code that I am using:
#include <stdio.h>
#include <stdarg.h>
#include <iof1.h>
void main(void)
{
/* PORTA, DDRA, DDRG etc... are LEDs and switch ports */
unsigned char *paddr, *adctl, *adr1;
unsigned short i = 0;
unsigned short k = 0;
unsigned char switched = 1; /* is char the smallest data type? */
unsigned char data[2000];
DDRA = 0x00; /* All in */
DDRG = 0xff;
adctl = (unsigned char*) 0x30;
adr1 = (unsigned char*) 0x31;
*adctl = 0x20; /* single continuos scan */
while(1)
{
if(*adr1 > 40)
{
if(PORTA == 128) /* Debugging switch */
{
PORTG = 1;
}
else
{
PORTG = 0;
}
if(i < 2000)
{
while(((*adctl) & 0x80) == 0x00);
{
data[i] = *adr1;
}
/* if(i > 10 && (data[(i-10)] - data[i]) < 20) */
i++;
}
if(PORTA == switched)
{
PORTG = 31;
/* Print a delimeter so teemtalk can send to excel */
for(k=0;k<2000;k++)
{
printf("%d,",data[k]);
}
if(switched == 1) /*bitwise manipulation more efficient? */
{
switched = 0;
}
else
{
switched = 1;
}
PORTG = 0;
}
if(i >= 2000)
{
i = 0;
}
}
}
}
Look forward to hearing any suggestions :)
(The graph below shows how these values look, the red box is the area I would like to identify.
As you sample sequence has glitches (short lived transients) try to improve the hardware ie change layout, add decoupling, add filtering etc.
If that approach fails, then a median filter [1] of say five places long, which takes the last five samples, sorts them and outputs the middle one, so two samples of the transient have no effect on it's output. (seven places ...three transient)
Then a computationally efficient exponential averaging lowpass filter [2]
y(n) = y(n–1) + alpha[x(n) – y(n–1)]
choosing alpha (1/2^n, division with right shifts) to yield a time constant [3] of less than the underlying response (~50samples), but still filter out the noise. Increasing the effective fractional bits will avoid the quantizing issues.
With this improved sample sequence, thresholds and cycle count, can be applied to detect quiescent durations.
Additionally if the end of the quiescent period is always followed by a large, abrupt change then using a sample delay "array", enables the detection of the abrupt change but still have available the last of the quiescent samples for logging.
[1] http://en.wikipedia.org/wiki/Median_filter
[2] http://www.dsprelated.com/showarticle/72.php
[3] http://en.wikipedia.org/wiki/Time_constant
Note
Adding code for the above filtering operations will lower the maximum possible sample rate but printf can be substituted for something faster.
Continusously store the current value and the delta from the previous value.
Note when the delta is decreasing as the start of weight application to the scale
Note when the delta is increasing as the end of weight application to the scale
Take the X number of values with the small delta and average them
BTW, I'm sure this has been done 1M times before, I'm thinking that a search for scale PID or weight PID would find a lot of information.
Don't forget using ___delay_ms(XX) function somewhere between the reading values, if you will compare with the previous one. The difference in each step will be obviously small, if the code loop continuously.
Looking at your nice graphs, I would say you should look only for the falling edge, it is much consistent than leading edge.
In other words, let the samples accumulate, calculate the running average all the time with predefined window size, remember the deviation of the previous values just for reference, check for a large negative bump in your values (like absolute value ten times smaller then current running average), your running average is your value. You could go back a little bit (disregarding last few values in your average, and recalculate) to compensate for small positive bump visible in your picture before each negative bump...No need for heavy math here, you could not model the reality better then your picture has shown, just make sure that your code detect the end of each and every sample. You have to be fast enough with sample to make sure no negative bump was missed (or you will have big time error in your data averaging).
And you don't need that large arrays, running average is better based on smaller window size, smaller residual error in your case when you detect the negative bump.

Resources