How do you write and read to memory with PIC18? - c

I want to store a number to PIC18 then retain it even if the power is lost or the unit is reset. I think my writing code portion looks fine, just the reading portion of it looks strange after the unit is reset.
I am using the following code which I got from Microchip.
Code:
unsigned int value;
unsigned int DEEdata = 1;
unsigned int DEEaddr = 0x04;
DataEEInit();
dataEEFlags.val = 0;
DataEEWrite(DEEdata,DEEaddr);
value = DataEERead(DEEaddr);
Nop();
printf("%d",value);
The ouput: 1
However when I reset the unit and only use the reading code I always get 255.
Code to read:
DataEEInit();
value = DataEERead(DEEaddr);
printf("%d",value);
The output: 255
Why is this happening? I am assuming maybe the value is not being saved or the reading portion is incorrect. Thank you!

Two functions: write to flash using 64-byte buffer # 8-byte blocks and a read/compare flash function.
For device: PIC18F46K80
Stuff for a header file:
#define PRGM_BUFFER_SIZE 8
#define TABLE_WRITE_SIZE 64
#define LOAD_TBL_PTR(x) { TBLPTRU = ((((x)>>8)>>8)&0xff);\
TBLPTRH = (((x) >> 8) & 0xff);\
TBLPTRL = ((x) & 0xff);\
}
Write to flash function:
/******************************************************
* Function : write_block
* Input : uint16_t position in destination flash
* Global : uint8_t buffer[64] - Location of source data
* Output : None
* Description : Writes the contents of the 64 byte
* data buffer to program flash space. Only 64 bytes
* can be written at once. The process of writing
* to flash is: Erase->Write.
******************************************************/
static void write_block(uint16_t addr)
{
int r, c;
// Erase flash block first. Erases a 64 byte block at a time.
LOAD_TBL_PTR(addr);
EECON1bits.EEPGD = 1; // Point to flash program memory
EECON1bits.CFGS = 0; // Access flash memory
EECON1bits.WREN = 1; // Enable write to memory
EECON1bits.FREE = 1; // Enable Erase operation
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1; // Clear the flash
asm("NOP"); // Stall
// Write buffer to internal buffer. This process writes 8 bytes at a time
// so we need to loop 8 times (8*8 = 64).)
for (r = 0; r < 8; r++)
{
LOAD_TBL_PTR((addr + (r * 8)));
for (c = 0; c < PRGM_BUFFER_SIZE; c++)
{
TABLAT = buffer[(r * 8) + c];
asm("TBLWT*+"); // Push byte and then inc to next internal buffer cell
}
// Write the block to flash
asm("TBLRD*-"); // Point back to original row
// Write internal buffer to flash
EECON1bits.EEPGD = 1; // Point to flash program memory
EECON1bits.CFGS = 0; // Access flash program memory
EECON1bits.WREN = 1; // Enable write to memory
INTCONbits.GIE = 0; // Disable interrupts
EECON2 = 0x55;
EECON2 = 0xAA;
EECON1bits.WR = 1; // Start programming flash
INTCONbits.GIE = 1; // Re-enable interrupts
EECON1bits.WREN = 0; // Disable write to memory
}
}
Verify written data (demonstrates flash read)
/******************************************************
* Function : compare_block
* Input : uint16_t position in destination flash
* Global : uint8_t buffer[64] - Location of previous written data
* Output : bool true=successful, false=did not match
* Description : Reads a 64 byte block of flash memory and
* compares it to the data found in the global buffer.
******************************************************/
static bool compare_block(uint16_t addr)
{
bool retVal = true; // succeeds
uint8_t i = 0;
INTCONbits.GIE = 0; // Disable interrupts
LOAD_TBL_PTR(addr);
for (i = 0; i < TABLE_WRITE_SIZE && retVal == true; i++)
{
asm("TBLRD*+");
if (buffer[i] != TABLAT)
retVal = false;
}
INTCONbits.GIE = 1; // Enable interrupts
return retVal;
}
Yours,
Bryan Wilcutt

The device you're using doesn't have internal non-volatile memory apart from its Flash, generally used for storing code.
You have two options that I can see:
Use some external Flash or EEPROM and interface it to the External Memory Bus that's available on this PIC (see page 97 of the Family Datasheet).
Remap the internal Flash to reserve a small portion that can be used for storing your data (so that it doesn't interfere with the memory area used exclusively for code) and write your data into this region (page 87).
I haven't worked with PICs for years, so can't offer you much in the way of implementation detail but I suspect there are many examples you can source from Microchip's website.
In essence, the reason your code doesn't work is because you're trying to access memory that isn't there. If it is there, then the interface is not correct.
EDIT:
I've had a look through the code examples page for the PIC18 on Microchip's website and can't find any C examples for writing to the program memory. Unfortunately, it looks like you'll have to implement it in assembler. I don't know the semantics for the MPLAB compiler but, generally, it'll be something like this if you're going to do it inline:
void my_assembler_function(void)
{
// Inline assembler code, actioned via C.
asm("MOV x y");
asm("MOV y z");
}
Alternatively, many C compilers for microprocessor's allow you to call an external .s file with a C function call, saving you from doing it inline.
I think you can follow the example I found here to actually implement the functionality you're after.

SRAM can not be used to store Non-volatile data...
SRAM will loose data during power cycle...
Options:
1. Use internal EEPROM if available.
2. External EEPROM through I2C or SPI.
3. PIC18 Data Emulation Library.

This is an explanation of the PIC18:
/* EEPROM Read and Write Functions -- WORKING
* Used PIC18F45K22 and MPLAB and C18
* Read and Write functions work.
* EEPROM has 256 bytes of memory (256 distinct characters)
* Select "Window" -> "PIC Memory Views" -> "EE Data Memory"
* Download program to PIC18
* Hold PIC in Reset (circle arrow with pause button)
* Open EE Data Memory Tab and click "Read Device Memory" button (Top left of EE Data tab) while PIC is held in Reset
*/
This is helpful code:
#include <p18cxxx.h>
#include <p18f45k22.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#pragma config FOSC = INTIO67, PLLCFG = OFF, PRICLKEN = ON, FCMEN = ON, PWRTEN = OFF
#pragma config BOREN = SBORDIS, BORV = 250, WDTEN = OFF, WDTPS = 2048, PBADEN = OFF, WRTD = OFF
#pragma config HFOFST = OFF, MCLRE = EXTMCLR, STVREN = ON, LVP = OFF, DEBUG = ON, CPD = OFF
void EE_Write(unsigned char addr, unsigned char value);
unsigned char EE_Read(unsigned char addr);
unsigned char test;
void main(void){
OSCTUNEbits.PLLEN = 1;
OSCCON = 0x4C; //Set to use internal clock.
OSCCON2 = 0x00; // No 4x PLL
TRISB = 0x00;
ANSELB = 0x00;
PORTB = 0x00;
EE_Write(05, 0x5A);
Delay10KTCYx(50);
test = EE_Read(05);
Delay10KTCYx(50);
PORTB = 0xFF;
Delay10KTCYx(50);
PORTB = 0x00;
}
void EE_Write(unsigned char addr, unsigned char value)
{
unsigned char SaveGIE = 0;
// Set EEPROM address
EEADR = addr%256;
// Set EEPROM data
EEDATA = value;
// Select Data
EECON1bits.EEPGD = 0;
// Select EEPROM
EECON1bits.CFGS = 0;
// Enable write
EECON1bits.WREN = 1;
// Save current global interrupt enable state
SaveGIE = INTCONbits.GIE;
// Disable interrupts
INTCONbits.GIE = 0;
// Write unlock sequence
EECON2 = 0x55;
EECON2 = 0xaa;
// Start write
EECON1bits.WR = 1;
// Restore previous interrupts enable state
INTCONbits.GIE = SaveGIE;
// Wait for write completion
while(EECON1bits.WR);
// Disable writes
EECON1bits.WREN = 0;
}
unsigned char EE_Read(unsigned char addr){
while(EECON1bits.RD || EECON1bits.WR); // check the WR&RD bit to see if a RD/WR is in progress
EEADR = addr; // Write the address to EEADR.
EECON1bits.CFGS = 0;
EECON1bits.EEPGD = 0;
EECON1bits.RD = 1; // Set the RD bit to trigger the eeprom read operation.
return(EEDATA);
}

Some PIC18 micros have an internal EEPROM in addition to the internal flash. The 18F87J11 does not have this so you have 2 options:
1) Write to the flash memory - this is where your program is stored. make sure the number of write/read cycles is ok for your application.
2) Use an external i2c or spi memory for your configuration settings
The DataEEWrite you are using are from an 'eeprom emulation' library from microchip (linked in the comments below. There are a couple of things to be careful of:
Watch out when reprogramming the flash! you might overwrite your settings
Remember it isn't really eeprom! The write cycles are limited and you have to erase big sections of memory - you can't erase a single byte

The value of 255 is default value for EEPROM memory. I think after changing the code, you program microcontroller IC again. So, your EEPROM memory will be erased and return to its default value. If you use MPLAB as compiler, you can go to 'Programmer'tab > Settings.. > Program Memory > Program Options and click on Preserve EEPROM on Program.
Hope it works.

To retain the values on power cycle, SRAM memory should be used. Please confirm if you have SRAM memory available first.

Related

Microchip PIC24FJ SPI writes correctly but only reads zeros

I have a PIC24FJ256GA702 communicating with a AD5724RBREZ quad DAC with a SPI link.
The DAC works fine, writing is no problem, but I am stuck on reading back the control register from it.
I get the correct waveform on the PIC pin that I am expecting, and the read routine runs, but it always returns zeros. The waveform is clearly not zero- the waveform on the scope is correct for the 4 channels and the internal reference being enabled.
Scope view of waveforms - blue = clock, yellow = data input from DAC at PIC pin
(The excessive ringing on the scope image is probably caused by a long distance ground connection- in practice these chips are about 25mm apart.)
I thought that the input pin was configured as an analogue, but it was correctly a digital input.
I connected it to a counter based on Timer1, and that counter does count if I try to read the DAC. This suggests that the PPS is working, the pin is not bust, and the the input signal is clean enough to use.
I think it may be a problem with the code or the decode timing of the SPI module, but as shown in the image the data is stable during the clock cycle so I cannot see what is wrong.
I have searched the forums, and it seems most with this problem trace it to analogue functions being enabled but that is not the case here.
Would anyone like to suggest something else to try, or post some working SPI read code if my code is not looking correct?
The code follows-
void AOUT_init(void)
{
//assume PPS is unlocked (it is on reset) - see note below
//setup SPI1 itself
SPI1CON1L = 0x0160; //TX work read 0
//SPI1CON1L = 0x0060; //TX not work
//SPI1CON1L = 0x0120; //TX work, read 0
//SPI1CON1L = 0x0020; //not tried
SPI1CON1H = 0x3000;
SPI1CON2L = 0x0017; //word length (17 hex = 24 bits)
//BRG
SPI1BRGL = 0x0000; //default = no divisor
//PPS - assume unlocked at this point
ANSBbits.ANSB13 = 0;
TRISBbits.TRISB13 = TRIS_INPUT;
//##########################################################################
RPINR20bits.SDI1R = 13; //set SDI1 data input to PIC to RB13
//##########################################################################
TRISBbits.TRISB15 = TRIS_OUTPUT;
RPOR7bits.RP15R = 8; //RB15 to SDI1 clock out from PIC
TRISBbits.TRISB14 = TRIS_OUTPUT;
RPOR7bits.RP14R = 7; //RB14 to SDI1 data out from PIC
//AD5724R has additional lines - not all used in practice
//setup and set initial level here
//AOUT-/LDAC
TRISBbits.TRISB6 = TRIS_OUTPUT;
LATBbits.LATB6 = 1;
//AOUT-/SYNC
TRISBbits.TRISB7 = TRIS_OUTPUT;
LATBbits.LATB7 = 1;
//AOUT-/CLR
TRISBbits.TRISB12 = TRIS_OUTPUT;
LATBbits.LATB12 = 1;
//turn SPI on
SPI1CON1Lbits.SPIEN = 1;
SPI1CON1Lbits.MSTEN = 1; //included in definition above
//now setup the AD chip
//output range set
AOUT_TX(0x0C00,0x0100); //all channels to 10V
//control
AOUT_TX(0x1900,0x0500);
//power control - enable DACs
AOUT_TX(0x1000,0x1F00);
}
The comms routine below is included for completeness- it just controls the other DAC lines. The /SYNC line is doing a chip select function.
static void AOUT_Comms(bool bSync, bool bLDAC, bool bClr)
{
//AOUT-/LDAC
LATBbits.LATB6 = bLDAC;
//AOUT-/SYNC
LATBbits.LATB7 = bSync;
//AOUT-/CLR
LATBbits.LATB12 = bClr;
}
This is write routine which works fine.
void AOUT_TX(uint16_t dataH, uint16_t dataL)
{
//AOUT uses 24 bit data
//this routine handles /SYNC line
//relies on AD chip having much faster response than chip cycle time
//AD chip limits at about 38MHz so much quicker than PIC.
AOUT_Comms(0,1,1);
SPI1BUFL = dataL;
SPI1BUFH = dataH;
while(SPI1STATLbits.SPIBUSY) //wait until sent
{
;
}
AOUT_Comms(1,1,1);
//
}
This is the read routine, which uses the routines above.
void AOUT_read_control(uint16_t ReadH, uint16_t ReadL)
{
uint16_t temp;
//to read, transmit the register to be read, then transmit a dummy command "NOP" to clock the data out.
//send register
AOUT_TX(0x9000,0x0000);
//read out- this is similar to write but reads the received buffer at the end.
//clear buffer
while (SPI1STATLbits.SPIRBF)
{
temp = SPI1BUFL;
temp = SPI1BUFH;
}
AOUT_Comms(0,1,1);
SPI1BUFL = 0;
SPI1BUFH = 0x1800; //nop operation via control register
while(SPI1STATLbits.SPIBUSY) //wait until sent
{
;
}
while (!SPI1STATLbits.SPIRBF)
{
; //hold until something received
}
ReadH = SPI1BUFH;
ReadL = SPI1BUFL; //these read zero
AOUT_Comms(1,1,1);
//
//dummy so can check counter also connected to same pin
temp = TMR1;
temp = TMR1;
}
Update-
I checked the SPI read decode by sending the received words directly to a display as suggested by the comments. I get 0000 for both words.
Also as suggested I connected a logic analyser at the PIC pins, and it decodes both read and write correctly.Logic Analyser view- Ch1/Blue/MOSI writing 0x180000, Ch2/Green/MISP reading 0x00001F, both correct

Unwanted Jump to interrupt MPLAB X IDE v.3.30

I am new to programming on micro controllers and I am trying to write a timer program for the PICLF1571. Every time it wakes from sleep, it's supposed to write to the flash memory. When I debug it with the simulation, it's able to write once, but once it loops the program gets stuck in the interrupt routine. If I comment out the Interrupt routine, the simulation jumps to another place or goes to 0x00.
The only time I see the program get stuck is when the function flash_write is used.
What could be some causes for interrupts to occur if no flags are triggered?
pin setup
void init(void){
ANSELA = 0x0; //|-> Pin setup
TRISA = 0x0;
TRISAbits.TRISA5 = 1;
PORTA = 0x0;
LATA = 0x0;
INTCONbits.GIE = 1; //|-> Interrupt setup
INTCONbits.IOCIE = 1;
IOCAP = 0x0;
IOCAPbits.IOCAP5 = 0;
INTCONbits.IOCIF = 0;
IOCAF = 0x0;
IOCAFbits.IOCAF5 = 0;
WDTCONbits.SWDTEN = 1; //|-> Watchdog Timer setup
WDTCONbits.WDTPS = 0b00001;// reconfigure for correct speed
//currently 0b10001
}
main
//#include <stdio.h>
#include <xc.h>
#include "init.h"
#include "Interrupt.h"
#include "flash.h"
int main(void){
init();
unsigned short ad = 1;
unsigned short f = 0x3FFF;
unsigned short a = 0x0000;
unsigned short ret = 0x0000;
flash_erase(0x0000);
while(1) {
asm("sleep");
flash_write(a,f);
//flash_erase(a);
//flash_read1(a,&ret);
}
return 1;
}
flash_write function. Instructions based on flowchart in datasheet
void flash_write(unsigned short addr, unsigned short data){
INTCONbits.GIE = 0; //||]->start write
PMCON1bits.CFGS = 0;//||]
PMADRH = (unsigned char)((addr >> 8) & 0xFF);
PMADRL = (unsigned char)(addr & 0xFF);
PMCON1bits.FREE = 0;//||]->enable write
PMCON1bits.LWLO = 1;//||]
PMCON1bits.WREN = 1;//||]
PMDATH = (unsigned char)((data >> 8) & 0xFF);
PMDATL = (unsigned char)(data & 0xFF);
PMCON1bits.LWLO = 0;
PMCON2 = 0x55; //||]->unlock sequence
PMCON2 = 0xAA; //||]
PMCON1bits.WR = 1; //||]
NOP(); //||]
NOP(); //||]
PMCON1bits.WREN = 0;//]->end write
INTCONbits.GIE = 1;
asm("RETURN");
}
interrupt routine
#include <xc.h>
void interrupt button(void){
if (INTCONbits.IOCIE == 1 && IOCAFbits.IOCAF5 == 1 ){
int time = 0;
while (PORTAbits.RA5 == 1){//RA5 in sim never changes. always 0
time++;
if (time >= 20000 && PORTAbits.RA5 == 1){
LATA = 0x0;
asm("NOP");
break;
}
if ( time < 20000 && PORTAbits.RA5 == 0){
asm("NOP");
break;
}
}
IOCAF = 0x0;
INTCONbits.IOCIF = 0;
asm("RETFIE");
}
}
0000h is the reset vector address; once written, subsequent resets will jump to 0x3fff, which is not a valid address on a device with only 0x3ff words of flash. Note that since 0x3fff is the erased state of flash memory, the write in this case actually has no effect that is not already caused by the erase alone.
Also understand that, the PIC12 flash memory is word-write/row-erase, and a row is 16 words. So the erase operation will wipe out the entire vector table and the start of the program area too. You are essentially modifying the code, but not in a manner than makes any sense (if it makes sense at all).
You should reserve an area of flash using appropriate linker directives and well away from the vector table (probably the entire last row at 0x3f0, to prevent the linker locating code in the space you want to write at run time.
Another issue is that the flash cell endurance on PIC12F1571 is only 10000 erase/write cycles - are you sure you want to write to the same address every time the device boots? If you "stripe" the reserved row and write to each of the 16 words in turn before erasing the row and restarting, you will increase endurance to 160000 cycles. Add more rows to get greater endurance. Since erased flash has a value 0x3fff (14 bit words), to find the "current value" you need to scan the reserved row(s) for the last value that is not 0x3fff (0x3fff is implicitly therefore not a valid actual value).

Trouble in writing to Internal EEPROM- PIC16F684

In a Home security alarm system, we have a remote control armed with PT2440
with Fixed encoding (no hop coding, no encryption/decryption) and a central
receiver system armed with MCU: PIC16F684. I must use the internal EEPROM (256 bytes).
After a lot of programming and testing my problem reduced to: I can write to internal
memory before the main loop, but inside the main while loop, the write operation
failed. Here it is my main code
//This function Writes data to given address in internal EEPROM of PIC MCU
void internal_EEPROM_putc(unsigned char addr, unsigned char data)
{
unsigned char INTCON_SAVE;
EEADR = addr;
EEDATA = data;
EEPGD = 0; // 0 = Access data EEPROM memory
WREN = 1; // enable writes to internal EEPROM
INTCON_SAVE = 0x9B; // Save INTCON register contants
GIE = 0; // Disable interrupts, Next two lines SHOULD run without
// interrupts
EECON2=0x55;
EECON2=0xAA;
WR = 1; // begin write to internal EEPROM
INTCON = INTCON_SAVE; //Now we can safely enable interrupts if previously used
delay_cycles(1); // like NOP
while(!WR) // Wait till write operation complete
delay_cycles(1);
WREN=0; // Disable writes to EEPROM on write complete (EEIF flag on set PIR2 )
}
// This function reads data from address given in internal EEPROM of PIC
unsigned char internal_EEPROM_getc(unsigned char addr)
{
EEADR = addr;
EEPGD= 0; // 0 = Access data EEPROM memory
RD = 1; // EEPROM Read
return EEDATA; // return data
}
void main()
{
// IT WORKS!
internal_EEPROM_putc(0x12,0x34); //Write 0x34 to EEPROM address 0x12
delay_cycles(1);
c = internal_EEPROM_getc(0x12); // Read EEPROM address 0x12 in to variable C
if (c != 0x34) {
RC1 = ON; // activate a relay, if read and write mismatches
delay_ms(500);
RC1 = OFF;
} // there is no relay activation
while (TRUE) {
// IT DOESN'T WORK!
internal_EEPROM_putc(0x13,0x56); //Write 0x34 to EEPROM address 0x12
delay_cycles(1);
c = internal_EEPROM_getc(0x13); // Read EEPROM address 0x12 in to variable C
if (c != 0x56) {
RC1 = ON;
delay_ms(500);
RC1 = OFF;
} // no relay activation
}
}
Any thoughts?
FYI, I use C programming language (not assembly), I use CCS C compiler (PCWHD) (4.057)
under Windows 7 (32 and 64 bits) and my programmer is PICKKit 2).

UART DMA for varying sized arrays

Using MPLAB X 1.70 with a dsPIC33FJ128GP802 microcontroller.
I've got an application which is collecting data from two sensors at different sampling rates (one at 50Hz, the other at 1000Hz), both sensor packets are also different sizes (one is 5 bytes, the other is 21 bytes). Up until now I've used manual UART transmision as seen below:
void UART_send(char *txbuf, char size) {
// Loop variable.
char i;
// Loop through the size of the buffer until all data is sent. The while
// loop inside checks for the buffer to be clear.
for (i = 0; i < size; i++) {
while (U1STAbits.UTXBF);
U1TXREG = *txbuf++;
}
}
The varying sized arrays (5 or 21 bytes) were sent to this function, with their size, and a simple for loop looped through each byte and outputted it through the UART tx register U1TXREG.
Now, I want to implement DMA to relieve some pressure on the system when transmitting the large amount of data. I've used DMA for my UART receive and ADC, but having trouble with transmit. I've tried both ping pong mode on and off, and one-shot and continuous mode, but whenever it comes to sending the 21 byte packet it messes up with strange values and zero value padding.
I'm initialising the DMA as seen below.
void UART_TX_DMA_init() {
DMA2CONbits.SIZE = 0; // 0: word; 1: byte
DMA2CONbits.DIR = 1; // 0: uart to device; 1: device to uart
DMA2CONbits.AMODE = 0b00;
DMA2CONbits.MODE = 1; // 0: contin, no ping pong; 1: oneshot, no ping pong; 2: contin, ping pong; 3: oneshot, ping pong.
DMA2PAD = (volatile unsigned int) &U1TXREG;
DMA2REQ = 12; // Select UART1 Transmitter
IFS1bits.DMA2IF = 0; // Clear DMA Interrupt Flag
IEC1bits.DMA2IE = 1; // Enable DMA interrupt
}
The DMA interrupt I'm just clearing the flag. To build the DMA arrays I've got the following function:
char TXBufferADC[5] __attribute__((space(dma)));
char TXBufferIMU[21] __attribute__((space(dma)));
void UART_send(char *txbuf, char size) {
// Loop variable.
int i;
DMA2CNT = size - 1; // x DMA requests
if (size == ADCPACKETSIZE) {
DMA2STA = __builtin_dmaoffset(TXBufferADC);
for (i = 0; i < size; i++) {
TXBufferADC[i] = *txbuf++;
}
} else if (size == IMUPACKETSIZE) {
DMA2STA = __builtin_dmaoffset(TXBufferIMU);
for (i = 0; i < size; i++) {
TXBufferIMU[i] = *txbuf++;
}
} else {
NOTIFICATIONLED ^= 1;
}
DMA2CONbits.CHEN = 1; // Re-enable DMA2 Channel
DMA2REQbits.FORCE = 1; // Manual mode: Kick-start the first transfer
}
This example is with ping pong turned off. I'm using the same DMA2STA register but changing the array depending on which packet type I have. I'm determining the packet type from the data to be sent, changing the DMA bytes to be sent (DMA2CNT), building the array same as before with a for loop, then forcing the first transfer along with re-enabling the channel.
It takes much longer to process the data for the large data packet and I'm starting to think the DMA is missing these packets and sending null/weird packets in its place. It seems to be polling before I build the buffer and force the first transfer. Perhaps the force isn't necessary for every poll; I don't know...
Any help would be great.
After a few days of working on this I think I've got it.
The main issue I experienced was that the DMA interrupt was being polled faster than previous transmission, therefore I was only getting segments of packages before the next package overwrote the previous. This was solved with simply waiting for the end of UART transmission with:
while (!U1STAbits.TRMT);
I managed to avoid the redundancy of recreating a new DMA with the package data by simply making the original data array the one recognised by the DMA.
In the end the process was pretty minimal, the function called every time a package was created is:
void sendData() {
// Check that last transmission has completed.
while (!U1STAbits.TRMT);
DMA2CNT = bufferSize - 1;
DMA2STA = __builtin_dmaoffset(data);
DMA2CONbits.CHEN = 1; // Re-enable DMA0 Channel
DMA2REQbits.FORCE = 1; // Manual mode: Kick-start the first transfer
}
Regardless of what the size of the package, the DMA changes the amount it sends using the DMA2CNT register, then it's simply re-enabling the DMA and forcing the first bit.
Setting up the DMA was:
DMA2CONbits.SIZE = 1;
DMA2CONbits.DIR = 1;
DMA2CONbits.AMODE = 0b00;
DMA2CONbits.MODE = 1;
DMA2PAD = (volatile unsigned int) &U1TXREG;
DMA2REQ = 12; // Select UART1 Transmitter
IFS1bits.DMA2IF = 0; // Clear DMA Interrupt Flag
IEC1bits.DMA2IE = 1; // Enable DMA interrupt
Which is one-shot, no ping-pong, byte transfer, and all the correct parameters for UART1 TX.
Hope this helps someone in the future, the general principle can be applied to most PIC microcontrollers.

DMA interrupt for SPI

I'm trying to recreate a project of writing to an SD card (using FatFS) for a dsPIC33FJ128GP802 microcontroller.
Currently to collect the date from the SPI I have a do/while that loops 512 times and writes a dummy value to the SPI buffer, wait for the SPI flag, then read the SPI value, like so:
int c = 512;
do {
SPI1BUF = 0xFF;
while (!_SPIRBF);
*p++ = SPI1BUF;
} while (--c);
I'm trying to recreate this using the DMA intterupts but it's not working like I had hoped. I'm using one DMA channel, SPI is in 8 bit mode for the time being, so DMA is in byte mode, it's also in 'null write' mode, and continuous without ping pong. My buffers are only one member arrays and the DMA is matched.
DMA2CONbits.CHEN = 0; //Disable DMA
DMA2CONbits.SIZE = 1; //Receive bytes (8 bits)
DMA2CONbits.DIR = 0; //Receive from SPI to DMA
DMA2CONbits.HALF = 0; //Receive full blocks
DMA2CONbits.NULLW = 1; //null write mode on
DMA2CONbits.AMODE = 0; //Register indirect with post-increment
DMA2CONbits.MODE = 0; //continuous mode without ping-pong
DMA2REQbits.IRQSEL = 10; //Transfer done (SPI)
DMA2STA = __builtin_dmaoffset(SPIBuffA); //receive buffer
DMA2PAD = (volatile unsigned int) &SPI1BUF;
DMA2CNT = 0; //transfer count = 1
IFS1bits.DMA2IF = 0; //Clear DMA interrupt
IEC1bits.DMA2IE = 1; //Enable DMA interrupt
From what I understand from the null write mode, the DMA will write a null value every time a read is performed. However, the DMA wont start until an initial write is performed by the CPU, so I've used the manual/force method to start the DMA.
DMA1CONbits.CHEN = 1; //Enable DMA
DMA1REQbits.FORCE = 1; //Manual write
The interrupt will now start, and runs without error. However the code later shows that the collection was incorrect.
My interrupt is simple in that all I'm doing is placing the collected data (which I assume is placed in my DMAs buffer as allocated above) into a pointer which is used throughout my program.
void __attribute__((interrupt, no_auto_psv)) _DMA2Interrupt(void) {
if (RxDmaBuffer == 513) {
DMA2CONbits.CHEN = 0;
rxFlag = 1;
} else {
buffer[RxDmaBuffer] = SPI1BUF;
RxDmaBuffer++;
}
IFS1bits.DMA2IF = 0; // Clear the DMA0 Interrupt Flag
}
When the interrupt has run 512 times, I stop the DMA and throw a flag.
What am I missing? How is this not the same as the non-DMA method? Is it perhaps the lack of the while loop which waits for the completion of the SPI transmission (while (!_SPIRBF);). Unfortunately with the null write mode automatically sending and receiving the SPI data I can't manually put any sort of wait in.
I've also tried using two DMA channels, one to write and one to read, but this also didn't work (plus I need that channel later for when I come to proper writing to the SD card).
Any help would be great!

Resources