My project was to create a automatic plant waterer. I have some sensors(soil moisture sensor, DHT22 air temp and humidity sensor, and others that are working fine). Here is my first issue.
So I have this DHT22 air temperature and humidity sensor. The way it works is that the microcontroller sends a constant high signal to the single serial port. The sensor is told to send data for air temp and humidity when that GPIO output pin sending the high signal goes low for at least 1ms. After 1ms (or more) that GPIO pin switches to input in order to be ready to read the signal from the serial port. In general, if the HIGH signal coming from the sensor lasts longer than 50us, it is a '1' bit, and if it lasts less than 50us it is a '0' bit value. I slowly built a code up from scratch by making use of a timer, a bunch of loops, and variables. I was able to simulate my code fine when I used it in the main but when using it in a UART interrupt it acts crazy as hell. I used UART outputs to watch the values and see how their changing, and I see my bit values jump to crazy high numbers in the 10's of thousands. Mind you these values are assigned during a conditional statement and can only become 1 or 0. When this happens it screws with my bit conversions to get the actual values of the air temp and humidity. So, if anyone has any idea why this is happening, please help me out. This was for a class project and unfortunately it didn't get turned in fully complete, but I have put so much time into it I want to see this thing properly functioning.
Another minor issue I have is using sysctldelay in one of my timer interrupt handlers it. This interrupt is used to periodically check the soil moisture conditions by calling another ADC interrupt that is connected to the soil moisture sensor. If it is below a certain value i want it to run an output that triggers the pump for 15 sec, so i tried to use sysctldelay for this. However anytime sysctldelay is in the interrupt, everything appears to work except for the call to the ADC interrupt to retrieve the value. Then when I magically remove it, the interrupt runs fine.
So there are all my issues with this project, so far. I really have a lot of plans to build on this but I really need to figure out these crazy issues first. Thank you!
I just posted my entire code. The section where the issues occur with the bit values being calculated to crazy high numbers occurs in the UARIntHandler and the section with the sysctldelay issues is at the Timer0A_Handler.
#include <stdint.h>
#include <math.h>
#include <stdbool.h>
#include "inc/hw_memmap.h"
#include "inc/hw_types.h"
#include "driverlib/debug.h"
#include "driverlib/sysctl.h"
#include "driverlib/adc.h"
#include "driverlib/interrupt.h"
#include "inc/tm4c123gh6pm.h"
#include "inc/hw_gpio.h"
#include "driverlib/pin_map.h"
#include "driverlib/rom_map.h"
#include "driverlib/gpio.h"
#include "driverlib/timer.h"
#include "driverlib/sysctl.h"
#include "driverlib/uart.h"
#include "utils/uartstdio.h"
#define BLUE_MASK 0x04
uint32_t ui32ADC0Value[1]; //store sample from ADC0 SS3
uint32_t ui32ADC1Value[1]; //store sample from ADC1 SS3
volatile uint32_t Photoresistor; //Photoresistor
volatile uint32_t Soil_Moisture; //Soil Moisture
volatile unsigned long Timer_Value;
uint32_t Letter;
volatile unsigned long timerValue, timeConverted, count, timeInMicro, timeInMicroPrevious, timeMicroLoop;
volatile int digit[32], soil_moisture_percentage, photoresistor_percentage;
volatile double humidity, temperature, temperatureCelsius, humidityRelative, temperatureFahrenheit;
//*****************************************************************************
void
PortFunctionInit(void)
{
// Enable Peripherals
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOF);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOE);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
// Enable pin PE3 and PE2 for ADC AIN0 and ADC AIN1 respectively
GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_3);
GPIOPinTypeADC(GPIO_PORTE_BASE, GPIO_PIN_2);
// Enable pin PE4 and PE5 for GPIOInput
GPIOPinTypeGPIOInput(GPIO_PORTE_BASE, GPIO_PIN_3);
GPIOPinTypeGPIOInput(GPIO_PORTE_BASE, GPIO_PIN_2);
HWREG(GPIO_PORTF_BASE + GPIO_O_LOCK) = GPIO_LOCK_KEY;
HWREG(GPIO_PORTF_BASE + GPIO_O_CR) = 0x1;
//
// Enable pin PF1, PF2, PF3, PE4, and PE5 for GPIOOutput (LEDs)
//
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_3);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_1);
GPIOPinTypeGPIOOutput(GPIO_PORTF_BASE, GPIO_PIN_2);
GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_4);
GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_5);
//
// Enable pin PE4, PE5, & PF0 for GPIOInput
//
GPIOPinTypeGPIOInput(GPIO_PORTF_BASE, GPIO_PIN_0);
// Enable pull-up on PF0
GPIO_PORTF_PUR_R |= 0x01;
}
//Globally enable interrupts
void IntGlobalEnable(void)
{
__asm(" cpsie i\n");
}
void UART_init(void)
{
//Enable Peripherals
SysCtlPeripheralEnable(SYSCTL_PERIPH_UART0);
SysCtlPeripheralEnable(SYSCTL_PERIPH_GPIOA);
// Configure Pins
GPIOPinConfigure(GPIO_PA0_U0RX);
GPIOPinConfigure(GPIO_PA1_U0TX);
GPIOPinTypeUART(GPIO_PORTA_BASE, GPIO_PIN_0 | GPIO_PIN_1);
//UARTConfigSetExpClk(UART0_BASE, SysCtlClockGet(), 115200, //don't need
//(UART_CONFIG_WLEN_8 | UART_CONFIG_STOP_ONE | UART_CONFIG_PAR_NONE)); //don't need
UARTClockSourceSet(UART0_BASE, UART_CLOCK_PIOSC);
UARTStdioConfig(0, 115200, 16000000);
IntMasterEnable(); //enable processor interrupts
IntEnable(INT_UART0); //enable the UART interrupt
UARTIntEnable(UART0_BASE, UART_INT_RX | UART_INT_RT); //only enable RX and TX interrupts
//code one line above will need to change to switch # in setup_rvmdk.S
}
//ADC0 initializaiton
void ADC0_Init(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ); // configure the system clock to be 40MHz
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC0); //activate the clock of ADC0
SysCtlDelay(2); //insert a few cycles after enabling the peripheral to allow the clock to be fully activated.
ADCSequenceDisable(ADC0_BASE, 2); //disable ADC0 before the configuration is complete
ADCSequenceConfigure(ADC0_BASE, 2, ADC_TRIGGER_PROCESSOR, 1); // will use ADC0, SS1, processor-trigger, priority 0
//ADCSequenceStepConfigure(ADC0_BASE, 2,0,ADC_CTL_CH0);
ADCSequenceStepConfigure(ADC0_BASE, 2,0,ADC_CTL_CH1|ADC_CTL_IE|ADC_CTL_END);
IntPrioritySet(INT_ADC0SS2, 0x01); // configure ADC0 SS3 interrupt priority as 0
IntEnable(INT_ADC0SS2); // enable interrupt 33 in NVIC (ADC0 SS3)
ADCIntEnableEx(ADC0_BASE, ADC_INT_SS2); // arm interrupt of ADC0 SS3
ADCSequenceEnable(ADC0_BASE, 2); // enable ADC0
}
void ADC1_Init(void)
{
SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ); // configure the system clock to be 40MHz
SysCtlPeripheralEnable(SYSCTL_PERIPH_ADC1); //activate the clock of ADC0
SysCtlDelay(2); //insert a few cycles after enabling the peripheral to allow the clock to be fully activated.
ADCSequenceDisable(ADC1_BASE, 3); //disable ADC0 before the configuration is complete
ADCSequenceConfigure(ADC1_BASE, 3, ADC_TRIGGER_PROCESSOR, 0); // will use ADC0, SS1, processor-trigger, priority 0
ADCSequenceStepConfigure(ADC1_BASE,3,0,ADC_CTL_CH0|ADC_CTL_IE|ADC_CTL_END);
IntPrioritySet(INT_ADC1SS3, 0x02); // configure ADC0 SS3 interrupt priority as 0
IntEnable(INT_ADC1SS3); // enable interrupt 33 in NVIC (ADC0 SS3)
ADCIntEnableEx(ADC1_BASE, ADC_INT_SS3); // arm interrupt of ADC0 SS3
ADCSequenceEnable(ADC1_BASE, 3); // enable ADC0
}
void UARTIntHandler(void){
uint32_t ui32Status;
ui32Status = UARTIntStatus(UART0_BASE, true); //get interrupt status
while(UARTCharsAvail(UART0_BASE)) //loop while there are chars
{
//UARTprintf("Temperature: %d F \n \n \r ", temperatureFahrenheit); // print temperature(F) value to UART
Letter = UARTCharGetNonBlocking(UART0_BASE);
if(Letter == 'R')
{
///// INITIATE CALL TO SENSOR FOR DATA //////
GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_4, 0x00); // PE3 Output Low (wake up signal to sensor)
SysCtlDelay(288000); // hold low input for 2ms
GPIOPinTypeGPIOInput(GPIO_PORTE_BASE, GPIO_PIN_4); // PE3 Switch to GPIO Input to recieve data from sensor
////// END OF DATA REQUEST //////
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0xff); // red LED ON (Pink light helps indicate state)
/////////////// START COUNT OF HIGH SIGNAL INPUT STATE BIT VALUES ///////////////////////
for (int i=0; i<=31; i=i)
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0xff); // green led turns on (white light helps indicate state)
//////////////////// ENTER LOW SIGNAL INPUT STATE ///////////////////
if(GPIOPinRead(GPIO_PORTE_BASE, GPIO_PIN_4)==0x00) // PE3 input is Low
{
TimerDisable(TIMER1_BASE, TIMER_A);
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0x00); // green led turns off (pink light helps indicate state)
timerValue = TimerValueGet(TIMER1_BASE, TIMER_A); // get value of timer, assign to timerValue
timeConverted = timerValue/40000000; // convert timerValue to show time in seconds
timeInMicro = timerValue/40; // convert timerValue to show time in micro seconds
timeMicroLoop = timeInMicro - timeInMicroPrevious; // how long timer was on during last high signal input
if(timeMicroLoop < 500000) // if greater than 1/2 a second
{
digit[i] = 0; // bit value for loop is 0
}
else
{
digit[i] = 1; // bit value for loop is 1
}
timeInMicroPrevious = timeInMicro; // used to find time in next high signal loop cycle
// //*************** UART OUTPUTS FOR DEBUGGING *****************//
UARTprintf("Total Timer Time(s): %d \n \r ", timeConverted);
UARTprintf("Total Timer Time(us): %d \n \r ", timeInMicro);
UARTprintf("Time of loop(us): %d \n \r ", timeMicroLoop);
UARTprintf("Digit %d (us): %d \n \n \r ", i, digit[i]);
i++; // advance for loop to find next time high input time
while(GPIOPinRead(GPIO_PORTE_BASE, GPIO_PIN_4)==0x00 && i < 32) // Loop While PE3 is Low until i = 32 so code doesn't get stuck when sensor sends low output after finishing data transmission
{
}
}
/////////////////// EXIT LOW SIGNAL INPUT STATE///////////////////
TimerEnable(TIMER1_BASE, TIMER_A); // start loop back up when signal is no longer low, and now high
}
////////////EXIT COUNT OF HIGH SIGNAL BIT VALUES FROM SENSOR//////////
TimerDisable(TIMER1_BASE, TIMER_A); // no more high signals are expected, stop timer from counting up
///////////////////CACULATION OF HUMIDITY AND TEMPERATURE BASED ON DIGIT VALUES FROM SENSOR////////////////////////
humidity = 0;
temperature = 0;
for(int e=0; e<16; e++) // for loop to turn binary values of the first 16 bits to decimal that represent humidity value
{
humidity = humidity + (digit[e]*(pow(2,e)));
}
for(int e=16; e<32; e++) // for loop to turn binary values of the last 16 bits to decimal that represent temperature value
{
temperature = temperature+ (digit[e]*(pow(2,(e-16))));
}
temperatureCelsius = ((120*temperature)/65535) - 40; // calculations to convert decimal into temperature in range of -40C to 80C
temperatureFahrenheit = ((temperatureCelsius*9)/5) + 32; //convert celsius value to fahrenheit
humidityRelative = (100*humidity)/65535; // calculation to convert decimal of humidity to value of 0%-100%
//////////////////END OF CALCULATIONS FOR HUMIDITY AND TEMPERATURE VALUES////////////////////////
// UARTprintf("Humidity: %d%%\n \n \r ", humidity); // print humidity value to UART
// UARTprintf("Temperature: %d F \n \n \r ", temperature); // print temperature(F) value to UART
//////////////UART OUTPUTS DISPLAYING CURRENT SENSOR READINGS/////////////////////
UARTprintf("Humidity: %d%%\n \n \r", humidityRelative); // print humidity value to UART
UARTprintf("Temperature: %d F \n \n \r", temperatureFahrenheit); // print temperature(F) value to UART
UARTIntClear(UART0_BASE, ui32Status); //clear the asserted interrupts
ADCProcessorTrigger(ADC0_BASE, 2); // initialize the ADC1 sequence
ADCProcessorTrigger(ADC1_BASE, 3); // initialize the ADC0 sequence
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x00); // red LED ON (Pink light helps indicate state)
}
}
}
void ADC0_Handler(void)
{
ADCIntClear(ADC0_BASE, 2);
ADCSequenceDataGet(ADC0_BASE, 2, ui32ADC0Value); // Gets ADC0 value
Photoresistor = ui32ADC0Value[0]; // give value for photoresistor
photoresistor_percentage = ((Photoresistor-60)*100);
photoresistor_percentage = (photoresistor_percentage/4025) - 100;
photoresistor_percentage = photoresistor_percentage * (-1); // give value for soil moisure variable
if(photoresistor_percentage < 0)
{
photoresistor_percentage = 0;
}
if (photoresistor_percentage > 100)
{
photoresistor_percentage = 100;
}
UARTprintf("Light Exposure: %d%% \n\r",photoresistor_percentage);
}
void ADC1_Handler(void)
{
ADCIntClear(ADC1_BASE, 3);
ADCSequenceDataGet(ADC1_BASE, 3, ui32ADC1Value); // Gets ADC0 value
Soil_Moisture = ui32ADC1Value[0];
soil_moisture_percentage = ((Soil_Moisture-2100)*100);
soil_moisture_percentage = (soil_moisture_percentage/1755) - 100;
soil_moisture_percentage = soil_moisture_percentage * (-1); // give value for soil moisure variable
if(soil_moisture_percentage < 0)
{
soil_moisture_percentage = 0;
}
if (soil_moisture_percentage > 100)
{
soil_moisture_percentage = 100;
}
UARTprintf("Soil Moisture: %d%% \n\r",soil_moisture_percentage);
}
void Timer0A_Handler(void)
{
TimerIntClear(TIMER0_BASE, TIMER_TIMA_TIMEOUT); //acknowledge flag for Timer0A timeout
ADCProcessorTrigger(ADC1_BASE, 3); // initialize the ADC1 sequence
//UARTprintf("Soil Moisture: %d%% \n\n\r",soil_moisture_percentage);
if(soil_moisture_percentage < 30) //when soil moisture is less than 30% water plant
{
UARTprintf("Pump waters plant for 15 seconds \n \n \r");
GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_5, 0xff); // PE3 Output Low (wake up signal to sensor)
//SysCtlDelay(40000000); // runs for 5 secs to save time during demo, change to 600000000 for 15s
// pumps flow is 1.2gpm, at 15s 0.3 gallons of water will be used to water plant
}
else
{
GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_5, 0x00); // PE5 Output low to keep pump off
}
}
void Timer1A_init()
{
SysCtlClockSet(SYSCTL_SYSDIV_5|SYSCTL_USE_PLL|SYSCTL_OSC_MAIN|SYSCTL_XTAL_16MHZ); // configure the system clock to be 40MHz
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER1);
TimerConfigure(TIMER1_BASE, TIMER_CFG_PERIODIC_UP);
TimerLoadSet(TIMER1_BASE, TIMER_A, 0xffffffff);
}
void Timer0A_Init(unsigned long period)
{
// Enable Peripheral Clocks
SysCtlPeripheralEnable(SYSCTL_PERIPH_TIMER0);
TimerConfigure(TIMER0_BASE, TIMER_CFG_PERIODIC); // configure for 32-bit timer mode
TimerLoadSet(TIMER0_BASE, TIMER_A, period -1); //reload value
IntPrioritySet(INT_TIMER0A, 3); // configure Timer0A interrupt priority as 0
IntEnable(INT_TIMER0A); // enable interrupt 19 in NVIC (Timer0A)
TimerIntEnable(TIMER0_BASE, TIMER_TIMA_TIMEOUT); // arm timeout interrupt
TimerEnable(TIMER0_BASE, TIMER_A); // enable timer0A
}
int main(void)
{
unsigned long period = 400000000; // set to 10s to save time during demo, will use 90s in real world application
PortFunctionInit();
UART_init(); // UART Interrupt Initializations
ADC1_Init(); // ADC1 Interrupt Initializations
ADC0_Init(); // ADC0 Interrupt Initializations
Timer0A_Init(period); // Timer0A Interrupt initializations
Timer1A_init(); // Timer1A Initializations
IntMasterEnable(); //enable processor interrupts
GPIO_PORTF_DATA_R |= 0x04;
timeMicroLoop=0;
timeInMicroPrevious=0;
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_2, 0xff); // Blue LED on (helps indicate state)
timeInMicroPrevious=0;
while(1)
{
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_3, 0x00); // green LED off
GPIOPinWrite(GPIO_PORTF_BASE, GPIO_PIN_1, 0x00); // red LED off
GPIOPinTypeGPIOOutput(GPIO_PORTE_BASE, GPIO_PIN_4); // Set PE3 to Output type
GPIOPinWrite(GPIO_PORTE_BASE, GPIO_PIN_4, 0xff); // PE3 Output High (will be set to low when ready for request signal to DHT22)
}
}