do you have libmpc.so.2 and does it have the right permisions?
I have seen this kind of error when moving shared libraries or if they have the wrong permissions
Since I'm running an old distribution and the bundled binaries require a newer glibc, I used that script and (after installing all required dependencies) it worked. I had to use the mspdebug in the binary distribution (the script didn't build it).
You will have to download and extract that into the appropriate directory on your unpacked Energia directory.
The gcc version in that tar file has the FRAM BSL corruption patch installed. Also, it was built with the intent of using fewer external shared libraries by statically linking with some of the required system libraries. If all else fails, you can try the build script located here:
TI has some sample code for the internal temperature sensor, but it does not explain how to scale the ADC reading to useful units of degrees. Here is a step-by-step explanation of how to do the scaling with integer math for degrees C, K and F. There is also sample code to display the temperature on a Nokia 5110 LCD.
The data sheet (SLAU144) has this formula for converting temperature in degrees Celsius to voltage.
V = 0.00355 * C + 0.986
What we need is a formula for converting voltage to temperature.
Rewrite the data sheet fomula with temperature on the left
0.00355 * C + 0.986 = V
Divide both sides by 0.00355
C + 277.75 = V / 0.00355
Subtract 277.75 from both sides
C = V / 0.00355 - 277.75
Now we have a formula for converting voltage to temperature.
The data sheet has this formula for converting voltage to ADC value, once again the opposite of what we neeed.
For Vref- == 0
A = 1023 * V / Vref
Swap sides
1023 * V / Vref = A
Multiply by Vref
1023 * V = A * Vref
Divide by 1023
V = A * Vref / 1023
For a 1.5V reference
V = A * 1.5 / 1023
Simplify
V = A * 0.0014663
Substitute ADC conversion forumula for voltage in the temperature conversion formula.
C = A * 0.0014663 / 0.00355 - 277.75
Simplify
C = A * 0.413 - 277.75
Now we have a formula to convert ADC reading to temperature.
It uses real numbers, so floating point math is required for good precision.
Floating point is slow and requires more flash, so let's use integer math instead.
Multiply by 65536 (2^16) and then divide by the same.
C = (A * 27069 - 18202393) / 65536
Use a right shift instead of divide. This will become a move of high word to low word.
C = (A * 27069 - 18202393) >> 16
Add 0.5 * 65536 to impove rounding.
C = (A * 27069 - 18202393 + 32768) >> 16
Simplify.
C = (A * 27069 - 18169625) >> 16
So that is how to go from ADC to degrees Celsius.
To convert degrees C to degees K.
K = C + 273.15
Applied to ADC to degrees C conversion formula.
K = (A * 27069 - 18169625) >> 16 - 273.15
Implement with integer math by multiplying by 65536
K = (A * 27069 - 18169625 - 17,901,158) >> 16
Simplify.
K = (A * 27069 - 268467) >> 16
To convert degrees C to degrees F.
F = C * 9 / 5 + 32
Applied to voltage to degrees C conversion forumula
F = (V / 0.00355 - 277.75) * 9 / 5 + 32
Multiply by 9
F = (V / 0.0003944 - 2499.75) / 5 + 32
Divide by 5
F = (V / 0.0019722 - 499.95) + 32
Add 32
F = V / 0.0019722 - 467.95
Substitute ADC to voltage forumula
F = A * 0.0014663 / 0.0019722 - 467.95
Simplifiy
F = A * 0.7435 - 467.95
Convert to integer
F = (A * 48724 - 30667156) >> 16
Improve rounding
F = (A * 48724 - 30667156 + 32768) >> 16
Simplify
F = (A * 48724 - 30634388) >> 16
So now we have three formulas to convert ADC reading to degrees C, K and F using fast and compact integer math.
C = (A * 27069 - 18169625) >> 16
K = (A * 27069 - 268467) >> 16
F = (A * 48724 - 30634388) >> 16
Using the ADC value, rather than a different temperature scale, will ensure greatest precision for each temperature scale.
main.c
#include
#include
#include "lcd.h"
#define ADC_SLEEP // Sleep during ADC conversion
//#define SHOW_ADC // Show ADC raw and ADC millivolts
// Print integer from -999 to 9999 using 12 x 16 font
void print_int(int i, const unsigned y)
{
if(i < -999 || i > 9999) return;
const unsigned neg = i < 0;
if(neg) i = -i;
div_t d; d.quot = i;
unsigned x = 48;
do {
d = div(d.quot, 10);
pd12(d.rem, x -= 12, y);
} while(d.quot);
if(neg) pd12(14, x -= 12, y);
while(x) pd12(10, x -= 12, y);
}
// Print integer from 0 to 9999 vertically using 6 x 8 font
void print_v(int i, unsigned x)
{
unsigned y = 4;
unsigned c;
if(i < 0 || i > 9999) return;
div_t d; d.quot = i;
do {
d = div(d.quot, 10);
c = d.rem + '0';
lcd_print((char *)&c, x, --y);
} while(d.quot);
c = ' ';
while(y) lcd_print((char *)&c, x, --y);
}
void main(void)
{
unsigned adc; // ADC value
int c, k, f; // Temperature in degrees C, K, and F
unsigned mv; // ADC reading in millivolts
//
WDTCTL = WDTPW | WDTHOLD; // Disable watchdog reset
//
lcd_init(); // Initialize LCD
lcd_clear(0); //
pd12(15, 48, 0); // Degrees
pd12(17, 59, 0); // F
pd12(15, 48, 2); // Degrees
pd12(16, 58, 2); // C
pd12(15, 48, 4); // Degrees
pd12(18, 59, 4); // K
#ifdef SHOW_ADC //
lcd_print("Am", 72, 4); // AD / mV
lcd_print("DV", 72, 5); //
#endif //
//
ADC10CTL0 = 0; // Configure ADC
ADC10CTL1 = INCH_10 | ADC10DIV_3; //
ADC10CTL0 = SREF_1 | ADC10SHT_3 | REFON | ADC10ON | ADC10IE;
//ADC10CTL0 = SREF_1 | ADC10SHT_3 | REFON | ADC10ON | ADC10IE | REF2_5V;
#ifdef ADC_SLEEP //
ADC10CTL0 |= ADC10IE; // Enable ADC conversion complete interrupt
#endif //
//
for(; { // for-ever
#ifdef ADC_SLEEP //
ADC10CTL0 |= (ENC | ADC10SC); // Begin ADC conversion
__bis_SR_register(LPM0_bits + GIE); // Sleep until conversion complete
#else //
ADC10CTL0 &= ~ADC10IFG; // Clear conversion complete flag
ADC10CTL0 |= (ENC | ADC10SC); // Begin ADC conversion
while(!(ADC10CTL0 & ADC10IFG)); // Wait for conversion to complete
#endif //
//
adc = ADC10MEM; // Read ADC
//
// Convert to temperature
c = ((27069L * adc) - 18169625L) >> 16; // Vref = 1.5V
//c = ((45115L * adc) - 18169625L) >> 16; // Vref = 2.5V
//
k = ((27069L * adc) - 268467L) >> 16; // Vref = 1.5V
//k = ((45115L * adc) - 268467L) >> 16; // Vref = 2.5V
//
f = ((48724L * adc) - 30634388L) >> 16; // Vref = 1.5V
//f = ((81206L * adc) - 30634388L) >> 16; // Vref = 2.5V
//
// Convert to millivolts
mv = (96094L * adc + 32768) >> 16; // Vref = 1.5V
//mv = (160156L * adc + 32768) >> 16; // Vref = 2.5V
//
// Display on LCD
print_int(f, 0); // Degrees F
print_int(c, 2); // Degrees C
print_int(k, 4); // Degrees K
//
#ifdef SHOW_ADC //
print_v(adc, 72); // ADC
print_v(mv, 78); // ADC millivolts
#endif //
//
//__delay_cycles(100000); //
} //
}
#pragma vector = ADC10_VECTOR // ADC conversion complete interrupt
__interrupt void ADC10_ISR(void) //
{ //
__bic_SR_register_on_exit(LPM0_bits); // Wakeup main code
} //
lcd.h
typedef enum {
lcd_command = 0, // Array of one or more commands
lcd_data = 1, // Array of one or more bytes of data
lcd_data_repeat = 2 // One byte of data repeated
} lcd_cmd_type;
void lcd_send(const unsigned char *cmd, unsigned len, const lcd_cmd_type type);
void lcd_home(void);
void lcd_pos(unsigned char x, unsigned char y);
void lcd_clear(unsigned char x);
void lcd_init(void);
void lcd_print(char *s, unsigned x, unsigned y);
void pd12(unsigned n, unsigned x, unsigned y);
I wanted to try out myself how well solar energy really works. Toward
this aim I built a solar collector system for waterheating. A small boiler with a
heat exchanger is used for heat storage and a ordinary heating pump
for the water circulation. Temperature is measured at the collector,
at the entrance and outlet of the heat exchanger and inside the heat
storage boiler. As a controller for the pump I used an MSP430
launchpad. There were several problems I had to solve.
First, temperature must be measured with a resolution of at least 1
degree. My preferred temperature sensor would have been a Pt100. This
sensors are accurate and linear, but not very sensitive. With a 8 bit
AD converter, a resolution of not more than 4 degree is possible, not
good enough. Therefore I was forced to use NTC sensors. These are more
sensitive, but not very accurate and the resistance changes with
temperature not linearly, but exponentially. When I tried this out, I
had to realize that the exponential function of C uses too much
memory. Therefore, I had to find a replacement and I used a rational
approximation that uses much less space and works satisfactorily
accurate. Further, as the NTC sensors are not accurate enough, I had to measure
the value of each and correct by software.
To supervise the working of the controller I transmit the temperature
values, the state of the pump and what action is taken to a PC that
can be connected to the launchpad. This is actually not needed for the
working of the controller, but it is nice to have a mean to see what
is going on.
Here is how the program works:
After initialization we enter an infinite loop, that does the actual
work.
- First all 4 temperature values are measured. According to these values
we decide what to do.
- If the boiler temp is too high, we switch the pump of for security
reasons.
- If the pump is off and the collector temp is higher than the boiler
temp by some margin and the collector temp is above some minimal
value, we switch the pump on.
- If the pump is on, we compare the entry and outlet temp of the heat
exchanger. If these differ less than some value, that is, practically
no heat is put into the boiler, we switch the pump off.
- Finally we toggle a LED every 1 sec to indicate that the controller is
working and wait 1 minute before we start the next cycle.
To make the temperature measurement more reliable, I measure the temp
8 times and take the average.
To minimize current consumption I put the CPU to sleep during wait.
This controller now works for more than half an year to my full
satisfaction. I get approx. 0.75 kW / m^2 heating power on a sunny day. I only regret, that we do not have more sunny days
here in Switzerland.
And here is the program that uses up nearly every bit of the 2K memory:
=======================================================
/* Solar collector controller
* Daniel Huber 1.7.2011 daniel_huber@sunrise.ch
*
* Measures Pin 1.3,1.4,1.5,1.7 with average
* Calculates temperatures from measured values
* If boiler temp too high -> do nothing
* If collector temp > boiler+ TDelCol switch on pump, wait 1 min.
* If temp of heatexchanger input < heat exchanger output+TDelEx switch off pump, repeat
* send values to RS232
*/
#include "msp430g2231.h"
#include "stdbool.h"
#include "stdlib.h"
#include "math.h"
#include "string.h"
#define Tcm 110 // max temp at collector
#define Tcmin 30 // min temp at collector
#define Tbm 90 // max temp at boiler
#define TDelCol 5 // min. temp between Collector and Boiler
#define TDelEx 2 // min. temp between heat exchanger in and out
#define TXD BIT1 // TXD on P1.1
#define Bit_time 104 // 9600 Baud, SMCLK=1MHz (1MHz/ 9600)=104
#define LEDR BIT0 // LED red
#define LEDG BIT6 // LED green
#define CHCOLLECT INCH_4 // measure channel collector
#define CHHEATEXIN INCH_5 // measure channel heat
exchanger
#define CHHEATEXOUT INCH_3 // measure channel heat
exchanger
#define CHBOILER INCH_7 // measure channel boiler
// pump on/off
#define PumpOn {P1OUT |= BIT0;PumpOnFl=true; }
#define PumpOff {P1OUT &= ~BIT0;PumpOnFl=false; }
unsigned char BitCnt; // Bit count, used when transmitting byte
unsigned int TXByte; // value sent over UART when Transmit()
is called
int i,j; // 'for' loop variable
int TCol,TExIn,TExOut,TBoiler,channel; // Measured ADC Value
bool PumpOnFl; // pump on flag
unsigned int ADCValue; // aux. value to return value from
interrupt
short state; // state variable
char msg[3];
float cor,R;
// Function Definitions
void Transmit(); //transmits a byte
int Single_Measure(); //measures with avarage
void Single_Measure0(); //measure single value
void TransmitRecord(/*int TCol,int TExIn,int TExOut,int TBoiler,bool PumpOnFl*/); // transmit one record
void delay1(); //delays 1 sec
int P2T(int P); // ADC value to degree
void main(void) {
WDTCTL = WDTPW + WDTHOLD+WDTCNTCL; // Stop WDT: PWD,Hold, Counter Reset
BCSCTL1 = CALBC1_1MHZ; // Set range
DCOCTL = CALDCO_1MHZ; // SMCLK = DCO = 1MHz
// BCSCTL3 |= LFXT1S1; // sets LFXT1Sx to 0b10, VLO mode, bit not set-> 32KHz
BCSCTL3 |= XCAP_2; // set 10pF for cristal oscillator
P1SEL |= TXD; // Connect TXD to timer pin
P1DIR |= TXD | LEDR | LEDG; // use TX
PumpOff; // Pump off
state=1;
__bis_SR_register(GIE); // interrupts enabled
while(1){
msg[0]='\0'; // empty string
cor=1.01*10.12;channel=CHCOLLECT; //correction factor:
TCol=P2T(Single_Measure()); // collector temp
cor=1*10.32;channel=CHHEATEXIN;
TExIn=P2T(Single_Measure()); // heat exchanger in temp
cor=0.89*10.21;channel=CHHEATEXOUT; // 0.937 takes care of R20 difference
TExOut=3+P2T(Single_Measure()); // heat exchanger out temp, correct for NTC tolerance
cor=1*10.32;channel=CHBOILER;
TBoiler=P2T(Single_Measure());// boiler temp
if (TBoiler > Tbm && state!=0) {PumpOff;state=0;strcat(msg,"st00");}
switch(state){
case(0): if(TBoiler case(1): if((TCol>TBoiler+TDelCol) && TCol>=Tcmin) {PumpOn; state=2;strcat(msg,"12");} break;
case(2): if(TExIn }
TransmitRecord();
for(i=1;i<60;i++){
P1OUT ^= LEDG; // toggle LED at P1.6
delay1();
}
}
}
// transmit one record
void TransmitRecord(){
unsigned int k;
i=0;
while(i>=0){
switch(i){
case(0): k='SS'; break;
case(1): k='tt'; break;
case(2): k=TCol; break;
case(3): k=TExIn; break;
case(4): k=TExOut; break;
case(5): k=TBoiler; break;
case(6): if(PumpOnFl)k=1; else k=0; break;
case(7): if (strlen(msg)>0){k=256*msg[1]+msg[0]; break;} else {i++;}
case(8): k='EE'; break;
case(9): {k='nn'; i=-2;}
};
TXByte = (k & 0x00FF); // Set TXByte
Transmit(); // Send
TXByte = k >> 8; // Set TXByte to the upper 8 bits
TXByte = TXByte & 0x00FF;
Transmit();
i++;
}
}
// averaged single measurement
int Single_Measure(/*int channel*/){
int ADCAvg = 0;
for (i = 0; i < 8; i++){ // add up values
Single_Measure0(channel);
ADCAvg += ADCValue;
}
ADCAvg >>= 3; // divide by 8
return ADCAvg;
}
/**
* Reads ADC channel once, using AVCC as reference.
**/
void Single_Measure0(/*int channel*/) {
ADC10CTL0 &= ~ENC; // Disable ADC
ADC10CTL0 = ADC10SHT_3 + ADC10ON + ADC10IE; // 64 clock ticks, ADC On, enable ADC interrupt
ADC10CTL1 = ADC10SSEL_3 +channel; // Set 'chan', SMCLK
__delay_cycles(1000);
ADC10CTL0 |= ENC + ADC10SC; // Enable and start conversion
_BIS_SR(CPUOFF + GIE); // sleep CPU
}
/**
* Transmits the value currently in TXByte. The function waits till it is
* finished transmiting before it returns.
**/
void Transmit() {
TXByte |= 0x100; // Add stop bit to TXByte (which is logical 1)
TXByte = TXByte << 1; // Add start bit (which is logical 0)
BitCnt = 0xA; // Load Bit counter, 8 bits + ST/SP
CCTL0 = OUT; // TXD Idle as Mark
TACTL = TASSEL_2 + MC_2; // SMCLK, continuous mode
CCR0 = TAR; // Initialize compare register
CCR0 += Bit_time; // Set time till first bit
CCTL0 = CCIS0 + OUTMOD0 + CCIE; // Set signal, intial value, enable interrupts
while ( CCTL0 & CCIE ); // Wait for previous TX completion
}
/**
* ADC interrupt routine. Pulls CPU out of sleep mode.
**/
#pragma vector=ADC10_VECTOR
__interrupt void ADC10_ISR (void)
{
ADCValue = ADC10MEM; // Saves measured value.
__bic_SR_register_on_exit(CPUOFF); // Enable CPU so the main while loop continues
}
/**
* Timer interrupt routine. This handles transmitting and receiving
bytes.
**/
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void) {
CCR0 += Bit_time; // Add Offset to CCR0
if ( BitCnt == 0) // If all bits TXed
{
TACTL = TASSEL_2; // SMCLK, timer off (for power consumption)
CCTL0 &= ~ CCIE ; // Disable interrupt
}
else
{
CCTL0 |= OUTMOD2; // Set TX bit to 0
if (TXByte & 0x01)
CCTL0 &= ~ OUTMOD2; // If it should be 1, set it to 1
TXByte = TXByte >> 1;
BitCnt --;
}
}
/**
* function to get temperature from measured ADC value
**/
int P2T(int P){
#define P0 0x3FF
R=cor*P/(P0-P);
return(
(159.444F+R*(29.4008F-0.21077F * R))/(1+R*(0.59504F+0.0155797F * R))
);
}
//put CPU to sleep for 1 sec
void delay1(){
IE1 |= WDTIE; // Watchdog Interrupt Enable
WDTCTL = WDTPW+WDTTMSEL+WDTSSEL; // Passwd(WDTPW), Counter-Mode
//Intervall(WDTTMSEL),Timer= "0"(WDTCNTCL),
//SourceClk=ACLCK(WDTSSEL),Sel:00=Clk/32768 01=Clk/8192 10:Clk/512 11=Clk/64
_BIS_SR(LPM3_bits + GIE); // put CPU to sleep LPM1_bits
}
// delay interrupt routine, wakes up CPU
#pragma vector=WDT_VECTOR
__interrupt void WATCHDOG_ISR (void){ // interrupt routine for delay
WDTCTL = WDTPW + WDTHOLD+WDTCNTCL; //Password(WDTPW),
//Watchdog stop(WDTHOLD),Counter=0, this resets register(exeption: hold bit)
// IE1 &= ~WDTIE; // Watchdog Interrupt Disable
__bic_SR_register_on_exit(LPM3_bits); // clear LPM3 bits so the main while loop continues
}
========================================================
Since I did the IR transmitter, I figured I need a receiver, so here's my first iteration. For this project, I am using Vishay's TSOP38238, which takes care of detecting, amplifying, filtering, and demodulating IR signal. Output from IR module is connected directly to port P1.1, which is configured as capture input. Timer's capture/compare capability is used to process the signal. P1.3-P1.5 are connected to LEDs to show the result (LEDs are toggled so sometimes they stay on, it's not a bug.) The only problem with this design is that some remotes could have shorter off period, which would screw up the logic (at least that's what I've read somewhere, but then again, you cannot always believe what you read on the net.)
#include "msp430g2231.h"
#define T05 300
#define T65 T05*13
#define T2 T05*4
#define T3 T05*6
unsigned int rxData = 0; // received data: A4-A0 and C6-C0 0000 AAAA ACCC CCCC
unsigned int bitCounter = 0;
void main(void)
{
WDTCTL = WDTPW + WDTHOLD; // stop WDT
BCSCTL1 = CALBC1_1MHZ; // load calibrated data
DCOCTL = CALDCO_1MHZ;
P1DIR &= ~BIT1; // P1.1 input
P1SEL = BIT1; // P1.1 Timer_A CCI0A
P1OUT &= ~(BIT3 + BIT4 + BIT5); // P1.3-1.5 out
P1DIR |= (BIT3 + BIT4 + BIT5);
TACTL = TASSEL_2 | MC_2; // SMCLK, continuous mode
CCTL0 = CM_2 | CCIS_0 | CAP | CCIE; // falling edge capture mode, CCI0A, enable IE
__bis_SR_register(LPM0_bits + GIE); // switch to LPM0 with interrupts
}
#pragma vector=TIMERA0_VECTOR
__interrupt void Timer_A (void)
{
if(CCTL0 & CAP) { // start bit
bitCounter++; // start counting bits
CCR0 += T65; // add 6.5 bits to counter
CCTL0 &= ~ CAP; // compare mode
} else {
switch (bitCounter) {
case 0x1000: // received all bits
bitCounter = 0; // reset counter
// process received data, for example toggle LEDs
switch (rxData & 0x001F) { // mask device number
case 19: // Volume - 0010011 = 19
P1OUT ^= BIT3;
break;
case 18: // Volume + 0010010 = 18
P1OUT ^= BIT4;
break;
case 21: // Power 0010101 = 21
P1OUT ^= BIT5;
break;
}
rxData = 0;
// end process received data
CCTL0 |= CAP; // capture mode
break;
default: // data bit
if (CCTL0 & SCCI) { // bit = 1
CCR0 += T2; // add 2 bits to counter
} else { // bit = 0
rxData |= bitCounter; // set proper bit of rxData
CCR0 += T3; // add 3 bits to counter
}
bitCounter <<= 1; // increase (shift) bit counter
break;
}
}
}
The first diagram shows how the current code works, the second one shows how it should work to be more reliable (new code to follow.)