Lenny, I'm sorry for this very late answer, but I found this topic really useful and you guys have given me the right directions. I got to check all the information you gave here in the SPRUH73 guide (technical ashsssfyiafgygrf) and definitely, if one uses the default 3MHz ADC clock or the CLK_M_OSC 24MHz clock, that wouldn't perfectly sync with the PRUs as 200MHz is not a multiple of these values. However, if I understood correctly, the PRCM acts like a prescaler, so maybe if you set PRCM = multiple of 3, do the clocks sync then? I know, maybe you REALLY need the whole 24MHz... But in case your application accepts 8MHz, there's a chance of syncing them.
What do you think? Em segunda-feira, 22 de dezembro de 2014 22:59:10 UTC-2, Lenny escreveu: > > Hi TJF and luciano, > > below you can find the assembler code which I used. The critical > configuration step is this one > //ADC_CLKDIV > MOV ADDR,ADC_TSC > MOV VALUE,0x00000000 //the 24MHz-clock rate is divided by VALUE+1 to > yield ADC_CLOCK > SBBO VALUE,ADDR,0x4C,4 //important to write all 4 bytes even though > only the first 2 count - if this is not done, the change doesnt take effect > for some reason > > For some reason, I struggled for some time believing that the ADC_CLKDIV > register was set to zero. Finally, all read and write operations went wrong > if I only did a 1 or 2 Byte read/write operation to the register. It only > worked properly when I read/wrote a full 4 Bytes to the register. > > The main loop of my program arms a single ADC acquisition per cycle, waits > for some time, and then retrieves a single value from the FIFO. There are > two sources of timing jitter: > 1) The read operation from the FIFO takes on my BBB something between 42 > and 44 PRU clock cycles. This is as far as I observed not fully > deterministic (some memory bus synchronization issue i guess). The solution > for this which I used is to have PRU0 running just as a timer (and > performing some well-timed calculations in the meantime), and let the PRU1, > after it has retrieved a new ADC value, stall until PRU0 has finished its > well-defined number of clock cycles and launch a new acquisition only after > the PRU0-PRU1 synchronisation has taken place. If one is careful about the > number of clock cycles per loop and the moments when one arms and reads out > the ADC, then this method only costs a few (<10) clock cycles but improves > control over the sampling time a lot. > > 2) As far as I observed, the ADC clock is not synchronized with the PRU > clock. So if the loop frequency is not a fraction of 24 MHz, there is a > "beating" between the two processes. If the loop frequency is a fraction of > 24 MHz, then there is from time to time a "glitch" between the two clocks. > I used the code below for a PID controller. When I set it to pure > proportional response, I observe random jumps in the group delay by > 1/24MHz. For my application this is not a problem. However, after knowing > this, if I would do this again, I would use an external ADC triggered by > the PRU for exactly this reason. If you should happen to find a way to > internally synchronize the ADC and PRU clocks, I would be really grateful > for a comment. > > With all this working, I can read data from a single ADC channel and > output this on the r30 register pins (hooked to a parallel DAC) at 1.4 MHz > rate (im not using the full 1.6 MHz because of the random glitches in point > 2) with only a few degrees phase jitter, again due to point 2). If the > synchronization with two PRUs interests you, you can also check out the > full code for both PRU's in the attached zip file. > > //prucode.hp > #define ADC_TSC 0x44E0D000 > #define FIFO0COUNT 0x44E0D0E4 > #define FIFO0DATA 0x44E0D100 > > //Register usage for PRU1 > > #define CALLREG r0.w2 > #define ENABLESTEPBUFFER r0.b0 > #define DIVISORSHIFT r0.b1 > > //Registers holding constants > #define ADC_ADDR r1 > #define OUTMASK r2 > > //Temporary values - used only for setup routines > #define ADDR r3 > #define VALUE r4 > > #define X0 r4 > > .macro PAUSE > QBNE ENDPROGRAM,r0,r0 > .endm > > .macro PAUSE10 > PAUSE > PAUSE > PAUSE > PAUSE > PAUSE > > PAUSE > PAUSE > PAUSE > PAUSE > PAUSE > .endm > > .macro PAUSE100 > PAUSE10 > PAUSE10 > PAUSE10 > PAUSE10 > PAUSE10 > > PAUSE10 > PAUSE10 > PAUSE10 > PAUSE10 > PAUSE10 > .endm > > //**********FIFO ROUTINES**************// > //attention: all offsets (3rd argument) are reduced by one, as ADC_ADDR > was increased by one before > > //Read a value from the FIFO0 buffer of the ADC > .macro READFIFO > .mparam dst > LBBO dst,ADC_ADDR,0xFF,4 > .endm > > //Read a how many values are stored in the FIFO0 register of the ADC > .macro READFIFOCNT > .mparam dst > LBBO dst,ADC_ADDR,0xE3,2 > .endm > > //Read the status register of the ADC IRQ > .macro READFIFOIRQ > .mparam dst > LBBO dst,ADC_ADDR,0x23,2 > .endm > > //Clear the stats register of the ADC IRQ > .macro CLEARFIFOIRQ > .mparam dst > MOV dst,0x0FFF > SBBO dst,ADC_ADDR,0x27,2 > READFIFOIRQ dst > .endm > > //Trigger a new ADC single-shot measurement > .macro ENABLESTEP1 > //STEPENABLE: > //enable only STEP1 a.k.a. bit 1 > MOV ENABLESTEPBUFFER,0x02 > SBBO ENABLESTEPBUFFER,ADC_ADDR,0x53,1 > .endm > > //********* End of FIFO routines *******// > > > Next file with executable code > > > // prucode1.p > // Code for PRU1 > > .setcallreg r0.w2 > .origin 0 > .entrypoint START > > //This is the first instruction that will be executed. For convenience, > the START section appears only after several definitions > FIRSTINSTRUCTION: > JMP START > > //Many definitions: Register names, Constants, Macros etc. > #include "prucode.hp" > > /////////////////////CALL routines//////////// > > //empty FIFO0 buffer so that zero values are in the 'pipeline' > EMPTYFIFOBUFFER: > //get IRQ status before emptying > READFIFOIRQ VALUE1.w0 > > EMPTYFIFOBUFFERLOOP: > //read a value > READFIFO VALUE0.w0 > //check buffer count > READFIFOCNT VALUE0.w2 > //read more values until the buffer is empty > QBNE EMPTYFIFOBUFFER,VALUE.w2,0x0000 > > //clear and read out the status of the buffer > CLEARFIFOIRQ VALUE1.w2 > RET > > //Init the whole program > INIT: > //global config settings > LBCO VALUE, CONST_PRUCFG, 4, 4 > CLR VALUE, VALUE, 4 // Clear SYSCFG[STANDBY_INIT] to enable > OCP master port > CLR VALUE, VALUE, 3 // set no-standby mode > SET VALUE, VALUE, 2 // set no-standby mode > CLR VALUE, VALUE, 1 // enable no-idle mode > SET VALUE, VALUE, 0 // enable no-idle mode > SBCO VALUE, CONST_PRUCFG, 4, 4 > > // Configure the programmable pointer registers > //Configs for PRU1 at offset 0x000024000, for PRU0 at offset 0x00022000 > MOV ADDR, 0x00024000 > //CONST_PRU01DRAM (C24) and CONST_PRU10DRAM (C25) offset (bits 11-8) > config with bits 0-7 resp. 16-23 > MOV VALUE, 0x00000000 > SBBO VALUE,ADDR,0x20,4 > //CONST_PRUSHAREDRAM (C28) bits 23-8 are given by bits 15:0 of VALUE > MOV VALUE, 0x00010000>>8 > SBBO VALUE,ADDR,0x28,4 > //CONST_DDR (C31) bits 23-8 are given by bits 31:16 of VALUE > MOV VALUE, 0x00000000 > SBBO VALUE,ADDR,0x2C,4 > > // Initialize Pru Ram to zero > MOV VALUE,0x00000000 > FILLDRAM VALUE > FILLSHAREDRAM VALUE > > RET > > SETUPADC: > // SETUP ADDRESS REGISTERS > MOV ADC_ADDR,ADC_TSC > > //because Immediate value addressing only goes up to 255 and > //because we need an offset of 256 for accessing the FIFO, increase > the base address by 1 > //this means, that all other offsets need to be reduced by 1 in the > following w.r.t. their offsets from the TRM > ADD ADC_ADDR,ADC_ADDR,1 > > //disable TSC_ADC_SS module,dont write channel id into FIFO > //and disprotect step config registers > MOV ADDR,ADC_TSC > MOV VALUE,0x00000004 //as above said already: disprotect and > disable... > SBBO VALUE,ADDR,0x40,4 //write value to CTRL register > > //ADC_CLKDIV > MOV ADDR,ADC_TSC > MOV VALUE,0x00000000 //the 24MHz-clock rate is divided by VALUE+1 to > yield ADC_CLOCK > SBBO VALUE,ADDR,0x4C,4 //important to write all 4 bytes even though > only the first 2 count - if this is not done, the change doesnt take effect > for some reason > > //TS_CHARGE_STEPCONFIG > MOV ADDR,ADC_TSC > MOV VALUE,0x00000000 //no unnecessary delay, do nothing here > SBBO VALUE,ADDR,0x5C,4 > > //TS_CHARGE_DELAY > MOV ADDR,ADC_TSC > MOV VALUE,0x00000001 //value must be greater than 0 > SBBO VALUE,ADDR,0x60,4 > > //STEPCONFIG1: SW continuous and no averages > //bits 22-19: AINx; bit1-0 = 01 for continuous > //bits 4-2 averaging: 100=16avg,000no avg > MOV ADDR,ADC_TSC > //MOV VALUE,0x00080001 //continuous mode > MOV VALUE,0x00080000 //sw one-shot mode > SBBO VALUE,ADDR,0x64,4 > > //STEPDELAY1 - add highest two bytes any value if the sampling need to > be slowed down > //integrate signal at analog input over 1 analog sample cycles > MOV ADDR,ADC_TSC > MOV VALUE,0x00000000 //bits 31-24: SampleDelay; bits 17-0: OpenDelay > SBBO VALUE,ADDR,0x68,4 //STEPDELAY1 > > //for future implementations of averaging etc., we also configure > steps 2 and 3 although they are never used here > > //STEPCONFIG2: SW continuous and no averages > //bits 22-19: AINx; bit1-0 = 01 for continuous > //bits 4-2 averaging: 100=16avg,000no avg > MOV ADDR,ADC_TSC > //MOV VALUE,0x00080001 //continuous mode > MOV VALUE,0x00080000 //one-shot mode > SBBO VALUE,ADDR,0x6C,4 > > //STEPDELAY2 > //integrate signal at analog input over 1 analog sample cycles > MOV ADDR,ADC_TSC > MOV VALUE,0x00000000 //bits 31-24: SampleDelay; bits 17-0: OpenDelay > SBBO VALUE,ADDR,0x70,4 //STEPDELAY2 > > //STEPCONFIG3: SW continuous and no averages > //bits 22-19: AINx; bit1-0 = 01 for continuous > //bits 4-2 averaging: 100=16avg,000no avg > MOV ADDR,ADC_TSC > MOV VALUE,0x00080001 > SBBO VALUE,ADDR,0x74,4 > > //STEPDELAY3 > //integrate signal at analog input over 1 analog sample cycles > MOV ADDR,ADC_TSC > MOV VALUE,0x00000000 //bits 31-24: SampleDelay; bits 17-0: OpenDelay > SBBO VALUE,ADDR,0x78,4 //STEPDELAY2 > > //STEPENABLE: > //enable only STEP1 a.k.a. bit 1 > //here: disable all steps for now > MOV ADDR,ADC_TSC > MOV VALUE,0x00000000 > SBBO VALUE,ADDR,0x54,4 > > //FIFO0THRESHOLD > MOV ADDR,ADC_TSC > MOV VALUE,0x000000FF //bits 5:0 = Value-1 for FIFO Threshold > interrupt generation > SBBO VALUE,ADDR,0xE8,4 > > //SYSCONFIG > MOV ADDR,ADC_TSC > MOV VALUE,0x00000000 //bits 3:2 00=force idle, 01=no idle, 10=smart > idle, 11=smart idle+wakeup > SBBO VALUE,ADDR,0x10,4 > > //enable TSC_ADC_SS module,dont write channel id > //and write-protect step config registers > MOV ADDR,ADC_TSC > MOV VALUE,0x00000001 > SBBO VALUE,ADDR,0x40,4 > > RET > > START: > //Zero all registers to avoid ambiguity > ZERO &r0,124 > //Initialise memory and registers > CALL INIT > > //Setup ADC > CALL SETUPADC > > //empty FIFO0 buffer in case it is not clean > CALL EMPTYFIFOBUFFER > //re-zero the buffer variables used by EMPTYFIFOBUFFER > MOV VALUE0,0x00000000 > MOV VALUE1,0x00000000 > > MAINLOOP: > //ask for a new value from the ADC, whose preparation takes about 190 > clock cycles > //(should be only 125 cycles (625ns) and once this worked, but > recently there are problems. Maybe due to readout latency from FIFO?) > ENABLESTEP1 > > //wait the required amount of time, and a bit more to make sure we > dont read a value when there is none > PAUSE100 > PAUSE100 > > //do the same as in PID-Loop without sending Enablestep > //retrieve sampled analog data > READFIFO X0 > //now we have the last adc sampling value (from AIN1) in the register > X0 and can write it to DDR memory or do whatever else we want > //........ > > JMP MAINLOOP > > -- For more options, visit http://beagleboard.org/discuss --- You received this message because you are subscribed to the Google Groups "BeagleBoard" group. To unsubscribe from this group and stop receiving emails from it, send an email to beagleboard+unsubscr...@googlegroups.com. For more options, visit https://groups.google.com/d/optout.