Reply
Posts: 4
Registered: ‎09-05-2017

ADC PWM Unsynchronized

I am using this script to turn on the ADC then start a PWM signal to other hardware that is generating data back to the ADC. The start of the samples (square waves from a pulse) to the ADC occurs at a different position in the ADC samples every time.

Is there a way to fix the timers to be consistent, or can I reset the timers before the loop is triggered by the GPIO, so that the signal I'm measuring is consistent every time I use the script?

This is on a Silicon Labs C8051F06x.

 

This is on a Silicon Labs C8051F06x.

    //-----------------------------------------------------------------------------
    // Includes
    //-----------------------------------------------------------------------------
    
    #include <c8051f060.h>                 // SFR declarations
    #include <stdio.h>
    #include <string.h>
    //-----------------------------------------------------------------------------
    // 16-bit SFR Definitions for 'F06x'
    //-----------------------------------------------------------------------------
    
    sfr16 RCAP3		= 0xCA;					// Timer3 Reload value
    sfr16 TMR3		= 0xCC;					// Timer3 Counter
    
    sfr16 ADC0		= 0xBE;					// ADC0 data
    sfr16 ADC1		= 0xBE;					// ADC1 data
    
    sfr16 DMA0DS	= 0xDB;					// DMA0 XRAM Address Pointer
    sfr16 DMA0CT	= 0xF9;					// DMA0 Repeat Counter Limit
    sfr16 DMA0DA 	= 0xD9;					// DMA0 Address Beginning
    sfr16 DMA0CS	= 0xFB;					// DMA0 Repeat Counter
    
    
    //-----------------------------------------------------------------------------
    // Global Constants
    //-----------------------------------------------------------------------------
    
    #define SYSCLK      24500000          // Internal oscillator frequency in Hz
    //#define SYSCLK		22118400			// SYSCLK frequency in Hz
    #define T0_CLOCKS 	10						// 8 clocks timer 0 overflow
    #define BAUDRATE 	115200				// Baud Rate for UART0
    
    #define DMA0_END_OF_OP	0x00			// End of operation
    #define DMA0_END_OF_OP_C 0x80			// End of operation + Continue
    #define DMA0_GET_ADC0	0x10			// Retrieve ADC0 data
    #define DMA0_GET_ADC1	0x20			// Retrieve ADC1 data
    #define DMA0_GET_ADC01  0x30			// Retrieve ADC0 and ADC1 data
    #define DMA0_GET_DIFF	0x40			// Retrieve Differential Data
    #define DMA0_GET_DIFF1	0x60			// Retrieve Differential and ADC1 data
    
    #define NUM_SAMPLES		1536			// Number of ADC samples to acquire (each sample 2 bytes)
    //#define NUM_SAMPLES	32768
    #define XRAM_START_ADD	0x0000			// DMA0 XRAM Start address of ADC Data log 
    #define SAMP_RATE		1000000			// ADC sample rate in Hz
    
    sbit LED = P0^7;						// LED: '1' = ON; '0' = OFF
    //sbit BUTTON = P3^7;					// pushbutton on the target board
    sbit BB_GPIO_INPUT = P3^1;				// pushbutton converted to gpio input for bb control -- stephen copeland
    sbit BB_GPIO_INPUT2 = P3^3;				//BBB P9_24 to trigget UART dump
    sbit RAM_CS = P4^5;						// chip select bit is P4^4
    sbit SW1     = P3^5;                   // ACQUIRE SW1 ='0' means switch pressed.
    sbit SW2	 = P3^6;				   // NEXT
    
    
    //-----------------------------------------------------------------------------
    // Function Prototypes
    //-----------------------------------------------------------------------------
    
    //void OSCILLATOR_Init (void);
    void SYSCLK_Init (void);
    void OSCILLATOR_Init (void);
    void UART0_Init (void);
    void PORT_Init (void);
    void ADC0_Init (void);
    void ADC1_Init (void);
    void DMA0_Init (void);
    void Timer3_Init (int counts);
    void EMIF_Init (void);
    void PCA0_Init (void);
    void PCA_ISR (void);
    void SendData (void);
    void Wait (void);
      
    //Code to acquire from ADC 
            void main (void)
        {
          // unsigned char delay_count;          // Used to implement a delay
          // bit duty_direction = 0;             // 0 = Decrease; 1 = Increase
        
           // Disable watchdog timer
           WDTCN = 0xde;
           WDTCN = 0xad;
        
           PORT_Init ();                       // Initialize crossbar and GPIO
         
        	OSCILLATOR_Init ();
           PCA0_Init ();                       // Initialize PCA0
           EMIF_Init ();						// Storing ADC samples in SRAM 
        
           SFRPAGE = CONFIG_PAGE;
        
           RAM_CS = 0;							// assert SRAM chip select
        
           UART0_Init ();						// initialize UART0
        
           Timer3_Init (SYSCLK/SAMP_RATE); 		// Init Timer3 for 100 ksps sample rate
        
           ADC0_Init ();						// configure ADC0 and ADC1 for differential measurement
        
           EA = 1;                             // Globally enable interrupts
        
        
        	while(1)
         	{							// loop this logic for multiple samples
        
        	   while(BB_GPIO_INPUT == 0);
        
        	   SFRPAGE = TIMER01_PAGE;
        	   TR0 = 1;
        	   DMA0_Init ();						// configure DMA to move NUM_SAMP samples
        
        	   SFRPAGE = UART0_PAGE;
        	   //printf ("Data Acquisition in progress...\n");
        
        	   SFRPAGE = DMA0_PAGE;					// Switch to DMA0 Page
        
        	   while (!(DMA0CN & 0x40));			// Wait for DMA to obtain and move ADC samples
        
        	   SFRPAGE = LEGACY_PAGE;
        	   //printf ("Data Acquisition complete.\n");
        	   SFRPAGE = TIMER01_PAGE;
        	   TR0 = 0;
           	   while(BB_GPIO_INPUT == 0);
        	   SendData();							// Send data via the UART0
        
        	}
        }

    
        //Code to print the data from DMA   
         void SendData (void)
            {
            	unsigned int i;
            	char old_SFRPAGE = SFRPAGE;
            	SFRPAGE = UART0_PAGE;
            
            	read_ptr = XRAM_START_ADD;   //pointer at beginning of data
            
            	for (i=0; i<NUM_SAMPLES; i++)
            	{
            		printf("%u",*read_ptr);		// send data as unsigned integers
            		putchar(',');
            		read_ptr++;
            	}
            	printf("\n");
            
            	SFRPAGE = old_SFRPAGE;
            }
    //-----------------------------------------------------------------------------
    // ADC0_Init
    //-----------------------------------------------------------------------------
    void ADC0_Init (void)
    {
    	char old_SFRPAGE = SFRPAGE;
    	int i;
    
    	SFRPAGE = ADC0_PAGE;			// Switch to ADC0 Page
    
    	ADC0CN = 0x84;					// ADC disabled, timer3 start-of-conversion 06-09-17
    									// track 16 SAR clocks before data conversion
    									// upon timer3 0V. DMA will enable ADC as neeeded
    	REF0CN = 0x03;					// turn on bias generator and internal reference.
    
    	for(i=0;i<30000;i++);			// WAit for Vref to settle
    	for(i=0;i<30000;i++);			// WAit for Vref to settle
    	for(i=0;i<30000;i++);			// WAit for Vref to settle
    	for(i=0;i<30000;i++);			// WAit for Vref to settle
    	for(i=0;i<30000;i++);			// WAit for Vref to settle
    	for(i=0;i<30000;i++);			// WAit for Vref to settle
    	for(i=0;i<30000;i++);			// WAit for Vref to settle
    	for(i=0;i<30000;i++);			// WAit for Vref to settle
    	for(i=0;i<30000;i++);			// WAit for Vref to settle
    	for(i=0;i<30000;i++);			// WAit for Vref to settle
    	for(i=0;i<30000;i++);			// WAit for Vref to settle
    
    	AMX0SL = 0x00;					// single-ended mode
    
    //	ADC0CF = (SYSCLK/25000000) << 4;	// elect SAR clock frequency =~ 25MHz
    
      ADC0CF =0;							// 06-09-17
    
    	SFRPAGE = old_SFRPAGE;
    }
    
    //-----------------------------------------------------------------------------
    // DMA0_Init
    //-----------------------------------------------------------------------------
    void DMA0_Init (void)
    {
    	char old_SFRPAGE = SFRPAGE;
    
    	SFRPAGE = DMA0_PAGE;			// switch to DMA0 page
    
    	DMA0CN = 0x00;					// disable DMA interface
    
    	DMA0DA = XRAM_START_ADD;		// starting point for XRAM addressing
    
    	DMA0CT = NUM_SAMPLES;			// get NUM_SAMPLES samples
    
    	DMA0IPT = 0x00;					// start writing at location 0
    
    	// push instructions onto stack in order of execution
    
    	DMA0IDT = DMA0_GET_ADC0;		// DMA to move ADC0 data
    	DMA0IDT = DMA0_END_OF_OP;
    
    	DMA0BND = 0x00;					// begin instruction executions at address 0
    	DMA0CN = 0xA0;					// mode 1 operations, begin executing DMA ops
    									// which will start ADC0
    
    	SFRPAGE = old_SFRPAGE;
    }
    
    //-----------------------------------------------------------------------------
    // Timer3_Init
    //-----------------------------------------------------------------------------
    //
    // Configure Timer3 to auto reload and generate ADC sample rate
    //	specified by <counts> using SYSCLK as its time base
    //
    void Timer3_Init (int counts)
    {
    	char old_SFRPAGE = SFRPAGE;
    
    	SFRPAGE = TMR3_PAGE;			// switch to timer 3 page
    
    	TMR3CN = 0x00;					// stop timer3; clear TF3;
    	TMR3CF = 0x08;					// Use SYSCLK as timer base;
    	RCAP3 = -counts;				// init reload values
    	TMR3 = 0xffff;					// set to reload immediately
    	TR3 = 1;						// start timer3
    
    	SFRPAGE = old_SFRPAGE;			
    }
    //-----------------------------------------------------------------------------
    // PCA0_Init
    //-----------------------------------------------------------------------------
    //
    // Return Value : None
    // Parameters   : None
    //
    // This function configures the PCA time base, and sets up 16-bit PWM output
    // mode for Module 0 (CEX0 pin).
    //
    // The frequency of the PWM signal generated at the CEX0 pin is equal to the
    // PCA main timebase frequency divided by 65536.
    //
    
    //
    //-----------------------------------------------------------------------------
    void PCA0_Init (void)
    {
       char SFRPAGE_save = SFRPAGE;        // Save current SFR Page
    	//int pulsecount;
      	TMOD &= 0xF0;					// clear all T0 control bits, disable Timer 0 Run
    	TMOD |= 0x02;					// 8-bit auto-reload timer
    
    	CKCON |= 0x08;					// T0 uses SYSCLK
    //	CKCON |= 0x02;					// T0 uses SYSCLK/48
    	TH0 = -T0_CLOCKS;				// set up reload value
    	TL0 = -T0_CLOCKS;				// set up initial value
    	
    	
       SFRPAGE = PCA0_PAGE;
    
       // Configure PCA time base; overflow interrupt disabled
       PCA0CN = 0x00;                      // Stop counter; clear all flags
       //PCA0MD = 0x08;                      // Use SYSCLK as time base
    
      // PCA0CPM0 = 0xCB;                    // Module 0 = 16-bit PWM mode and
                                           // enable Module 0 Match and Interrupt
                                           // Flags
    	PCA0MD = 0x04; 						// use Timer 0 as time base
    //	PCA0CPM0 = 0xCB;					// Module 0 = 16-bit PWM mode
    	PCA0CPM0 = 0x42;						// module 0 = 8-bit pwm mode
    						
    
    
       // Configure initial PWM duty cycle = 91%
     //  CEX0_Compare_Value = 65536 - (65536 * 0.91); //16 bit calculation
     //   PCA0CPL0 = (CEX0_Compare_Value & 0x00FF);
      // PCA0CPH0 = (CEX0_Compare_Value & 0xFF00)>>8;
    
      // 8 bit PWM calculation
    	PCA0CPH0 = 256 - (256 * 0.5);
    
    
    
       // Start PCA counter
       
       SFRPAGE = CONFIG_PAGE;
       EIE1 |= 0x08;                       // Enable PCA interrupts
    
    	while(SW1 != 1);
    
       SFRPAGE = PCA0_PAGE;
       CR = 1;
    	
    
       SFRPAGE = TIMER01_PAGE;
       TR0 = 0;
    	
    //	for(pulsecount = 0; pulsecount < 35000; pulsecount ++);
    //	CR = 0;
    
       SFRPAGE = SFRPAGE_save;
    }

 

Posts: 2,326
Registered: ‎10-14-2014

Re: ADC PWM Unsynchronized

I am not sure I understand the question correctly or not. do you mind to upload the timimg requirements in a picture?
it look you are trying to excite an external component with PWM, then the external component generate some voltage and you would measure the voltage with ADC. Now you need the ADC and PWM edge synchronized.
WeiguoLu
Posts: 4
Registered: ‎09-05-2017

Re: ADC PWM Unsynchronized

You've pretty much assessed it properly. I want the pwm to begin (and consequentially the response from the external component) at the same position in the ADC samples each time. Right now there is a shift, so the first response might start a few hundred samples in, then the next time it runs, it might shift closer to the start a hundred or so. Each time seems to shift, however, it seems that there's some type of pattern occurring in the shift. I think it's something to do with a timer not resetting

Posts: 2,326
Registered: ‎10-14-2014

Re: ADC PWM Unsynchronized

I am not sure if you need several PWM pulse to excite the external component for per ADC sample. If one PWM pulse would trigger a ADC sample, I am wondering if you could tied the PWM output to the CNVSTRx and use the PWM edge to start the ADC.

WeiguoLu
Posts: 4
Registered: ‎09-05-2017

Re: ADC PWM Unsynchronized

I only need the PWM pulse to start when the ADC starts but it can stop when the ADC stops too

Posts: 4
Registered: ‎09-05-2017

Re: ADC PWM Unsynchronized

I should mention I'm relatively new to this environment and chipset, so any detailed examples are greatly appreciated!