/** test_adc app: This app uses the CC2511's analog-to-digital converter (ADC) to read the voltages on all 6 analog inputs and measure the voltage of the Wixel's VDD (3V3) line. The results are reported to the computer either in CSV format or as a bar graph. == Parameters == input_mode: Specifies whether to enable internal pull-down resistors, enable internal pull-up resistors, or just let the analog lines float. 1 = Pull-ups 0 = Float (default) -1 = Pull-downs bar_graph: Specifies whether to print out a bar graph or not. 1 = Print a bar graph (default, requires you to use a terminal program that supports VT100 commands) 0 = Print the 7 readings on a single line, separated by commas. report_period_ms: Specifies the number of milliseconds to wait between reports to the computer. The default is 40. */ #include #include #include #include #define T1CCTL2_IM 0x40 // Channel 2 Interrupt mask #define T1CCTL2_CMP 0x38 #define T1CCTL2_CMP0 0x08 #define T1CCTL2_CMP1 0x10 #define T1CCTL2_CMP2 0x20 #define T1CCTL2_MODE 0x04 // Capture or compare mode #define T1CCTL2_CAP 0x03 #define T1CCTL2_CAP0 0x01 #define T1CCTL2_CAP1 0x02 // P2DIR (0xFF) - Port 2 Direction #define P2DIR_PRIP0 0xC0 #define P2DIR_0PRIP0 0x40 #define P2DIR_1PRIP0 0x80 #define P2DIR_DIRP2 0x1F #define P2DIR_PRIP0_0 (0x00 << 6) #define P2DIR_PRIP0_1 (0x01 << 6) #define P2DIR_PRIP0_2 (0x02 << 6) #define P2DIR_PRIP0_3 (0x03 << 6) #define MAX_BUF_SZ 32 #define DATA_AQU_TIME 250 // 250 msecs uint16 CODE adcOutputX[MAX_BUF_SZ] = {0}; uint16 CODE adcOutputY[MAX_BUF_SZ] = {0}; uint16 CODE adcOutputZ[MAX_BUF_SZ] = {0}; /* PARAMETERS *****************************************************************/ int32 param_input_mode = -1; int32 param_bar_graph = 1; int32 param_report_period_ms = 40; uint32 XDATA triggerTime = 0; /* VARIABLES ******************************************************************/ // A big buffer for holding a report. This allows us to print more than // 128 bytes at a time to USB. uint8 XDATA report[1024]; // The length (in bytes) of the report currently in the report buffer. // If zero, then there is no report in the buffer. uint16 DATA reportLength = 0; // The number of bytes of the current report that have already been // send to the computer over USB. uint16 DATA reportBytesSent = 0; unsigned char P0_0_interrupt = 0; /* FUNCTIONS ******************************************************************/ void analogInputsInit() { switch(param_input_mode) { case 1: // Enable pull-up resistors for all pins on Port 0. // This shouldn't be necessary because the pull-ups are enabled by default. P2INP &= ~(1<<5); // PDUP0 = 0: Pull-ups on Port 0. P0INP = 0; break; case -1: // Enable pull-down resistors for all pins on Port 0. P2INP |= (1<<5); // PDUP0 = 1: Pull-downs on Port 0. P0INP = 0; // This line should not be necessary because P0SEL is 0 on reset. break; default: // Disable pull-ups and pull-downs for all pins on Port 0. P0INP = 0x3F; break; } } void initAdc2Dma(uint8 bufferSize) { // Initialize the three axis buffers and be done. // X AXIS //First 5 bits of DC6 selects DMA trigger // 20 ADC EOC in a seq sample rdy // 21 ADC EOC ch 0 in seq smpl rdy // 22 ADC EOC ch 1 in seq smpl rdy // 23 ADC EOC ch 2 in seq smpl rdy // Last 3 bits of DC6: // 0b110 // // NOTE: I HAVE USED numbers for individual channels as we also have // the radio channel to work with for transmit. // dmaConfig._2.SRCADDRH = XDATA_SFR_ADDRESS(ADCH); dmaConfig._2.SRCADDRL = XDATA_SFR_ADDRESS(ADCL); dmaConfig._2.DESTADDRH = (uint16)adcOutputX >> 8; dmaConfig._2.DESTADDRL = (uint16)adcOutputX; dmaConfig._2.LENL = bufferSize; dmaConfig._2.VLEN_LENH = 0x00; dmaConfig._2.DC6 = 0b11010101; // Channel 0 is 21 dmaConfig._2.DC7 = 0b00101010; // IRQ (bit 3) is enabled // Y AXIS dmaConfig._3.SRCADDRH = XDATA_SFR_ADDRESS(ADCH); dmaConfig._3.SRCADDRL = XDATA_SFR_ADDRESS(ADCL); dmaConfig._3.DESTADDRH = (uint16)adcOutputY >> 8; dmaConfig._3.DESTADDRL = (uint16)adcOutputY; dmaConfig._3.LENL = bufferSize; dmaConfig._3.VLEN_LENH = 0x00; dmaConfig._3.DC6 = 0b11010110; // Channel 1 is 22 dmaConfig._3.DC7 = 0b00101010; // IRQ (bit 3) is enabled // Z AXIS dmaConfig._4.SRCADDRH = XDATA_SFR_ADDRESS(ADCH); dmaConfig._4.SRCADDRL = XDATA_SFR_ADDRESS(ADCL); dmaConfig._4.DESTADDRH = (uint16)adcOutputZ >> 8; dmaConfig._4.DESTADDRL = (uint16)adcOutputZ; dmaConfig._4.LENL = bufferSize; dmaConfig._4.VLEN_LENH = 0x00; dmaConfig._4.DC6 = 0b11010111; // Channel 2 is 23 dmaConfig._4.DC7 = 0b00101010; // IRQ (bit 3) is enabled } void initDSM(void) { P0SEL |= (BIT2 | BIT1 | BIT0); PERCFG |= 0x40; P0DIR &= ~(BIT2 | BIT1 | BIT0); // 0,1,2 is input T1CTL &= ~0x03; // Set mode (timer) to 00 ==> Suspend T1CNTL = 0x00; // Clear Timer 1 counter by writing any value // Set Sample rate by writing to T1CC0 T1CC0H = 0x25; // Timer 1 capture/compare value high T1CC0L = 0x22; // Timer 1 capture/compare value low // 0x2522 = 3249 // This value should be equivalent of 396 usecs // Which is TODO.. T1CCTL0 |= 0x04; // Set Timer 1 channel 0 to compare mode T1CC1L = 0; // Load first sample as 0000 T1CC1H = 0; T1CTL |= 0x02; // Set to modulo mode 0000 to T1CC0 T1CCTL1 &= ~0x07; // Timer 1 channel 1 compare control clear bit 0 1 & 2 // which is no Capture when CMP is still not set to // 111. When set to 111, DSM will start OVFIM = 1; // Enable Overflow interrupt //INT_ENABLE(INUM_T1, INT_ON); // Enable Timer 1 interrupt T1CCTL1 |= T1CCTL1_IM; // Enabling DSM mode will start DSM right away. DSM mode should // be enabled rising or falling edge is detected?? // T1CCTL1 |= 0x38; // Set to DSM Mode // Enable global interrupt by setting the IEN0.EA=1 EA = 1; } void enableDSM(void) { // We may need only the last three lines, for now keep it T1CTL &= ~0x03; // Set mode (timer) to 00 ==> Suspend T1CNTL = 0x00; // Clear Timer 1 counter by writing any value T1CCTL0 |= 0x04; // Set Timer 1 channel 0 to compare mode, BIT 2 T1CC1L = 0; // Load first sample as 0000 T1CC1H = 0; T1CTL |= 0x02; // Set to modulo mode 0000 to T1CC0 T1CCTL1 &= ~0x07; // Timer 1 channel 1 compare control clear bit 0 1 & 2 // which is no Capture when CMP is still not set to // 111. When set to 111, DSM will start OVFIM = 1; // Enable Overflow interrupt //INT_ENABLE(INUM_T1, INT_ON); // Enable Timer 1 interrupt T1CCTL1 |= T1CCTL1_IM; // Enabling DSM mode will start DSM right away. DSM mode should // be enabled rising or falling edge is detected?? // T1CCTL1 |= 0x38; // Set to DSM Mode // Enable global interrupt by setting the IEN0.EA=1 EA = 1; T1CCTL1 |= 0x38; // Set to DSM Mode which will trigger DSM } void disableDSM(void) { T1CTL &= ~0x03; // Set mode (timer) to 00 ==> Suspend T1CCTL1 &= ~0x07; // Timer 1 channel 1 compare control clear bit 0 1 & 2 // which is no Capture when CMP is still not set to // 111. When set to 111, DSM will start OVFIM = 0; // Disable Overflow interrupt and Timer 1 interrupt T1CCTL1 &= ~T1CCTL1_IM; T1CCTL1 &= ~0x38; // Disable DSM Mode EA = 1; // But leave global interrupt enabled setting IEN0.EA=1 } void enablePort0Trigger(void) { PICTL = (PICTL & ~0b00000001); // Setup Rising edge IEN1 = 0b00100000; //enable port 0 interrupts PICTL = 0b00001000; //Port 0, inputs 3 to 0 interrupt enable, //and rising edge interrupt EA = 1; } ISR(P0INT,0) { P0_0_interrupt++; LED_RED_TOGGLE(); LED_YELLOW_TOGGLE(); P0IFG = 0; //clear them all, safely IRCON &= 0b10011111; //clear port 0 flag and docs //say bit six must always be 0 // Enabling DSM mode will start DSM right away. DSM mode should // be enabled rising or falling edge is detected?? enableDSM(); ADCCON1 |= 0b01110000; //Start ADC Conversion // // NOW ENABLE Timer1 Interrupts for SAMPLING // IEN1 = 0b00000001; //disable port 0 interrupts & enable T1 interrupt delayMicroseconds(396); // Delay for all 3 times ADC conversion time // for all three axes triggerTime = getMs(); initDSM(); // Initialize DSM for about 250 msecs // starting with the port 0 trigger point enableDSM(); EA = 1; //enable all interrupts } uint8 timer1Count = 0; volatile uint16 timer1Ch0CapResult[MAX_BUF_SZ] = {0}; ISR(T1,0) { // Clear Timer 1 channel 0 interrupt flag. The clearing has to be like this // to avoid loosing other Timer 1 interrupts (writing 1 to T1CTL[7:4] has // no effect as these are R/W0 accessible). This has to be done for W0 // accessible interrupt flags. T1CTL = (~T1CTL_CH0IF & 0xF0) | (T1CTL & 0x0F); if (timer1Count >= MAX_BUF_SZ) { for (timer1Count = 0; timer1Count < MAX_BUF_SZ; timer1Count++) timer1Ch0CapResult[timer1Count] = 0; timer1Count = 0; } // The Timer value when a capture has occured // timer1Ch0CapResult[timer1Count] = T1CC0L; // timer1Ch0CapResult[timer1Count] |= (T1CC0H << 8); timer1Count++; // Check if time period falls within 250 msecs of the trigger point // if so start ADC which is followed by DMA. Else stop data acquisition // and enable trigger. if ((getMs() - triggerTime) < DATA_AQU_TIME) { ADCCON1 |= 0b01110000; // Start ADC Conversion followed by DMA } else { // Disable DSM and enable P0 interrupt trigger disableDSM(); //Port 0, inputs 3 to 0 interrupt enable, only for now //and rising edge interrupt enablePort0Trigger(); } } unsigned char DMA_IFG; // // ISR to handle DMA interrupts // ISR(DMA,0) { DMA_IFG = DMAIRQ; if (DMA_IFG & BIT0) { LED_RED(1); DMAIRQ &= ~BIT0; //clear the flag } if (DMA_IFG & BIT1) { LED_YELLOW(1); DMAIRQ &= ~BIT1; //clear the flag } if (DMA_IFG & BIT2) { LED_GREEN(1); DMAIRQ &= ~BIT3; //clear the flag } // Enable interrupt bits again dmaConfig._2.DC7 = 0b00101010; // IRQ (bit 3) is enabled dmaConfig._3.DC7 = 0b00101010; // IRQ (bit 3) is enabled dmaConfig._4.DC7 = 0b00101010; // IRQ (bit 3) is enabled IEN1 = 0b00100001; //enable port 0 interrupts and T1 interrupt EA = 1; //enable all interrupts } // Initilizes I/O Ports void initPort0_0to3AsInputAndEI(void) { // Port 0 // P0_0 ADC AI Analog Input (ADC) X // P0_1 ADC AI Analog Input (ADC) Y // P0_2 ADC AI Analog Input (ADC) Z // Is it P0SEL |= ???? or POSEL &= // P0SEL &= ~(BIT0 | BIT1 | BIT2); P0SEL |= (BIT0 | BIT1 | BIT2); P0DIR &= ~(BIT0 | BIT1 | BIT2); // Set P0_0, P0_1 and P0_2 to be input P0 = 0x00; P0INP &= ~(BIT0 | BIT1 | BIT2); // Pull up/down and not Tristate????? // // Setup interrupts // // Clear interrupt flags for P0 P0IFG &= ~(BIT0 | BIT1 | BIT2); P0IF = 0; // Set individual interrupt enable bit in the // Should'nt setting up interrupt bits be done last? // For now defer enabling interrupts // PICTL = (PICTL & ~BIT2) | 0x08; // Enable P0 interrupts // P0IE = 1; // Enable global interrupt by setting the [IEN0.EA = 1] // EA = 1; } void updateLeds() { usbShowStatusWithGreenLed(); LED_YELLOW(0); LED_RED(0); } // This gets called by puts, printf, and printBar to populate // the report buffer. The result is sent to USB later. void putchar(char c) { report[reportLength] = c; reportLength++; } // adcResult should be between 0 and 2047 inclusive. void printBar(const char * name, uint16 adcResult) { uint8 i, width; printf("%-4s %4d mV |", name, adcConvertToMillivolts(adcResult)); width = adcResult >> 5; for(i = 0; i < width; i++){ putchar('#'); } for(; i < 63; i++){ putchar(' '); } putchar('|'); putchar('\r'); putchar('\n'); } void sendReportIfNeeded() { static uint32 lastReport; uint8 bytesToSend, i; uint16 vddMillivolts, adcDataX, adcDataY, adcDataZ; // Create reports. if (getMs() - lastReport >= param_report_period_ms && reportLength == 0) { lastReport = getMs(); reportBytesSent = 0; vddMillivolts = adcReadVddMillivolts(); adcSetMillivoltCalibration(vddMillivolts); for(i = 0; i < MAX_BUF_SZ && i <= timer1Count; i += 2) { adcDataX = adcConvertToMillivolts(adcOutputX[i]); adcDataY = adcConvertToMillivolts(adcOutputY[i]); adcDataZ = adcConvertToMillivolts(adcOutputZ[i]); if (param_bar_graph) { printf("\x1B[0;0H"); // VT100 command for "go to 0,0" printf("timer1Count %5d P0IntCount %5d", timer1Count, P0_0_interrupt); printBar("%4d mv", adcDataX); printBar("%4d mv", adcDataY); printBar("%4d mv", adcDataZ); printf("VDD %4d mV", vddMillivolts); } else { printf("%2d %4d, %4d, %4d, %4d\r\n", i, adcDataX, adcDataY, adcDataZ, vddMillivolts); } } } if (timer1Count >= MAX_BUF_SZ) timer1Count = 0; // Send the report to USB in chunks. if (reportLength > 0) { bytesToSend = usbComTxAvailable(); if (bytesToSend > reportLength - reportBytesSent) { // Send the last part of the report. usbComTxSend(report+reportBytesSent, reportLength - reportBytesSent); reportLength = 0; } else { usbComTxSend(report+reportBytesSent, bytesToSend); reportBytesSent += bytesToSend; } } } #if 0 void initDsmMode() { /*************************************************************************** * Setup clock & frequency */ // Set the system clock source to HS XOSC and max CPU speed, // ref. [clk]=>[clk_xosc.c]. This is already being done so commenting // it out. All configurations for clock has been taken care of. Start // with Timer1Control configuraiton // SLEEP &= ~SLEEP_OSC_PD; // while( !(SLEEP & SLEEP_XOSC_S) ); // CLKCON = (CLKCON & ~(CLKCON_CLKSPD | CLKCON_OSC)) | CLKSPD_DIV_1; // while (CLKCON & CLKCON_OSC); // SLEEP |= SLEEP_OSC_PD; // Set max tick speed (CLKCON.TICKSPD = 0) // CLKCON = (CLKCON & ~CLKCON_TICKSPD) | TICKSPD_DIV_1; // Clear and set prescaler divider value to 1 T1CTL = (T1CTL & ~T1CTL_DIV) | T1CTL_DIV_1; /*************************************************************************** * Setup interrupt */ // Clear Timer 1 overflow interrupt flag // CPU interrupt flag (IRCON) for Timer 1 is cleared automatically by // hardware T1CTL &= ~T1CTL_OVFIF; // Set interrupt enable for overflow on Timer 1 and disable interrupt for // the other channels // Check if other interrupts should be disabled TODO.. OVFIM = 1; T1CCTL0 &= ~T1CCTL0_IM; // Disable interrupt on channel 0 T1CCTL1 &= ~T1CCTL1_IM; // Disable interrupt on channel 1 T1CCTL2 &= ~T1CCTL2_IM; // Disable interrupt on channel 2 // Enable interrupt by setting IEN1.T1IE T1IE = 1; // Enable global interrupt by setting the IEN0.EA=1 EA = 1; /*************************************************************************** * Setup peripheral I/O for Timer * * By choosing alternative 2 location for Timer 1 P1_1 becomes the * peripheral pin for DSM channel. This pin is also used as the input * to the audio interface on SMARTRF04EB. This way the result of DSM * can be listened to through the headphone output on SMARTRF04EB. */ // Select pin 1 on port 0 to be used as peripheral I/O P0SEL |= BIT1; // Choose alternative 2 location for Timer 1 PERCFG |= PERCFG_T1CFG; // Give priority to Timer 1 over USART 0 for using port 0 P0SEL |= 0x08; /*************************************************************************** * Setup DSM settings */ // Suspend Timer 1 T1CTL &= ~T1CTL_MODE; // Clear Timer counter T1CNTL = 0x00; // Set the sample rate. // This is done by dividing clock tick speed on sample frequency. // In this example, we are using tick speed of 26 MHz, and the desired sample // rate is 8 kHz. This can be calculated like this: 26000 / 8 = 3250 = 0x0CB1. T1CC0H = 0x0C; T1CC0L = 0xB1; // Set Timer 1 channel 0 to compare mode T1CCTL0 |= T1CCTL0_MODE; // Load first sample T1CC1L = 0x00; T1CC1H = 0x00; counter++; // Set Timer operation to modulo mode T1CTL |= T1CTL_MODE_MODULO; } #endif #if 0 void initTimer1CapMode() { // 0, 1, and 2 of Port 0 are inputs P0DIR &= ~(BIT2 | BIT1 | BIT0); PICTL = 0b00001000; //Port 0, inputs 3 to 0 interrupt enable, //and rising edge interrupt (Bit 0 0 is RisingEdge) //IEN1 = 0b00100000; //enable port 0 interrupts /*************************************************************************** * Setup interrupt */ // Clear Timer 1 channel 0 interrupt flag // CPU interrupt flag (IRCON) for Timer 1 is cleared automatically by hardware. T1CTL &= ~T1CTL_CH0IF; // Set individual interrupt enable bit in the peripherals SFR // // For now, only channel 0 is being used. We will have to // enable channel 1 and channel 2 as well later....TODO T1CCTL0 |= T1CCTL0_IM; // Enable interrupt on channel 0 T1CCTL1 &= ~T1CCTL1_IM; // Disable interrupt on channel 1 T1CCTL2 &= ~T1CCTL2_IM; // Disable interrupt on channel 2 OVFIM = 0; // Disable overflow interrupt // Enable Timer 1 interrupts by setting [IEN1.T1IE=1] IEN1 = 0; IEN1 |= BIT1; // Enable global interrupt by setting the [IEN0.EA=1] EA = 1; /*************************************************************************** * Setup peripheral I/O for Timer * * We can also choose the Alternative 2 location for Timer 1, or for the * peripherals * that use the same pins as Timer 1. This can be done by setting * PERCFG-register */ // Select P0_2 for peripheral function P0SEL |= (BIT0 | BIT1 | BIT2); // Set P2DIR to prioritize Timer 1's control over port 0 pins for // channel 0 over USART 0 P2DIR = (P2DIR & ~P2DIR_PRIP0) | P2DIR_PRIP0_2; /*************************************************************************** * Setup Timer settings * * Here we will select which channel(s) that will be used. We can choose to * use them in compare mode or capture mode. * * We can also select what mode the Timer shall operate on. When the mode is * selected, the Timer will start to run. Please see the data sheet for more * information. * * Notes: * - T1CCO is used by channel 1 & 2 for some compare modes, in case channels * are used simultaneously. * - In compare mode using modulo mode or up-down mode, channel 0 will * generate spike signals when [T1CCTL0.CMP = 3 or 4] since T1CC0 will * then be both the compare value and the overflow value. * - The input signal (pulse), when in capture mode, must have a duration * longer than the system clock. * - To choose 26 MHz clock speed, HS XOSC must be chosen as system clock * source. */ // Select capture mode, and set capture mode to capture on rising edge // T1CCTL0 = (T1CCTL0 & ~(T1CCTL0_CAP | T1CCTL0_MODE)) | T1C0_BOTH_EDGE; // The above assignment is modified as below T1CCTL0 = (T1CCTL0 & ~(T1CCTL0_CPSEL | T1CCTL0_CAP | T1CCTL0_MODE)) | (T1C0_RISE_EDGE | T1CCTL0_IM); // Set prescalar divider value to 1 to get a tickspeed ????? // set Timer 1 to free running mode T1CTL = (T1CTL & ~(T1CTL_MODE | T1CTL_DIV)) | T1CTL_MODE_FREERUN; // Disable Timer 1 interrupt now. T1 interrupts is enabled in ISR for Port 0 // REVISIT IEN1 &= BIT1; // Enable global interrupt by setting the [IEN0.EA=1] // REVISIT EA = 1; } #endif void main() { systemInit(); usbInit(); analogInputsInit(); initAdc(); initDma(); // Initialize common code initAdc2Dma(32); initDSM(); enablePort0Trigger(); // initTimer1CapMode(); while(1) { boardService(); updateLeds(); usbComService(); sendReportIfNeeded(); } }