This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

Using ADC088S022 with uDMA SPI Transfer on Tm4C129

Other Parts Discussed in Thread: ADC088S022

I am attempting to use the uDMA SPI on the TM4C129 in 8-bit ping pong mode to continually acquire data from an external ADC (the TI ADC088S022). This ADC is to go on the tip of a set of electrodes with a low noise amplifier design to minimize noise. The TM4C129 will go on a separate PCB. I have set up the TM4C129 to sucessfully communicate via uDMA with another board and am now attempting to get it to communicate with the ADC. The problem I am having is in the timing. The TM4C129 is set up for 8-bit transfer using SPI uDMA ping pong mode. The board uses an interrupt on Timer 0 to send the address of the ADC channel required to the ADC088S022. This is all set up for a transfer at 50KSPS, ie a 0.8 MHz clock and an interrupt that triggers every 20us. The ADC is setup with a 5V supply. Upon reading the data, I can see that when a 3.3V signal is sent to one of the channels, the value received and stored is 10. When a 5V signal is sent to one of the channels, the value received is 15. I believe this is because of a timing issue. According to the datasheet, the first 4 bits received from the ADC are 0, the 8 data bits are then sent followed by another 4 zeros. This may be the reason why for 5V, although I should be seeing a value of 11111111, I am only seeing a value of 00001111. And for 3.3V, why I am seeing a value of 00001010 instead of 10101010. Can anyone suggest to me how I can get around this issue, I am relatively new to using uDMA and have never used this ADC before. Here is the link to the ADC datasheet for reference.

  • Hello Elliott

    Can you please share the configuration of SPI on TM4C129?

    Regards
    Amit
  • Yes, so I have been configuring the module using the registries for efficiency, the initilisation function is shown below:

    /******************************************************************************************
    Initialisation Function for SPI.
    *******************************************************************************************/
    void SPI_Init(void){
    	
    	SYSCTL->RCGCSSI  |= 0X01;     								                  // ACTIVATE SSI0
    	SYSCTL->RCGCGPIO |= 0x01; 										                  // ACTIVATE PORT A
      while((SYSCTL->PRGPIO&0x0001) == 0){};				                  // ready?
      GPIOA_AHB->AFSEL |= 0x2C;           					                  // ENABLE ALT FUNCTION ON PA2,3,5
      GPIOA_AHB->DEN   |= 0x2C;             				                  // ENABLE I/O ON PA2,3,5
    		
    	/* CONFIGURE PA2,3,5 AS SSI0*/
      GPIOA_AHB->PCTL = (GPIOA_AHB->PCTL&0xFF0F00FF)+0x00F0FF00;
    		
      GPIOA_AHB->AMSEL = 0;               					                 // DISABLE ANALOG FUNCTIONS
      SSI0->CR1        &=~ (1UL<<1);           			                 // DISABLE SSI
      SSI0->CR1        &=~ (1UL<<2);            		                 // SET AS MASTER MODE
    		
      /* CLOCK DIVIDER FOR 1 MHz SSIClk ASSUMING 120 MHZ CLOCK*/
      SSI0->CPSR = (SSI0->CPSR&~0x000000FF )+150;
      SSI0->CR0 &= ~(0x0000FF00  |       						                 // SCR = 0 
                     0x00000080  |         					                 // SPH = 0
                     0x00000040);         					                 // SPO = 0
    																								
      SSI0->CR0 = (SSI0->CR0&~0x30)+0;                               // FRF = FREESCALE
    																								
      SSI0->CR0 = (SSI0->CR0&~0xF)+0x7;                              // DSS = 8-bit
      SSI0->DR = 0x2;                    			                       // DUMMY DATA LOADED HERE	
    }

    I also have the following function to initialize uDMA on the SPI RX FIFO:

    /******************************************************************************************
    Initialisation Function for SPI UDMA.
    *******************************************************************************************/
    void DMA_SPI_Init (void){
    	
    	int i;                                        //used to clear control table at start
    	volatile uint32_t delay;                      //delay
    	
      for(i=0; i<256; i++){
        ucControlTable[i] = 0;                      //clear values in control table.
      }
    	
      SYSCTL->RCGCDMA = 0x01;                       // µDMA CLOCK
      delay = SYSCTL->RCGCDMA;                     
      UDMA->CFG = 0x01;                             // ENABLE MASTER
      UDMA->CTLBASE = (uint32_t)ucControlTable;     //SET BASE AS CONTROL TABLE
      UDMA->CHMAP1 = (UDMA->CHMAP1&0xFFFFF0FF)
    									|0x00000000;                  //SET FOR SSI0 RX
      UDMA->PRIOCLR = (1UL<<10);                    //DEFAULT PRIORITY
      UDMA->ALTCLR = (1UL<<10);                     //USE PRIMARY CONTROL
      UDMA->USEBURSTCLR = (1UL<<10);                //BURST AND SINGLE REQUESTS
      UDMA->REQMASKCLR = (1UL<<10);                 //REQUEST FROM CHANNEL 10 ALLOWED
    }

    And the following functions to configure the Ping-Pong mode:

    /******************************************************************************************
    SSI0 Interrupt Config Function 
    *******************************************************************************************/
    void SPI_Interrupt_Enable(void){
    	
    	SSI0->IM |=(1UL<<4);                          //use SSI0 receive interrupt 
    	SSI0->DMACTL |=(1UL<<0);                      //enable SSI0 receive fifo dma
    	
    	SSI0->CR1 |= 0X2;            									//enable SSI0
    	NVIC_EnableIRQ(SSI0_IRQn);                    //enable NVIC for GPIO Port D
    
    }
    
    /******************************************************************************************
    Reset primary control structure
    *******************************************************************************************/
    void static setReg(void){
      ucControlTable[CH10PR]   = (uint32_t)SourcePt;           // 
      ucControlTable[CH10PR+1] = (uint32_t)DestinationPt;      // 
      ucControlTable[CH10PR+2] = 0x0C0103F3;                   //
    /* DMACHCTL          Bits    Value Description
       DSTINC            31:30   00    8-bit source address increment
       DSTSIZE           29:28   00    8-bit destination data size
       SRCINC            27:26   11    no destination address increment
       SRCSIZE           25:24   00    8-bit source data size
       reserved          23:18   0     Reserved  
       ARBSIZE           17:14   100   Arbitrates after 8 transfer
       XFERSIZE          13:4  count-1 Transfer count items
       NXTUSEBURST       3       0     N/A for this transfer type
       XFERMODE          2:0     011   Use ping-pong transfer mode
      */
    }
    
    /******************************************************************************************
    Reset alternate control structure
    *******************************************************************************************/
    void static setAlt(void){
      ucControlTable[CH10ALT]   = (uint32_t)SourcePt;               //
      ucControlTable[CH10ALT+1] = (uint32_t)DestinationPt2;         // 
      ucControlTable[CH10ALT+2] = 0x0c0103F3;                       // 
    
    	/* DMACHCTL          Bits    Value Description
       DSTINC            31:30   00    8-bit source address increment
       DSTSIZE           29:28   00    8-bit destination data size
       SRCINC            27:26   11    no destination address increment
       SRCSIZE           25:24   00    8-bit source data size
       reserved          23:18   0     Reserved  
       ARBSIZE           17:14   100   Arbitrates after 8 transfer
       XFERSIZE          13:4  count-1 Transfer count items
       NXTUSEBURST       3       0     N/A for this transfer type
       XFERMODE          2:0     011   Use ping-pong transfer mode
      */
    }
    
    // Outputs: none
    // This routine does not wait for completion, runs continuously
    void DMA_Start(volatile uint8_t *source, volatile uint8_t *source2){
      DestinationPt = source+63;  // last address of source buffer
    	DestinationPt2 =source2+63;
      SourcePt = SSI0_DR ;
      Count = 64;  
      setReg();  
      setAlt();  
    	SPI_Interrupt_Enable();
      UDMA->ENASET |= (1UL<<10);  // CHANNEL 10 ENABLED
    }
    
    void get_data (){
    	SSI0->DR = 0x00;
    }

    The SSI0 interrupt handler is as follows:

    void SSI0_Handler (void){ 
     SSI0->ICR = (1UL<<4);                          //clear the interrupt flags for next interrupt event
     NumberOfBuffersSent++;
      if((ucControlTable[CH10PR+2]&0x0007)==0){     // regular buffer complete
        setReg();                                   // rebuild channel control structure
    		histogram (buffer);
    		print_data(buffer);
    
      }
      if((ucControlTable[CH10ALT+2]&0x0007)==0){  // Alternate buffer complete
        setAlt();      		                        // rebuild channel control structure
    	
       histogram (buffer2);
    	 print_data(buffer);
     }	
    }

    I also have a Timer0A interrupt handler to send the address to the ADC:

    void TIMER0A_Handler(void){
      TIMER0->ICR |= (1UL<<0);// acknowledge timer0A timeout
      get_data();
    }

  • Just a quick update, I've changed all the configuration to use 16 bit mode and as expected, the values received are for 5V , 111111110000, and for 3.3V, 101010110000. I could use this and convert it to 8 bit numbers, but my application requires very efficient code, hence the using of the udma etc in the first place. Do you have any ideas?

    Thanks,

    Elliott
  • Elliott Smith said:
    I have been configuring the module using the registries for efficiency

    Why do you think that - and for whom does direct register (ever) prove efficient?

    Use of direct register - as opposed to vendor's long welcome and exhaustive APIs - makes little sense.  And one doubts that it's efficient - or that "efficiency" is required for set-up/config of a peripheral.

    Direct register forces great extra effort - upon yourself - and your hapless forum helpers.  Every single bit w/in key registers must be properly configured - and sometimes the sequence of register writes is of importance.

    API's manage all of the register "heavy lifting" for you - shield you from obscure errors - and have withstood the, "test of time."   Your direct register code offers, "None of the above" - hard to agree that it's - in any way - "efficient."

  • Hello Elliott,

    Reading DRM code is difficult as all logical operations need to be manually done to ensure correctness. However (if my Boolean arithmetic is right) the ADC requires that the CS be held Low during the transaction which is 16-bits long. However in the case mentioned after every DSS of 8-bits worth of transfer the CS will be de-asserted causing the transaction to fail

    Regards,
    Amit