/*** InitWS2812(uint8_t * pPatternBuffer, uint32_t cbPatternBuffer, uint32_t fInvert) * * Parameters: * pPatternBuffer: A pointer to the pattern buffer for the DMA to use * The application allocates this and should be * CBWS2812PATBUF(__cDevices) bytes long. * * cbPatternBuffer: This should be CBWS2812PATBUF(__cDevices) or larger * * fInvert: Because the WS2812 does not have a VinH <= 3.3 when Vcc >= 4.7v * External hardware may be needed to level shift the 3.3v SDO output * to a higher Data in signal to the WS2812. This level shifter may * be a simple transistor tied to 5v - 7v. The transistor will invert * the SDO output signal and to maintain the correct signal polarity * to the WS2812 you would need to invert the signal coming out of SDO. * If fInvert is true, the SDO output signal will be inverted from what * the WS2812 would normally take. By default, the is "false". * * Return Values: * True if the core timer can be aquired and initialization succeeded. * * Description: * * Initialize the SPI to 20MHz which is a 50ns clock * This will enable .35us pattern resolution in the SPI * shift register. * * Also, initialize 2 DMA channels, one to shift out * the pattern buffer, the other to maintain zeros * for the restart / reset patteren (RES). * * ------------------------------------------------------------ */ uint32_t InitWS2812(uint8_t * pPatternBuffer, uint32_t cbPatternBuffer, uint32_t fInvert) { // Disable SPI and DMA SPI2CON = 0; DMACON = 0; // set up SPI2 SPI2CONbits.MSTEN = 1; // SPI in master mode SPI2CONbits.ENHBUF = 1; // enable 16 byte transfer buffer SPI2CONbits.STXISEL = 0b10; // trigger DMA event when the ENBUF is half empty // SPI2CONbits.DISSDI = 1; // Disable the SDI pin, allow it to be used for GPIO (not implemented on an MX6) SPI2STAT = 0; // clear status register SPI2BRG = (__PIC32_pbClk / (2 * 20000000)) - 1; // 20MHz, 50ns IEC1bits.SPI2RXIE = 0; // disable SPI interrupts IEC1bits.SPI2TXIE = 0; // disable SPI interrupts IEC1bits.SPI2EIE = 0; // disable SPI interrupts IFS1bits.SPI2RXIF = 0; // disable SPI interrupts IFS1bits.SPI2TXIF = 0; // disable SPI interrupts IFS1bits.SPI2EIF = 0; // disable SPI interrupts IEC1bits.DMA0IE = 0; IFS1bits.DMA0IF = 0; IEC1bits.DMA1IE = 0; IFS1bits.DMA1IF = 0; DMACONbits.ON = 1; // turn on the DMA controller IEC1CLR=0x00010000; // disable DMA channel 0 interrupts IFS1CLR=0x00010000; // clear existing DMA channel 0 interrupt flag // Set up DMA channel 0 DCH0CON = 0; // clear it DCH0CONbits.CHAED = 0; // do not allow events to be remembered when disabled DCH0CONbits.CHAEN = 0; // do not Allow continuous operation DCH0CONbits.CHPRI = 0b11; // highest priority DCH0ECON = 0; // clear it DCH0ECONbits.CHSIRQ = _SPI2_TX_IRQ; // SPI2TX 1/2 empty notification DCH0ECONbits.SIRQEN = 1; // enable IRQ transfer enables DCH0INT = 0; // do not trigger any events DCH0INTbits.CHBCIF = 0; // clear IF bit IPC9bits.DMA0IP = 7; // priority 7 IPC9bits.DMA1IS = 0; // sub priority 0 DCH0SSA = KVA_2_PA(pPatternBuffer); // source address of transfer DCH0SSIZ = cbPatternBuffer; // number of bytes in source DCH0DSA = KVA_2_PA(&SPI2BUF); // destination address is the SPI2 buffer DCH0DSIZ = 1; // 1 byte at the destination DCH0CSIZ = 1; // only transfer 1 byte per event // Set up DMA channel 1 DCH1CON = 0; // clear it DCH1CONbits.CHCHNS = 0; // allow chaining to the next higher priority DMA channel 0 DCH1CONbits.CHCHN = 1; // Turn on chaining DCH1CONbits.CHAED = 0; // do not allow events to be remembered when disabled DCH1CONbits.CHAEN = 1; // turn on continuous operation DCH1CONbits.CHPRI = 0b11; // highest priority DCH1ECON = 0; // clear it DCH1ECONbits.CHSIRQ = _SPI2_TX_IRQ; // SPI2TX 1/2 empty notification DCH1ECONbits.SIRQEN = 1; // enable IRQ transfer enables DCH1INT = 0; // do not trigger any events if(fInvert) { DCH1SSA = KVA_2_PA(&ones); // refresh cycle is inverted streaming 1s } else { DCH1SSA = KVA_2_PA(&zeros); // normal refresh cycle streaming 0s } DCH1SSIZ = 1; // number of bytes in source DCH1DSA = KVA_2_PA(&SPI2BUF); // destination address is the SPI2 buffer DCH1DSIZ = 1; // 1 byte at the destination DCH1CSIZ = 1; // only transfer 1 byte per event // initial time for the core serivce routine read_count(tWS2812LastRun); // attach the core service routine if(attachCoreTimerService(WS2812TimerService)) { // Enable DMA channel 1 and SPI 2 // we enable in the refresh cycle until a pattern // is loaded in the main sketch. fWS2812Updating = true; IFS1bits.DMA0IF = 0; IEC1bits.DMA0IE = 1; DCH1CONbits.CHEN = 1; // turn DMA channel 1 ON; just zero output SPI2CONbits.ON = 1; return(1); // success } // Things are not good, disable SPI and DMA SPI2CON = 0; DMACON = 0; // error out return(0); }
/*** convertWiFIREadcEC ** ** Parameters: ** channelNumber - The PIC32 analog channel number as in the PIC32 datasheet ** ** Return Value: ** The converted value for that channel ** ** Errors: ** If return value of zero and error may have occured ** ** Description: ** Coverts the analog signal to a digital value on the ** given pic32 analog channel number */ static int convertWiFIREadcEC(uint8_t channelNumber) { int i,k = 0; uint8_t vcn = channelNumber; int analogValue = 0; #define KVA_2_PA(v) (((uint32_t) (v)) & 0x1fffffff) static uint16_t __attribute__((coherent)) ovsampValue; // set the channel trigger for GSWTRG source triggering if(channelNumber == 43 || channelNumber == 44 || channelNumber >= 50) { return(0); } else if(channelNumber >= 45 ) { vcn = channelNumber - 45; AD1IMOD |= 1 << ((vcn * 2) + 16); // say use the alt; set SHxALT } AD1CON3bits.ADINSEL = vcn; // manually trigger the conversion AD1CON1bits.FILTRDLY = AD1CON2bits.SAMC + 5; // strictly not needed, but set the timing anyway // set up for 16x oversample AD1FLTR6 = 0; // clear oversampling AD1FLTR6bits.OVRSAM = 1; // say 16x oversampling AD1FLTR6bits.CHNLID = vcn; // the ANx channel // setup DMA IEC4bits.DMA7IE = 0; // disable DMA channel 4 interrupts IFS4bits.DMA7IF = 0; // clear existing DMA channel 4 interrupt flag // setup DMA channel x DMACONbits.ON = 1; // make sure the DMA is ON DCH7CON = 0; // clear DMA channel CON DCH7ECON = 0; // clear DMA ECON DCH7ECONbits.CABORT = 1; // reset the DMA channel while(DCH7CONbits.CHEN == 1); // make sure DMA is not enabled while(DCH7CONbits.CHBUSY == 1); // make sure DMA is not busy DCH7CONbits.CHPRI = 3; // use highest priority DCH7ECONbits.CHSIRQ = _ADC1_DF6_VECTOR; // say the ADC filter complete triggers the DMA DCH7ECONbits.SIRQEN = 1; // enable the IRQ event for triggering DCH7SSA = KVA_2_PA(&AD1FLTR6); // address of the source DCH7SSIZ = 2; // source size is 2 bytes DCH7CSIZ = 2; // cell size transfer DCH7DSA = KVA_2_PA(&ovsampValue); // destination address DCH7DSIZ = sizeof(ovsampValue); // destination size // must throw out first 16 samples // keep the 17th. We can not access any ADC registers // however we can look at the DMA to see when things complete for(i=0; i<17; i++) { do { DCH7ECONbits.CABORT = 1; // reset the DMA channel AD1FLTR6bits.AFEN = 0; // make sure oversampling is OFF while(DCH7CONbits.CHEN == 1); // wait for DMA to stop while(DCH7CONbits.CHBUSY == 1); // wait for DMA not to be busy DCH7INT = 0; // clear all interrupts DCH7CONbits.CHEN = 1; // enable the DMA channel AD1FLTR6bits.AFEN = 1; // enable oversampling AD1CON3bits.RQCONVRT = 1; // start conversion // we have noticed problems that the DMA channel is not always // triggered, so after a time out value, just try again. // fundamentally the problem is that AD1FLTR6bits.AFEN must // be reset for each oversample conversion, disable and reenabled. // we know a conversion is going to take 8000ns * 16, or 128 us // we don't want to check too often so DMA and ADC can work // but we don't want to wait too long and hold things up for(k=0; k<20; k++) // this is more than enough time for the conversion, { // really 8 should be good enough delayMicroseconds(16); // this will cause us to check about 8 times if(DCH7INTbits.CHBCIF == 1) { break; // DMA completed and copied the oversampled result } } } while(DCH7INTbits.CHBCIF == 0); // if the conversion did not finish, try again } analogValue = ovsampValue >> 2; // 16 oversample gets you 2 extra bits // we are done, clean up the DMA, oversampling filter, and ADC DCH7CON = 0; while(DCH7CONbits.CHBUSY == 1); AD1CON3bits.ADINSEL = 0; AD1FLTR6 = 0; if(channelNumber >= 45 ) { AD1IMOD &= ~(0b11 << ((vcn * 2) + 16)); // don't use alt } return(analogValue); }
/*** void flashOperation(uint32 nvmop, uint32 addr, uint32 data) ** ** Synopsis: ** Performs either a page erase, word write, or row write ** ** Parameters: ** nvmop either NVMOP_PAGE_ERASE, NVMOP_WORD_PGM, or NVMOP_ROW_PGM ** addr the uint32_t flash address of: the page to erase, word location to write, or row location to write ** data a uint32_t of data to write, or the uint32_t of the SRAM address containing the array of data to write to the row ** ** Return Values: ** True if successful, false if failed ** ** Errors: ** None ** ** Notes: ** data has no meaning when page erase is specified and should be set to 0ul ** */ uint32_t FlashOperation(uint32_t nvmop, uint32_t addr, uint32_t data) { unsigned long t0; unsigned int status; #if defined(_PCACHE) unsigned long K0; unsigned long PFEN = CHECON & _CHECON_PREFEN_MASK; #endif // Convert Address to Physical Address NVMADDR = KVA_2_PA(addr); NVMDATA = data; NVMSRCADDR = KVA_2_PA(data); // Suspend or Disable all Interrupts SuspendINT(status); #if defined(_PCACHE) // disable predictive prefetching, see errata CHECONCLR = _CHECON_PREFEN_MASK; // turn off caching, see errata ReadK0(K0); WriteK0((K0 & ~0x07) | K0_UNCACHED); #endif // Enable Flash Write/Erase Operations NVMCON = NVMCON_WREN | nvmop; // this is a poorly documented yet very important // required delay on newer silicon. // If you do not delay, on some silicon it will // completely latch up the flash to where you need // to cycle power, so wait for at least // 6us for LVD start-up, see errata t0 = _CP0_GET_COUNT(); while (_CP0_GET_COUNT() - t0 < ((6 * F_CPU) / 2 / 1000000UL)); // magic unlock sequence NVMKEY = 0xAA996655; NVMKEY = 0x556699AA; NVMCONSET = NVMCON_WR; // Wait for WR bit to clear while (NVMCON & NVMCON_WR); // see errata, wait 500ns before writing to any NVM register t0 = _CP0_GET_COUNT(); while (_CP0_GET_COUNT() - t0 < ((F_CPU / 2 / 2000000UL))); // Disable Flash Write/Erase operations NVMCONCLR = NVMCON_WREN; #if defined(_PCACHE) // restore predictive prefetching and caching, see errata WriteK0(K0); CHECONSET = PFEN; #endif // Restore Interrupts RestoreINT(status); // assert no errors return(! (NVMCON & (_NVMCON_WRERR_MASK | _NVMCON_LVDERR_MASK))); }