Embedded Systems and Power Electronics

Total Pageviews

About Me

My photo
I am currently a PhD student at UC Berkeley, following a 6-year journey working at Apple after my undergrad years at Cornell University. I grew up in Dhaka, Bangladesh where my interest in electronics was cultivated, resulting in the creation of this blog.

BTemplates.com

Powered by Blogger.

Oct 30, 2014

Interfacing a color TFT display with the PIC32MX250F128B


I have been working on interfacing the PIC32MX250F128B with a small 2.2" TFT display from Adafruit. It's a nice little display that is fairly easy to communicate with, using SPI communication. The display I'm using is: http://www.adafruit.com/product/1480

Adafruit provides nice open-source libraries for their products. However, they are for Arduino and thus cannot be directly reused for the PIC32. I went through the library and ported it over for the PIC32, in C. I have attached my project file as a .zip file and you can download it to go through the library header and source files, as well as the demo code. I've tried heavily commenting the code so that it is self-explanatory.

As far as hardware goes, with the demo code, the pin connections for the display are:

BL (backlight): I left it unconnected, but you can connect it to 3.3V for backlight.
SCK: connected to RB14 on the PIC
MISO: left unconnected, since I'm not reading anything from the screen
MOSI: connected to RB11 on the PIC
CS: connected to RB1 on the PIC
SDCS: left unconnected as I'm not using the microSD card for this
RST: connected to RB2 on the PIC
D/C: connected to RB0 on the PIC
VIN: connected to 3.3V supply
GND: connected to gnd

The pins I used are defined in the code and you can easily change them as required.

I used my custom proto-board for testing on a breadboard. You can find details here: http://tahmidmc.blogspot.com/2014/02/pic32-proto-board-details-schematic-pcb.html


Here's a video showing the PIC32 running the demo code and doing some simple graphics on the screen. You can see that it runs fairly quickly and quite smoothly.


Project files with all source and header files:
https://drive.google.com/file/d/0B4SoPFPRNziHdksweU1vUkZ4SHM/view?usp=sharing
http://www.4shared.com/zip/O-LcEU0Ace/Adafruit_TFTX.html

If you have any comments, questions or suggestions, do let me know!

Oct 16, 2014

PIC32 DMA+SPI+DAC : Analog output synthesis with zero CPU overhead


I have previously shown how to use the PIC32 SPI module to use the 12-bit DAC MCP4822: http://tahmidmc.blogspot.com/2014/10/pic32-spi-using-mcp4822-12-bit-serial.html. While that does allow you to generate analog outputs as desired, it requires you to use CPU cycles to process the timer interrupt and accordingly drive the SPI module.

Since the PIC32 contains DMA channels, the process can be completely offloaded from the CPU. For an idea of the PIC32 DMA module, refer to my previously written article: http://tahmidmc.blogspot.com/2014/05/simple-pic32-dma-example.html

Fig. 1 - The generated sine wave at fpwm = 400kHz and 32 elements in the sine table

So, the simple way of offloading the SPI update to the DMA module would be to let the DMA channel transfer data to the SPI buffer. The SPI module is configured for 16-bit data transfer (since the MCP4822 write command requires 2 bytes). This means that the cell size for the DMA channel has been set to 2 (2 bytes to transfer once triggered). The DMA transfer is triggered by a timer interrupt. Unlike the previous example where I used Timer 1 and its interrupt for the SPI transfer, here I use Timer 2. The reason for this is that, if I want the entire process to be offloaded from the CPU, the SS (Slave Select) or CS (Chip Select) also has to be done entirely in hardware. For that I used the Output Compare 1 module. To use the OC1 module, either Timer 2 or Timer 3 (or the combined 32-bit timer) has to be used. So, I just went with Timer 2.

Configuring the DMA module for transferring data to the SPI buffer was the simple part. I found the configuration of the SS/CS pin the more challenging part. The idea here was to use the OC module in "dual compare mode continuous output pulses" mode. The OC module in this mode generates continuous pulses - the output pin OC1 - which I used as CS/SS - is set high one PBCLK (peripheral bus clock) after the Timer value reaches OC1R; the OC1 pin is cleared one PBCLK after the Timer value reaches OC1RS. Since SS/CS is active low, I set ( (period register) - 4) to be OC1RS and a variable CSlength to be OC1R. CSlength was chosen to be 80% of the period register. What this meant was that. One PBCLK after the Timer reached the (period register - 4), the OC1 pin went low (CS/SS went low) "selecting" the DAC. After about 0.80*(period register) from there, the OC1 pin went high. This means that the CS pin is low for 80% of the period - it goes low a small time right before the DMA transfer happens and is raised high late enough, after the DMA transfer occurs. I determined that 80% the period was enough time since that is higher than the time required to shift out 16 bits of data at 20MHz SPI clock. See Fig. 2 below for an illustration of the operation of the output compare module in the "dual compare mode continuous output pulses" mode:

 Fig. 2 - Output Compare module in Dual Compare Mode: Continuous Output Pulse mode (taken from PIC32 reference manual, figure 16-16)

Beyond that, the idea is simple. There is a sine table that is the DMA source. The key thing to remember here is that the 12-bit data must be OR-ed with 0x3000 since that is required for the DAC (to set gain=1, shutdown = 0 and channel = A):

void generateTables(void){
    uint8_t i;
    for (i = 0; i<TABLE_SIZE; i++){
        sineTable[i] = (short) (2047.0 * sin(6.2832*((float)i)/(float)TABLE_SIZE));
        sineTable[i] = sineTable[i] + 2047;

        sineTable[i] = 0x3000 | sineTable[i];
    }
}

Since the entries in the sine table are of "short" data type (each element occupies two bytes), the source size (in bytes) is twice the number of elements. The destination source size is two bytes since I'm transferring two bytes to the SPI buffer register. The cell size is two bytes since the DMA channel has to transfer two bytes (16 bits) at a time to the SPI buffer register. The DMA configuration is:

    DmaChnOpen(0, 3, DMA_OPEN_AUTO);
    // (ch, ch priority, mode)
    DmaChnSetTxfer(0, sineTable, &SPI1BUF, TABLE_SIZE*2, 2, 2);
    // (ch, start virtual addr, dest virtual addr, source size, dest size, cell size)
    DmaChnSetEventControl(0, DMA_EV_START_IRQ(_TIMER_2_IRQ));
    // (ch, trigger irq)
    DmaChnEnable(0);

A point of note is that, the DmaChnSetTxfer( ) takes in the virtual addresses of the source and destination, not the physical addresses. The virtual-to-physical address conversion is done by the function itself as opposed to it being required manually when doing register level operations (as done in my previous DMA example article).

To demonstrate that the DAC control is done with no CPU overhead, in the main( ) function, I have an infinite loop that is just toggling RA0. The output is checked on an oscilloscope:

Fig. 3 - RA0 toggle being demonstrated

Observe that the frequency of the square wave is 4.0MHz - the same that would be observed if all the PIC was doing was the pin toggling.

This also gave better performance than my previous experiment where I did not use DMA. The output sine wave has a higher frequency than observed before and this matches exactly as expected: 400kHz/32 = 12.5kHz as seen (see Fig. 1).

The rest should be very easy to understand and self-explanatory. If you have any questions or suggestions, feel free to comment!

Here are the source and header files:
config.h: https://drive.google.com/file/d/0B4SoPFPRNziHcmlxMmg2Si0zWjQ/view?usp=sharing
main.c: https://drive.google.com/file/d/0B4SoPFPRNziHcmlxMmg2Si0zWjQ/view?usp=sharing

Oct 11, 2014

PIC32 SPI: Using the MCP4822 12-bit serial dual DAC


I recently got a few pieces of the MCP4822 DAC. You can check out the datasheet here: http://ww1.microchip.com/downloads/en/DeviceDoc/22249A.pdf

I found them to be neat little devices. In a small 8-pin PDIP package, the MCP4822 comes with 2 12-bit DACs, which you can easily configure over SPI. This was a great opportunity to get a simple PIC32 SPI application going. I worked on this today to see how fast I can get the DAC output going.

Here's the pinout of the MCP4822:
Fig. 1 - MCP4822 pinout (taken from datasheet)

The MCP4822 can be supplied a voltage in the range of 2.7V to 5.5V. Since I use 3.3V for my PIC32, I used the same 3.3V supply for the VDD for the MCP4822. Pin 5 is the active low signal LDAC that is used to synchronize the two DAC channels. When this pin is brought low, the data in the DAC's input register is copied to the output and both outputs are updated at the same time. I just had this tied to ground. VoutA and VoutB are the two output pins. The other pins are the regular pins for SPI communication - CS (active low) chip select, SCK serial clk, SDI serial data in.

The datasheet provides a nice diagram explaining the timing and pin functions very nicely:
Fig. 2 - Write command for MCP4822 (taken from datasheet)

The write command to the MCP4822 is a 16-bit wide command sent over SPI. Bit 15 (A/B) selects which channel you're sending data to: 1 signifies channel B, 0 signifies channel A. Bit 13 (GA) selects the gain. The MCP4822 has an internal 2.048V reference it uses for the DAC. So, that means that with a gain of 1, the maximum possible output voltage is 2.04975V (4095/4096 * Vref). If a higher output is required, the gain can be increased. The MCP4822 has a configurable gain of 1 or 2. When GA = 1, gain = 1; when GA = 0, gain = 2. Bit 12 SHDN is the shutdown signal. When SHDN is low, the output of the DAC is shut down. The 12 data bits from there on: D11 to D0 ([D11:D0], [bit11:bit0]) are the 12 data bits for the digital to analog conversion.

The transfer function is the very-simple, (almost) typical-for-DACs:
Vout = (D_12 / 4096) * 2.048V * Gain
D_12 is the digital value given by D11 to D0 in the write command, 4096 = 2^12 (12-bit resolution), 2.048V = internal voltage reference, gain = 1 or 2 depending on bit GA in write command.

While I typically use the registers and configure them manually, I decided to use the Microchip plib (peripheral library) here for the SPI module. The peripheral library consists of a lot of low-level functions and macros that essentially require understanding the peripheral but just make the code nicer and easier to read.

I decided to clock the MCP4822 at 20MHz (max for MCP4822) to get quick data transfers.

The SPI module can be initialized using one of 2 options:
1) Using the OpenSPIx(config1, config2) and SpiChnSetBrg(spi_channel, spi_brg) functions
2) Using the SpiChnOpen(spi_channel, config, spi_divider) function

These are both described in the plib documentation.

The DAC initialization routine I wrote is shown below:
//================================================================
// SS = PORTA4
#define SS      LATAbits.LATA4
#define dirSS   TRISAbits.TRISA4
void initDAC(void){
/* Steps:
 *    1. Setup SS as digital output.
 *    2. Map SDO to physical pin.
 *    3. Configure SPI control and clock with either of a or b:
 *        a. OpenSPIx(config1, config2) and SpiChnSetBrg(spi_channel, spi_brg)
 *        b. SpiChnOpen(spi_channel, config, spi_divider)
 */

    dirSS = 0;                    // make SS an output
    SS = 1;                        // set SS = 1 to deselect slave
    PPSOutput(2, RPB5, SDO2);    // map SDO2 to RB5

///////
#define config1 SPI_MODE16_ON | SPI_CKE_ON | MASTER_ENABLE_ON
    /*    FRAME_ENABLE_OFF
     *    ENABLE_SDO_PIN        -> SPI Output pin enabled
     *    SPI_MODE16_ON        -> 16-bit SPI mode
     *    SPI_SMP_OFF            -> Sample at middle of data output time
     *    SPI_CKE_ON            -> Output data changes on transition from active clock
     *                            to idle clock state
     *    SLAVE_ENABLE_OFF    -> Manual SW control of SS
     *    MASTER_ENABLE_ON    -> Master mode enable
     */
#define config2 SPI_ENABLE
    /*    SPI_ENABLE    -> Enable SPI module
     */
//    OpenSPI2(config1, config2);
    // see pg 193 in plib reference

#define spi_channel    2
    // Use channel 2 since channel 1 is used by TFT display

#define spi_brg    0
    // Divider = 2 * (spi_brg + 1)
    // Divide by 2 to get SPI clock of FPBDIV/2 -> max SPI clock

//    SpiChnSetBrg(spi_channel, spi_brg);
    // see pg 203 in plib reference

//////

//////

#define spi_divider 2
/* Unlike OpenSPIx(), config for SpiChnOpen describes the non-default
 * settings. eg for OpenSPI2(), use SPI_SMP_OFF (default) to sample
 * at the middle of the data output, use SPI_SMP_ON to sample at end. For
 * SpiChnOpen, using SPICON_SMP as a parameter will use the non-default
 * SPI_SMP_ON setting.
 */
#define config SPI_OPEN_MSTEN | SPI_OPEN_MODE16 | SPI_OPEN_DISSDI | SPI_OPEN_CKE_REV
    /*    SPI_OPEN_MSTEN        -> Master mode enable
     *    SPI_OPEN_MODE16        -> 16-bit SPI mode
     *    SPI_OPEN_DISSDI        -> Disable SDI pin since PIC32 to DAC is a
     *                            master-to-slave    only communication
     *    SPI_OPEN_CKE_REV    -> Output data changes on transition from active
     *                            clock to idle clock state
     */
//    SpiChnOpen(spi_channel, config, spi_divider);
//////
}
//================================================================

The code block above has code written using both the initialization functions (one is commented out). Essentially, the configuration is that SPI channel 2 is enabled as a master, it is configured for 16-bit data transmission, the SDI pin is disabled, it is configured for serial output change from active high (1) to active low (0) as required by the MCP4822 (see Fig. 2) and the clock divisor is set to 2 so that SPI clock = 20MHz.

The SCK pins for the PIC32 are mapped physically to the RB15 (SCK2) and RB14 (SCK1) pins (check pinout on page 4 in datasheet). The SDO and SDI pins are remappable pins and thus require you to map them to physical pins. The SDI pin here is unused since the PIC32-to-MCP4822 is a one way communication with the PIC32 as the master and the MCP4822 as the slave.

The datasheet provides the table for all the PPS (peripheral pin select) mappings. The relevant section for the SDO is:
Table 1 - PPS configuration for SDO

I decided to use RPB5:

PPSOutput(2, RPB5, SDO2);    // map SDO2 to RB5

The connections I used for the MCP4822 are:


Fig. 3 - MCP4822 connections


The chip select / slave select line (CS as shown on the MCP4822 pinout, see Fig. 1) has to be controlled manually. (For a hardware-based processor-free control scheme, see PIC32 DMA+SPI+DAC : Analog output synthesis with zero CPU overhead.) This is an active low chip select signal. When data is being sent to the MCP4822, while the SPI transaction is occurring, the CS pin must be kept low. Initially I checked to see if there was a way the hardware would take care of that. I looked into the framed SPI mode. However, this did not serve my purpose. From what I've read, the framed SPI mode is an SPI mode where the clock is always output (instead of only during transactions as traditionally done). However, the hardware generates a low sync signal (by pulling CS low) before the data is transmitted from the PIC32. The problem here was that the CS pin was pulled low and kept low for one SPI clock period before raising CS high as data is transmitted out from SDO. This isn't going to work for the MCP4822 since the chip requires that CS be held low during the entirety of data transmission (see Fig. 2). So I just decided to use RA4 as the pin for CS. Before starting any transmission, I pull CS low in software and raise it high at the end.

Here's the code for writing to the DAC:

inline void writeDAC(uint16_t data){
    SS = 0; // select slave device: MCP4822 DAC
    while (TxBufFullSPI2()); // ensure buffer is free before writing
    WriteSPI2(data);   // send the data through SPI
    while (SPI2STATbits.SPIBUSY); // blocking wait for end of transaction
    SS = 1; // deselect slave device, transmission complete
}

Since I called this function from the ISR in my code, I decided to make it an inline function to eliminate function call overhead.

In my test code, I just wanted to see how fast an output I can get. So I clocked the MCP4822 at 20MHz and tried to maximize the frequency of the output signal I generate: I used channel A to generate a sine wave output and channel B to generate a triangle wave output. I used a timer (Timer 1) to control the timing and in the ISR, I just called the writeDAC inline function to generate outputs as required from two pre-generated tables.

Here are the different waveforms and outputs I generated:

Fig. 4 - Generating the outputs with timer period = 400kHz, sine table entries = 64



Fig. 5 - Generating the outputs with timer period = 550kHz, sine table entries = 32



Fig. 6 - Generating the outputs with timer period = 550kHz, sine table entries = 64



Fig. 7 - SPI clock and data transmission
You can clearly see the SPI clock during transmission here. From the measurement panel on the right you can see that the SPI clock is 20MHz. There are also 16 pulses you can count - since a write command to the DAC consists of 16 bit transfers. CH2, on the bottom, shows the data being transmitted. Since the first signal (leftmost) is a 1, and since this corresponds to bit 15, this is a snapshot of data being transmitted to DAC channel B.


Fig. 8 - DAC steps - zoomed in


Fig. 9 - DAC steps - zoomed out


Fig. 10 - Hardware test setup

For the PIC32 proto board, I used my own custom proto board: http://tahmidmc.blogspot.com/2014/02/pic32-proto-board-details-schematic-pcb.html


Here are the header and source files for my test program:

config.h: https://drive.google.com/open?id=0B4SoPFPRNziHdk5KT2hWNUNCckE
main.c: https://drive.google.com/open?id=0B4SoPFPRNziHWFc0Y01qSW5XUkE


While this was a quick test I did to test the PIC32 SPI and the MCP4822, this is in no means a complete evaluation of the MCP4822. For example, I updated the DAC more quickly than I should have, to push for speed, as this didn't give enough of a settling time. However, I was satisfied with the results and so, stuck with that. I will do some further testing on this later.

For more details of the PIC32MX250F128B SPI module, refer to the datasheet and reference manual. For more details on the MCP4822, refer to its datasheet.

If you have any questions, feel free to ask. Let me know what you think, in the comments section below!

Note: Updated September 10, 2015

Jun 23, 2014

PIC32MX250F128B ipl7: odd shadow register set behavior and observations


This issue has now been solved. Scroll to the bottom for the update.

I started working on a pure sine wave inverter based on the PIC32MX250F128B. The first stage was to just get the SPWM working and test that before proceeding to add the power stage, feedback and other control, etc.

This led me to some interesting observations regarding the PIC32MX250F128B interrupts, especially the use of the shadow register set and a related problem.

I set up Timer 2 to be the time base for sine wave generation (DDS/SPWM) using OC1. In the Timer 2 interrupt, the duty cycle update request is performed (OC1RS is updated). Timer 2 interrupt priority was set to 7.
This all worked fine. The initial ISR definition was:

void __ISR(_TIMER_2_VECTOR, ipl7) T2int(void) {
.....
}


However, when I used the ADC (input from potentiometer for amplitude control - a start at implementing feedback), accessing any of the ADC registers in the while (1) loop in main stopped the PIC32 from working. 

I then added a line of code in main to continuously toggle RA0 (PORTA bit 0). This would stop when trying to access the ADC registers as well. (It worked fine if I removed the code that accessed the ADC.) This meant that the core was being stopped. So, I thought that it might be some kind of exception being thrown (?).
However, changing the timer 2 interrupt priority from 7 (which I had initially) to anything else fixes the problem. My guess was that something went wrong when the shadow register set was being used for interrupt priority level 7 (?).

So I did some more tests and came up with this:

The way I have the ISR defined initially selects the "AUTO" mode of selection where, I believe, the compiler decides whether to use the shadow register set or software for context save/restore.
Forcing the compiler to use the software context save/restore instead of the shadow register set seems to fix it:

void __ISR(_TIMER_2_VECTOR, ipl7SOFT) T2int(void) {
.....
}


I forced the shadow register set just to confirm:

void __ISR(_TIMER_2_VECTOR, ipl7SRS) T2int(void) {
.....
}
and sure enough it didn't work.

So, since it worked with ipl6, my guess was that for ipl6 (and for any ipl except 7), the compiler chooses software for AUTO, whereas for ipl7, the compiler chooses SRS.

So, I forced the shadow register set on ipl6:

void __ISR(_TIMER_2_VECTOR, ipl6SRS) T2int(void) {
.....
}
and sure enough, it didn't work.

Forcing software for ipl6 fixes it (as does AUTO).

I looked through the errata of the chip and couldn't find anything. (The errata is an interesting read.)  I am also a little surprised since I did have ipl7 running fine previously. Maybe something weird happens with a certain combination of peripherals (?) that I unfortunately happened to use. I'll still see if I can find this documented, and I'll have to check whether an exception is thrown and if so, what type. If nothing else, at least it was a nice, albeit frustrating, lesson. Hopefully, it'll help someone else who might run into a similar problem.

UPDATE/SOLUTION: It turns out that the PIC32MX250F128B does not have a shadow register set, as mentioned in the datasheet. I seemed to have missed that while concentrating on the reference manual which talks about the shadow register set for the PIC32 series. Thus, from the point of view of my 'experiments', the lesson learnt could very well just be that when using ipl7, force the compiler to use software instead of the default AUTO mode (which may make the compiler try to use the shadow register set, which doesn't exist - explaining the problem I faced earlier).

---------------------------------------------------
DDS = direct digital synthesis
ipl = interrupt priority level
ISR = interrupt service routine 
SPWM = sinusoidal pulse width modulation
SRS = shadow register set

Jun 17, 2014

Simple AC voltmeter


I had long been thinking of designing a simple AC voltmeter but had somehow never gotten to it. However, with school just ending, and me going back to electronics, this was a neat simple yet useful circuit that I felt I could just get done with in a day or two.

The concept is fairly straight forward. Since I needed this only to measure the mains AC voltage and the output voltage of the sine wave inverter I was working on (based on the PIC32MX250F128B - more on this later), I didn't need to do any "true RMS" measurement. All I had to do was to convert the mains AC to DC, filter it and measure the peak voltage. Of course this had to be scaled down to a safe range for the microcontroller. The RMS voltage for a sine wave is equal to 0.707 (to 3 decimal places) the peak voltage.

The design was based on the Microchip PIC16F676. However I realized that I didn't have any in stock with me. So, I just used the pin-compatible PIC16F684. On the software side of things, the difference between the code for PIC16F676 and PIC16F684 is just one line. (See source file attached below.)

The variable resistor is adjusted so that the reading of my AC voltmeter matches that of a commercial voltmeter (Extech MA640 DMM). This is a sort of calibration.

CAUTION: THIS CIRCUIT IS NOT ISOLATED FROM THE MAINS AC AND HAS THE POTENTIAL TO BE LETHAL. YOU MUST BE VERY CAREFUL IF YOU ARE GOING TO USE THIS DESIGN. DO NOT ATTEMPT IF INEXPERIENCED. I CANNOT AND WILL NOT BE HELD RESPONSIBLE FOR ANY DAMAGE DONE WHILE USING THIS CIRCUIT.

Here's the schematic:

 Fig. 1 - Voltmeter

Here are some pictures:

 Fig. 2 - The PCB without (above) and with (below) the transformer


Fig. 3 - The test setup


Fig. 4 - Measuring voltage set with the variac


 Fig. 5 - Measuring voltage set with the variac


 Fig. 6 - Measuring voltage set with the variac

Fig. 7 - Measuring voltage set with the variac


C source file:
https://drive.google.com/file/d/0B4SoPFPRNziHdDB6cDdzNGZPdXc/edit?usp=sharing

http://www.4shared.com/file/Cz56s9Zlba/ACvoltmeter.html

mikroC project file:
https://drive.google.com/file/d/0B4SoPFPRNziHN2MydDRDalRWS1k/edit?usp=sharing
http://www.4shared.com/file/JwW3bpJNce/ACvoltmeter.html

Parts list (Excel XLSX file):
https://drive.google.com/file/d/0B4SoPFPRNziHb2ZCcnFNVnNpc2s/edit?usp=sharing
http://www.4shared.com/file/b_NcW4vdce/parts_list.html

Schematic (ARES DSN file):
https://drive.google.com/file/d/0B4SoPFPRNziHb1NFWDdwcWpJV2M/edit?usp=sharing
http://www.4shared.com/file/U6kPQH_rce/schematic.html

PCB (ARES LYT file):
https://drive.google.com/file/d/0B4SoPFPRNziHNDhhMDBkNFlnWW8/edit?usp=sharing
http://www.4shared.com/file/ZVczMJ7Wba/schematic.html

PCB (PDF files):
https://drive.google.com/file/d/0B4SoPFPRNziHMV9LdTBab1BXeDg/edit?usp=sharing
Mirrored:  https://drive.google.com/file/d/0B4SoPFPRNziHSmxvT1pSbXBWOTA/edit?usp=sharing

It's a fun simple yet useful project that you can build pretty easily if you want. Let me know what you think in the comments section below!

May 13, 2014

Simple PIC32 DMA example


I've been testing the PIC32 DMA recently. The PIC32 has a DMA (direct memory access) module that allows the data transfer in the PIC32 without CPU intervention during data transfer - thus freeing up CPU to perform other tasks while the data is transferred).

First, I did a simple test to just see if it works (I've also done some DMA timing testing - I'll talk about them soon in another article). The first test was to just use the DMA module to transfer data from a constant array (from FLASH) to PORTA (LATA for output). This would be visually seen as LEDs connected to PORTA blinking.

The code is:

//================================

/***************************************************
 * Programmer: Syed Tahmid Mahbub
 * Target PIC: PIC32MX250F128B
 *
 * Date: 05/08/2014 - 05/10/2014
 *
 * Program to test DMA operation
 * DMA will transfer const (from FLASH) to PORTA
 ***************************************************/

#include "plib.h"           // peripheral library
#include "settings.h"       // configuration bits settings

const unsigned char portOut[] = {0x05, 0x0A};

unsigned int sourceAddr;
unsigned int destinationAddr;

void Initialize(void){

    SYSTEMConfigPerformance(8000000); // Running at 8MHz for now
    ANSELA = 0; ANSELB = 0;
    TRISA = 0; TRISB = 0;

    ///// Virtual to physical memory

    /* using a pointer returns virtual memory address:
     *      eg. const unsigned int* src = (void*) &LATA;
     * returns 0xBF886030 -> agrees with datasheet
     *
     * reference manual says that to convert this to physical
     *      address, AND it with 0x1FFFFFFF and get the
     *      corresponding physical address
     */

    ///// Set source and destination addresses
   
    sourceAddr = (unsigned int) &portOut & 0x1FFFFFFF;      // Physical address of portOut
    destinationAddr = (unsigned int) &LATA & 0x1FFFFFFF;    // Physical address of LATA

    ///// Initialize dma first
    DMACON = 0x8000;            // dma module on
    DCRCCON = 0;                // crc module off
    DCH0INT = 0;                // interrupts disabled
    DCH0SSA = sourceAddr;       // source start address
    DCH0DSA = destinationAddr;  // destination start address
    DCH0SSIZ = 2;               // source size - 2 bytes
    DCH0DSIZ = 1;               // destination size - 1 byte
    DCH0CSIZ = 1;               // cell size - 1 bytes
    DCH0ECON = 0x1310;          // dma transfer triggered by interrupt 19: Timer 4

    ///// Initialize timer 4 now - timer 4 interrupt triggers dma transfer
    PR4 = 3124;         // 100 milliseconds
    T4CON = 0x70;       // prescaler 1:256, timer currently off
    // timer 4 interrupt request triggers dma

    ///// Enable dma channel
    DCH0CON = 0x93;     // channel enabled, always on, priority 3
}


void main(void){

    Initialize();
    T4CONSET = 0x8000; // turn on timer 4
    while (1){
       
    }

}

//================================

The DMA module allows data transfer from a source to a destination without CPU intervention during data transfer. The data transfer can be triggered by any interrupt request within the PIC. An interesting point of note is that the DMA module maintains its own flags for detecting interrupt requests for data transfer start/abort requests. This is completely independent of the INT interrupt controller enable and flag settings/configuration.

Points of note on the DMA:

  • Data is transferred from the source register to the destination register upon a transfer request.
  • The source size and destination size registers define the size of the source and destination to which you want to transfer data. For example, LATA is a 32-bit register (typical for the PIC32). However, since I'm only planning to transfer data to the lowest byte in LATA (see code above), my destination size is one byte.
  • The cell size describes the number of bytes to transfer upon one DMA transfer request. For example, in my code I was transferring just one byte every DMA request (either 0x05 or 0x0A) - so the cell size is 1 byte.
  • The DMA transfer is complete when either a block transfer occurs or you abort the transfer. A block transfer occurs when the number of bytes equal to the size of the larger of the source and destination register sizes is transferred. So, in my code, a block transfer is when both byte 0x05 and 0x0A are transferred. So, that's two DMA cell transfers upon two interrupt requests (only one cell transfer occurs upon one interrupt request).
  • The source and destination addresses are PHYSICAL ADDRESSES, NOT VIRTUAL ADDRESSES used by the MIPS core. The reference manual mentions the easy translation between physical and virtual addresses: (Physical address) = (Virutal address) & 0x1FFFFFFF. Remember that when you point to a register address (eg &LATA), that returns the virtual address and not the physical address.
  • The DMA transfer is triggered by an interrupt request (IRQ). So, in the DMA event control register, you must specify which IRQ is to trigger the data transfer. To ensure that this IRQ triggers a DMA transfer, the SIRQEN (Channel Start IRQ Enable bit) bit (bit 4 of the DCHxECON register) must be set. Additionally, when you specify the IRQ in the event control register, make sure you use the IRQ # and not the vector #.
  • The maximum source size, destination size and cell size are all 65,535 bytes (wow!). You can transfer that many bytes on an event. 
For further details on the DMA controller, go through the reference manual.

For information on using the DMA plib (peripheral library) functions, see: http://people.ece.cornell.edu/land/courses/ece4760/PIC32/



Apr 5, 2014

Hacking the PIC32 Comparator Reference to obtain a DAC



The PIC32MX250F128B has a myriad of peripheral features that makes this cheap little 28-pin 32-bit microcontroller extremely powerful. However, it does lack a digital-to-analog converter (DAC). A simple approach to digital-to-analog conversion is to use one of the PWM channels and then to use an RC low-pass filter at the output.

However, there is another neat little feature of the device that can be exploited or rather utilized to obtain a rather crude DAC – the analog comparator module. Rather, the analog comparator reference module. The PIC32MX250F128B contains an internal voltage reference module that was created to be used as a reference voltage for the internal analog comparator module. However, it does allow you to output this reference voltage to an output pin. Analog voltage output with digital control? Sounds like a DAC.

The voltage reference module is built off of a 16-tap resistor ladder network with two selectable ranges – from 0 to 0.67 CVREF or from 0.25 to 0.75 CVREF. CVREF is the reference voltage for the module and can be connected to VDD (or an external VREF+). Thus there are 32 possible voltage combinations. However, there are some voltages present in both ranges, and additionally, “interleaving” the two ranges does not give a linear set of 32 values. Thus this cannot be used to achieve a full 5-bit resolution DAC. Cleverly selecting the values from the two ranges can, however, lead to more than 16 steps. 16 steps is the number of steps achievable from either range.

The stabilizing time for the internal reference module is listed in the datasheet as 10µs. However, this is defined as the time required to stabilize for a transition from 0000 to 1111 in the voltage selection bits (4bits = 16steps). This means that this can be pushed to relatively high speeds (in the tens of kilohertz) pretty easily depending on the range of the swing of the voltage.

I have received good results with frequencies in the tens of kilohertz. At about 100kHz (10µs between samples), I can see the waveform “curve” a bit but even then it doesn’t look too bad and is definitely useable.

Why use this as a DAC? Well, it’s a nice little tool available to you. Consider the fact that this outputs an analog value with no required additional filtering. Additionally, if you don’t need anything that requires a high resolution (ie no need for a high resolution DAC), this will allow you to leave a valuable output compare / PWM channel to use for something else. If not for anything else, it’s just a fun thing to explore.

I have written a sample piece of code to test this. There are four modes of operation here:


  • Sawtooth mode - Generate sawtooth waveform with 16 steps (only 1 DAC range) (see Fig. 1 below)
  • DAC_Out mode - Output all the values of the DAC full range (32 steps) - this clearly illustrates the non-linearity I was talking about. (see Fig. 2 below) Observe the apparently linear region in the middle.
  • Sine_1 mode - Generate half sine wave output with 16 point sine table. The 16 DAC points are used for the entire sine half wave. DAC used with 16 steps (see Fig. 3 below)
  • Sine_2 mode - Generate half sine wave output with 32 point sine table (16 step DAC mode) - ie this uses the 16 DAC points for half of a sine half wave and then repeats these for the second half, utilizing the 16 step DAC more fully (see Fig. 4 below)
Here are some pictures from my experiments:

Fig. 1 - Sawtooth mode  


Fig. 2 - DAC_Out mode


Fig. 3 - Sine_1 mode


Fig. 4 - Sine_2 mode

Here are download links to the source code:

Source code (main.c):
https://drive.google.com/file/d/0B4SoPFPRNziHZUd5NHM5UDhiSGs/edit?usp=sharing
http://www.4shared.com/file/HaU5syz-ba/main.html

Any ideas? Comments? Feedback? Let me know in the comments section below!

Feb 16, 2014

Automatic voltage stabilizer (AC-AC) with PIC16F873A - circuit, explanation, PCB, source code, videos and loads of pictures!




Download links for the article: 

Google docs (go to the link, click File in the PDF window, then click Download):
https://docs.google.com/file/d/0B4SoPFPRNziHb3VrX3JhWko3d1E/edit

Scribd link: http://www.scribd.com/doc/207498741/Automatic-Voltage-Stabilizer-with-PIC16F873A

4shared download link: http://www.4shared.com/office/ktU0k9dqba/Voltage_Stabilizer.html



You can download all the files related to the voltage stabilizer:

Google docs (go to the link, click File in the PDF window, then click Download):

PCB images: