/** 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 #include // Not sure why the compiler was complaining even though cc2511map.h was // included. Had to include the following two lines #define T1CCTL1_IM 0x40 // Channel 1 Interrupt mask #define T1CTL_CH0IF 0x20 // Timer 1 channel 0 interrupt flag #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 128 #define DATA_AQU_TIME 250 // 50 msecs #define BIT0 0x01 #define BIT1 0x02 #define BIT2 0x04 // RX data which is not in dma.c DMA_CONFIG XDATA radioRxConfig; uint16 XDATA adcOutputX[MAX_BUF_SZ] = {0}; uint16 XDATA adcOutputY[MAX_BUF_SZ] = {0}; uint16 XDATA adcOutputZ[MAX_BUF_SZ] = {0}; /* PARAMETERS *****************************************************************/ int32 CODE param_input_mode = -1; int32 CODE param_bar_graph = 1; int32 CODE param_report_period_ms = 40; uint32 DATA 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; uint16 DATA volatile P0_interruptCount = 0; uint16 DATA volatile timer1Count = 0; /* FUNCTIONS ******************************************************************/ /******************************************************************************* * @fn initDma * * @brief This function clears all interrupt flags and disarms all DMA channels 1 to 4. * This function is written specifically for a different application * * @parm none * * @return void * * DMA is used by the following functions * buffers to radio * port 0_0 ADC * port 0_1 ADC * port 0_2 ADC * All DMA related registers should be initialized for the above four functions * */ void initDma(void) { // ARM channels 0, 1 and 2 DMAARM &= (DMA_CHANNEL_0 | DMA_CHANNEL_1 | DMA_CHANNEL_2); DMAARM |= (DMA_CHANNEL_0 | DMA_CHANNEL_1 | DMA_CHANNEL_2); DMAREQ &= (DMA_CHANNEL_0 | DMA_CHANNEL_1 | DMA_CHANNEL_2); DMAREQ |= (DMA_CHANNEL_0 | DMA_CHANNEL_1 | DMA_CHANNEL_2); DMAIE = 1; // Enable DMA Interrupt CX #if radio DMA0CFG = (uint16)&radioRxConfig; //Initialize the structure to Rx Pkts #endif DMA1CFG = (uint16)&dmaConfig; //Initialize all 4 channels as done in // original code // Clearing the interrupt flag of the DMA and enabling DMA interrupt. // INT_SETFLAG(INUM_DMA, INT_CLR); // INT_ENABLE(INUM_DMA, INT_ON); // DMA_SET_ADDR_DESC1234(&dmaConfig); Not required to do this as // with the current compiler, the above statements will take care of // high and low bytes } void initAdc(void) { #if 0 ADCCFG = 0x07; // Enable ADC input AIN0(P0_0), AIN1(P0_1), AIN2(P0_2) ADCCON2 = 0xB2; // Set reference voltage (AVDD pin), // decimation rate to 512 (12 bit resolution) and ADCCON3 = 0x00; // Disable extra conversion #endif } void startADCConversion(void) { #if 0 ADCCON1 = 0x33; // Set ADC start condition to '11' (ADCCON1.ST = 1) ADCCON1 |= 0b01110000; //Start ADC Conversion #endif } 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) { #if 0 P0SEL |= (BIT2 | BIT1 | BIT0); PERCFG = 0x00; // Alternate 1 Location // as per Wixel layout CX P0DIR &= ~(BIT2 | BIT1 | BIT0); // 0,1,2 is input #endif // As per Twoway design guide, select the following and use // In twoway, Alt 2 location was used. However, in our design, // we need to use Alt 1 location. // P1SEL |= 0x02; // Select Port 1_2 as output //PERCFG = 0x00; // Alternative 2 loc Port 1: 2 (0), 1 (1), 0 (2) // Bit 6 is Timer 1 I/O location: 0 --> Alt 1 // Since 0 is the reset value, we dont have to do // anything here. So comment it out... P1DIR = 0xFF; // Direction is output: In our case all // unused ports are output ports 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 CX //INT_ENABLE(INUM_T1, INT_ON); // Enable Timer 1 interrupt // T1CCTL1 |= T1CCTL1_IM; // Enable the interrupts in enableDSM. CX // 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 CX 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; // CX Enable interrupt after this function returns. T1CCTL1 |= 0x38; // Set to DSM Mode which will trigger DSM P0IE = 0; // Disable Port 0 IEN1 &= ~0x00100000; // Disable Port 0 IEN1 |= 0x00000011; // Disable Port 0 Enable Timer 1 and DMA interrupts T1IE = 1; // Enable Timer1 interrupt DMAIF = 1; EA = 1; // IEN0: Each int source is individually enabled/disabled __asm nop __endasm; __asm nop __endasm; __asm nop __endasm; } 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 T1IE = 0; // Disable Timer1 interrupt as well DMAIF = 0; IEN1 &= ~0x00100011;// Disable Port 0, Timer 1 and DMA Interrupts EA = 1; // But leave global interrupt enabled setting IEN0.EA=1 } void disablePort0Trigger(void) { P0IE = 0; // Disable P0 interrupt __asm nop __endasm; __asm nop __endasm; __asm nop __endasm; PICTL &= ~(1<<3); // PICTL.P0IENL = 0 Disable Port 0 interrupts for // inputs 0-3 BIT3 (USB_RESUME is #7). __asm nop __endasm; __asm nop __endasm; __asm nop __endasm; #if 0 // Either the following or individual bits.. // IRCON &= ~0b00100011; //clear port 0 flag and docs //say bit six must always be 0 //Refer to page 61 on clearing interrupts #endif P0IE = 0; IRCON &= ~0b00100000; IEN1 &= ~0x00100000; // Disable Port 0 & DMA , disable Timer 1 interrupts DMAIF = 0; } void enablePort0Trigger(void) { #if 0 PICTL = (PICTL & ~0b00000001); // Setup Rising edge PICTL |= 0b00001000; //Port 0, inputs 3 to 0 int enable, //and rising edge interrupt IEN1 = 0b00100000; //enable port 0 interrupts //EA = 1; #endif P0IE = 0; // Disable the P0 interrupt while we are reconfiguring it (maybe not necessary). __asm nop __endasm; __asm nop __endasm; __asm nop __endasm; PICTL &= ~(1<<0); // PICTL.P0ICON = 0 Detect rising edges // (this is required for waking up). PICTL |= (1<<3); // PICTL.P0IENL = 1 Enable the Port 0 interrupts // for inputs 0-3 (USB_RESUME is #7). P0IE = 1; // IEN1: Enable P0 int POIE is bit 5 (port 0 int enable) __asm nop __endasm; __asm nop __endasm; __asm nop __endasm; T1IE = 0; // Disable Timer1 interrupt as well IEN1 &= ~0x00000011; // disable Timer 1 and DMA interrupts IEN1 |= 0x00100001; // Enable Port 0 & DMA , disable Timer 1 interrupts DMAIF = 1; P0IE = 1; EA = 1; // IEN0: Each int source is individually enabled/disabled } volatile uint8 buffer; void readAdc(void) { uint8 i; switch(param_input_mode) { case -1: for (buffer = 0; buffer < MAX_BUF_SZ; buffer++) { for (i=0; i<3; i++) { if (!i) adcOutputX[buffer] = adcRead(i); if (i==1) adcOutputY[buffer] = adcRead(i); if (i==2) adcOutputZ[buffer] = adcRead(i); } } break; default: break; } } void analogInputsInit() { switch(param_input_mode) { case 1: initAdc(); // CX #if 0 initDma(); // CX initAdc2Dma(MAX_BUF_SZ); // CX initDSM(); // CX enablePort0Trigger(); #endif break; default: break; } } ISR(P0INT,0) { P0_interruptCount++; disablePort0Trigger(); LED_RED_TOGGLE(); P0IFG &= ~(BIT0 | BIT1 | BIT2); //clear only port 0_0, 0_1 and 0_2 flags // P0IE = 0; // IEN1: Disable P0 int POIE is bit 5 (port 0 int enable) __asm nop __endasm; __asm nop __endasm; __asm nop __endasm; // IRCON &= ~0b01100000; //clear port 0 flag and docs //say bit six must always be 0 //Refer to page 61 on clearing interrupts startADCConversion(); triggerTime = getMs(); // initDSM(); // Initialize DSM for about 50 msecs // starting with the port 0 trigger point delayMicroseconds(396); // Delay for all 3 times ADC conversion time // for all three axes // Enabling DSM mode will start DSM right away // // DSM mode should // be enabled rising or falling edge is detected?? enableDSM(); #if 0 #endif } ISR(T1,0) { timer1Count++; // 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); // Check if time period falls within 50 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) { startADCConversion(); } else { // Time has expired (more than DATA_AQU_TIME.. // Disable DSM and enable P0 interrupt trigger disableDSM(); //Port 0, inputs 3 to 0 interrupt enable, only for now //and rising edge interrupt enablePort0Trigger(); } if (timer1Count >= MAX_BUF_SZ) { for (timer1Count = 0; timer1Count < MAX_BUF_SZ; timer1Count++) { adcOutputX[timer1Count] = 0; adcOutputY[timer1Count] = 0; adcOutputZ[timer1Count] = 0; } timer1Count = 0; } #if 0 #endif } void setUnusedPortsAsOutputPorts() { P1SEL = 0x00; // 0 --> Gen purpose I/O P2SEL = 0x00; // 0 --> Gen purpose I/O // It appears that reset value is 0 so // nothing may have to be done here. P1DIR = 0xFF; // Set all ports as output P2DIR = 0x1F; // Set all ports as output // P1INP = 0x00; // Since Port 1 is set as o/p nothing needs to CX // done here as 0x00 is the reset value // P2INP = 0x00; // Pull up/down and not Tristate pg 86 CX // Same as above, nothing to be done here } // Initilizes I/O Ports void initPort0_0to3AsInput(void) { // P0 = 0xFF; // Reset the Port 0 CX // Clear interrupt flags for P0 // P0IFG &= ~(BIT0 | BIT1 | BIT2); // Interrupt status flag so dont write // P0IF = 0; // Corresponds to IRCON (CPU int reg) CX // 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); // Select peripheral 1 is peri 0 is gpio P0SEL &= 0x07; // Select rest as Gen purp O/P CX // No need to pull-up or pull-down when // configured as O/P // Here, we are setting rest of P0 ports // as GPIO, when selecting dir P0DIR // select it as o/p pins // Dont leave them undefined // refer to page 87 sec 12.4.2 P0DIR &= ~(BIT0 | BIT1 | BIT2); // Set P0_0, P0_1 and P0_2 to be input P0DIR |= 0xF8; // Set rest as output refer page 86 CX // P0INP &= ~0xF8; // Pull high/low unused ports (3-7) CX // No need to do this as after reset // the value is zero ??? // 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, 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; uint16 i, adcDataX, adcDataY, adcDataZ; #if 0 uint16 vddMillivolts; #endif // Create reports. if (getMs() - lastReport >= param_report_period_ms && reportLength == 0) { lastReport = getMs(); reportBytesSent = 0; LED_RED_TOGGLE(); #if 0 vddMillivolts = adcReadVddMillivolts(); adcSetMillivoltCalibration(vddMillivolts); #endif readAdc(); // This is for debug code. for(i = 0; i <= (param_input_mode == -1) ? buffer:timer1Count; i ++) { 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" putchar('\r'); putchar('\n'); printf("timer1Count %5d P0IntCount %5d\r\n", timer1Count, P0_interruptCount); printBar("adcX", adcDataX); printBar("adcY", adcDataY); printBar("adcZ", adcDataZ); #if 0 printf("VDD %4d mV\r\n", vddMillivolts); #endif } else { printf("%2d %4d, %4d, %4d\r\n", i, adcDataX, adcDataY, adcDataZ); } } } // 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; } } } void main() { systemInit(); /********** * Initialize ports P0_0, P0_1, P0_2 as input for ADXL377 **********/ initPort0_0to3AsInput(); setUnusedPortsAsOutputPorts(); usbInit(); analogInputsInit(); // This is for testing only CD #if 0 initAdc(); // Enable this after debugging CX initDma(); // Initialize common code (For now init in analogIn..) initDsm(); // Initialize DSM initAdc2Dma(MAX_BUF_SZ); enablePort0Trigger(); #endif // initTimer1CapMode(); while(1) { boardService(); updateLeds(); usbComService(); sendReportIfNeeded(); } }