Unwanted Jump to interrupt MPLAB X IDE v.3.30 - c

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).

Related

I need to know why my interrupts are not working here

I am using stm8l - discovery and i have created a code for toggling a led for every 1 second using timer (TIM1) but this is not working properly. I am missing something here in my configuration
I could enter the interrupt function for the first time but after that it does not enter the interrupt function. Someone please look into and help me out
enter code here
#include <iostm8l.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "defs.h"
unsigned int count = 0;
#svlreg #interrupt void TIM1(void)
{
count += 1;
TIM1_SR1 &= ~(0x01);
}
main()
{
CLK_DIVR = 0x00; // Set the frequency to 16Mhz
CLK_PCKENR2 = 0x02; // clock for timer1
PC_DDR = 0x80; // direction output for led
PC_CR1 = 0x80; // fast push pull mode
PE_DDR = 0x80; // direction output for led
PE_CR1 = 0x80; // fast push pull mode
TIM1_PSCRH = 0x3e; //to create a frequency for 1000 hz
TIM1_PSCRL = 0x80; // so prescalar is 16000
TIM1_CR1 = 0x01;
TIM1_IER = 0x01;
_asm("rim\n");
while(1)
{
if (count == 1000)
{
PE_ODR ^= 0x80;
count = 0;
}
}
}
The interrupt enters only one time but after that it does not enter. So variable "count" remains at value 1
You are using magic numbers for the bit masks instead of defined constants, so the code is pretty damn hard to read for you and me both. Change this so that the code ends up like for example
TIM1_SR1 &= ~TIM1_SR1_UIF;
Since this is a 8 bit MCU it is also absolutely essential that you u suffix all integer contants, or they will be of type signed int, which you don't want.
For example this code TIM1_SR1 &= ~(0x01); is equivalent to TIM1_SR1 &= -2. Very easy to write accidental subtle bugs this way. I recommend studying Implicit type promotion rules.
It is highly recommended to disassemble every ISR you write to see what machine code you actually end up with, and single step it through the debugger while watching the register as well. This particular register seems to ignore having 1 written to it, so you could probably just do TIM1_SR = TIM1_SR1_UIF;. Incorrectly cleared timer flags inside ISRs is one of the most common bugs in embedded systems.
Quoting the manual:
UIF: Update interrupt flag
– At overflow or underflow if UDIS = 0 in the TIM1_CR1 register
– When CNT is re-initialized by software using the UG bit in TIM1_EGR register, if URS = 0 and UDIS = 0 in the TIM1_CR1 register.
– When CNT is re-initialized by a trigger event (refer to the TIM1_SMCR register description), if URS = 0 and UDIS = 0 in the TIM1_CR1 register
Your code doesn't appear to do any of this, so it is pretty safe to assume the timer counter isn't reset.
Another problem is that count must be declared as volatile or otherwise the compiler might optimize out this code completely:
if (count == 1000)
{
PE_ODR ^= 0x80;
count = 0;
}

DRDY PIC18F45K80 to QT1481

I also have a problem with DRDY. I need to include DRDY. The pins for DRDY are RD2 and RD5. They are both inputs.
Here is the information for DRDY.
DRDY Pin
DRDY is an open-drain output (in SPI mode) or bidirectional pin (in UART mode) with an internal 20 k – 50 k pullup
resistor.
Most communications failures are the result of failure to properly observe the DRDY timing.
Serial communications pacing is controlled by this pin. Use of DRDY is critical to successful communications with the
QT1481. In either UART or SPI mode, the host is permitted to perform a data transfer only when DRDY has returned
high. Additionally, in UART mode, the QT1481 delays responses to the host if DRDY is being held low by the host.
After each byte transfer, DRDY goes low after a short delay and remains low until the QT1481 is ready for another
transfer. A short delay occurs before DRDY is driven low because the QT1481 may otherwise be busy and requires
a finite time to respond.
DRDY may go low for a microsecond only. During the period from the end of one transfer until DRDY goes low and
back high again, the host should not perform another transfer. Therefore, before each byte transmission the host
should first check that DRDY is high again.
If the host wants to perform a byte transfer with the QT1481 it should behave as follows:
1. Wait at least 100 µs after the previous transfer (time S5 in Figure 3-2 on page 23: DRDY is guaranteed to go
low before this 100 µs expires).
2. Wait until DRDY is high (it may already be high).
3. Perform the next transfer with the QT1481.
In most cases it takes up to 3 ms for DRDY to return high again. However, this time is longer with some commands
or if the STS_DEBUG setup is enabled, as follows:
0x01 (Setups load): <20 ms
0x02 (Low Level Cal and Offset): <20 ms
Add 15 ms to the above times if the STS_DEBUG setup is enabled.
Other DRDY specifications:
Min time DRDY is low: 1 µs
Max time DRDY is low after reset: 100 ms
The timing diagram is this:
How can implement that?
The code I have written with my friend is written here:
#include <xc.h>
#include "PIC.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
//#include <pic18f45k80.h>
#define MSB 1
#define LSB 0
// SPI PIN CONFIGURATION
#define SCK_TRIS TRISCbits.TRISC3 = 0 ;
#define SDO_TRIS TRISCbits.TRISC5 = 0 ;
#define SDI_TRIS TRISCbits.TRISC4 = 1 ;
#define QTA_SS_TRIS TRISDbits.TRISD4 = 0 ;
#define QTB_SS_TRIS TRISEbits.TRISE2 = 0 ;
#define QTA_SS_LAT_LOW LATDbits.LATD4 = 0 ;
#define QTA_SS_LAT_HIGH LATDbits.LATD4 = 1 ;
#define QTB_SS_LAT_LOW LATEbits.LATE2 = 0 ;
#define QTB_SS_LAT_HIGH LATEbits.LATE2 = 1 ;
#define QTA_DRDY_TRIS TRISDbits.TRISD5 = 1 ;
#define QTB_DRDY_TRIS TRISDbits.TRISD2 = 1 ;
#define QTA_DRDY_LAT_LOW LATDbits.LATD5 = 0 ;
#define QTA_DRDY_LAT_HIGH LATDbits.LAT52 = 1 ;
#define QTB_DRDY_LAT_LOW LATDbits.LAT25 = 0 ;
#define QTB_DRDY_LAT_HIGH LATDbits.LATD2 = 1 ;
#define QTB_DRDY PORTDbits.RD2 ;
#define QTA_DRDY PORTDbits.RD5 ;
// FREQUENCY SELECT
#define _XTAL_FREQ 16000000
// PIN SETUP
void PIN_MANAGER_Initialize(void)
{
/**
LATx registers
*/
LATE = 0x00;
LATD = 0x00;
LATA = 0x00;
LATB = 0b00010000;
LATC = 0x00;
/**
TRISx registers
*/
TRISE = 0x00;
TRISA = 0x08;
TRISB = 0x01;
TRISC = 0b00010000;
TRISD = 0xEF;
PORTC = 0b00010010 ;
/**
ANSELx registers
*/
ANCON0 = 0x00;
ANCON1 = 0x00;
/**
WPUx registers
*/
WPUB = 0x00;
INTCON2bits.nRBPU = 1;
}
// SPI
void SPI_Initialize(void)
{
// SMP Middle; CKE Idle to Active;
SSPSTAT = 0b00000000;
// SSPEN enabled; WCOL no_collision; CKP Idle:High, Active:Low; SSPM FOSC/4; SSPOV no_overflow;
SSPCON1 = 0b00111010;
// SSPADD 0;
SSPADD = 0x00;
ADCON0 = 0 ;
ADCON1 = 0x0F ; //Makes all I/O digital
SCK_TRIS ;
SDO_TRIS ;
SDI_TRIS ;
QTA_SS_TRIS ;
QTB_SS_TRIS ;
QTA_DRDY_TRIS ;
QTB_DRDY_TRIS ;
}
signed char WriteSPI( unsigned char data_out )
{
unsigned char TempVar;
TempVar = SSPBUF; // Clears BF
PIR1bits.SSPIF = 0; // Clear interrupt flag
SSPCON1bits.WCOL = 0; //Clear any previous write collision
SSPBUF = data_out; // write byte to SSPBUF register
if ( SSPCON1 & 0x80 ) // test if write collision occurred
return ( -1 ); // if WCOL bit is set return negative #
else
while( !PIR1bits.SSPIF ); // wait until bus cycle complete
return ( 0 ); // if WCOL bit is not set return non-negative#
}
unsigned char ReadSPI( void )
{
unsigned char TempVar;
TempVar = SSPBUF; // Clear BF
PIR1bits.SSPIF = 0; // Clear interrupt flag
SSPBUF = 0x00; // initiate bus cycle
while(!PIR1bits.SSPIF); // wait until cycle complete
return ( SSPBUF ); // return with byte read
}
unsigned char DataRdySPI( void )
{
if ( SSPSTATbits.BF )
return ( +1 ); // data in SSPBUF register
else
return ( 0 ); // no data in SSPBUF register
}
// SOFTWARE EUART
void out_char(char character, char bit_order){
uint8_t i = 0;
RSOUT = 1 ; // MSB
__delay_ms(1);
RSOUT = 0 ; // START
__delay_us(100);
for (i = 8; i>0; --i){
if (bit_order){ // Bit order determines how you will put the bits, from left to right (MSB) or right to left (LSB)
RSOUT = (character & 0x80) ? 1:0; // in MSB you compare the left-most bit doing an AND with 0x80, and put 1 if true, 0 elsewhere.
character <<= 1; // Shift the character to the left, discrading the bit just sent
} else {
RSOUT = (character & 0x01); // in LSB you compare the right-most bit doing an AND with 0x01, and put 1 if true, 0 else.
character >>= 1; // Shift the character to the right, discrading the bit just sent
}
__delay_us(100);
}
RSOUT = 1 ; // STOP
}
void out_str(char * string, uint8_t len, char bit_order){
uint8_t i = 0;
for (i = 0; i< len; i++){
out_char(string[i], bit_order);
}
}
void SYSTEM_Initialize(void)
{
PIN_MANAGER_Initialize() ;
SPI_Initialize() ;
}
void main(void)
{
SYSTEM_Initialize() ;
while (1)
{
QTB_SS_LAT_LOW ; // Transmit data
char temp ;
WriteSPI(0x0F) ; // Send a byte
while(!DataRdySPI()) ; // wait for a data to arrive
temp = ReadSPI(); // Read a byte from the
QTB_SS_LAT_HIGH ; // Stop transmitting data
__delay_us(100) ;
}
}
No. Do not just write a bunch of code, then see what it does. That kind of shotgun (or, if you prefer, spaghetti-to-the-wall) approach is a waste of effort.
First, drop all those macros. Instead, write comments that describe the purpose of each chunk of code, like the first three assignments in your SPI_Initialize() function.
Next, convert your specification to pseudocode. The format does not matter much, just use something that lets you keep your mind focused on what the purpose is, rather than on the details on how to do it.
The datasheet says that with SPI, there are three outputs from the PIC (^SS, SCK, MOSI on the QT1481), and two inputs (^DRDY and MISO on the QT1481). I'll use those names for the data lines, and for their respective I/O pin names on the PIC.
The setup phase on the PIC should be simple:
Make ^DRDY an input
Make ^SS an output, set it HIGH
Make SCK an output, set it LOW
Make MOSI an output, set it LOW
Make MISO an input
Set up SPI using SCK, MOSI, MISO
Each transfer is a bidirectional one. Whenever you send data, you also receive data. The zero command is reserved for receiving multiple data, says the datasheet. So, you only need a function that sends a byte, and at the same time receives a byte:
Function SPITransfer(command):
Make sure at least 0.1ms has passed since the previous transfer.
Do:
Nothing
While (^DRDY is LOW)
Set ^SS LOW
response = Transfer(command)
Set ^SS HIGH
Return response
End Function
As I understand it, for PICs and properly initialized hardware SPI the response = Transfer(command) line is in C
SSPBUF = command;
while (!DataRdySPI())
;
response = SSPBUF;
You can also bit-bang it, in which case it is (in pseudocode):
response = 0
For bit = 7 down to 0, inclusive:
If (command & 128):
Set MOSI high
Else:
Set MOSI low
End If
Set SCK low
Sleep for a half period
command = command / 2
response = response * 2
If MISO high:
response = response + 1
End If
Set SCK high
Sleep for a half period
End For
but obviously the hardware SPI approach is better.
(When you get this working, you can use the hardware SPI without a wait loop from a timer interrupt, making the communications essentially transparent to the "main operation" of the PIC microcontroller. That requires a slightly different approach, with a command and response queues (of a few bytes), but will make it much easier for the PIC to do actual work, other than just scan the QT1481.)
After a reset, you essentially send 0x0F until you get 0xF0 back:
while (SPITransfer(0x0F) != 0xF0)
;
At this point, you have the steps you need to implement in C. OP also has the hardware (an oscilloscope) to verify their code works.

Without using interrupts produce a rising sawtooth waveform

Here I need to output a waveform on oscilloscope in C which should in a rising sawtooth waveform.I am not sure if my code is correct. Any help or suggestions?
while(1)
{
for (i = 1; i < 360; i++);
// Check to see if status.TRDY is 1
while(*(base+2) & 0x40 != 1);
// while shift register is not empty
// Make the sawtooth pattern
if (saw == 0x1fff){
saw = 0x1000;
}
else {
saw = saw+1;
}
// transmit sawtooth to the oscilloscope
*(base+1) = saw;
}
This only tidies up the OP's posted code. It does not answer how to program the DAC. OP is using a 16-bit amplitude value but his register addressing suggests 8-bit registers - perhaps two writes are needed.
I suggest you also need function arguments defining the period of the sawtooth wave, and the number of steps. You also need an exit condition. I leave these points to you.
#Chris Stratton also commented that the I/O port should be the correct language type.
#define MINSAW 0x1000
#define MAXSAW 0x1FFF
unsigned *base = (unsigned *)0xD000; // "insert your value"
int main() {
unsigned saw, i;
while(1) {
for (i = 0; i < 360; i++) {
// ratio the waveform amplitude
saw = MINSAW + i * (MAXSAW - MINSAW) / 359;
// Check to see if status.TRDY is 1
while((*(base+2) & 0x40) != 0x40);
// transmit sawtooth to the oscilloscope
*(base+1) = saw;
}
}
return 1;
}

Multiplexing seven segment display on AVR Atmega324A

I've been stuck on this all day, I'm trying to create a count down timer using two seven segment displays. I want it to start at 20 and count down to zero. While 10< I only want to have the left display on(i.e no 0 in the tens place). I'm using an Atmega 324A. I have all of port C connected to the display segments and am using PIND0 to toggle between the two. Here is what I have so far.
#include <avr/io.h>
#include<util/delay.h>
int main(void) {
int prescale = (8000000/8)/1000-1;
int digit = 1;
uint8_t display;
int seven_seg = {0x3F,0X06,0X5B,0X4F,0X66,0X6D,0X7D,0C07,0X7F,0X6F};
// Set OC1 to output
DDRD = (1<<0);
DDRC = 0xFF;
OCR1A = prescale;
//clear counter on compare match
TCCR1A = (0<<COM1A1) | (1<<COM1A0);
//Set Prescale and CTC Mode
TCCR1B = (0<<CS12) | (1<<CS11) | (0<<CS10) | (0<<WGM13) | (1<<WGM12);
while(1) {
display++;
if(display>50) display = 0;
for (i = 250; i>0; i--){
PORTD ^= 0<<PIND0;
PORTC = seven_seg[display%10];
PORTD ^= 1<<PIND0;
_delay_ms(100);
for (i = 250; i>0; i--){
PORTD ^= 1<<PIND0;
PORTC = seven_seg[display/10];
PORTD ^= 0<<PIND0;
_delay_ms(100);
}
}
while((TIFR1 & (1<<OCF1A)) == 0) {}
TIFR1 &= (1 << OCF1A);
}
}
All this does is set both displays to 0. Do I need another for loop to iterate through the seven_seg[] array while it's doing this? really not sure how to tackle this one. Any help would be great.
you make 2 big faults:
you don't use timer
you should separate display-driving-logic from value-generating-logic
best thing would be you split the tasks and plan how to implement this.
Task one: providing the data to display
Task two: transfering that data to a display friendly representation
Task three: the aktual displaying of that data
Task one is Easy. lets assume you want to display integers and you have three 7-seg-disps.
So task one is to provide some Data to display.
int16_t numberToDisplay = 234;
Task two is also not that hard. a display friendly representation would be one byte per display element.
#define NUM_7SEGS 3
volatile uint8_t dispData[NUM_7SEGS]; // volatile since it is be accassed by different contexts
now we need some mechanism that transfers the input value to the display data
void val2DispData(int16 val)
{
uint8_t i;
for(i=NUM_7SEGS; i; --i){
uint8_t r = (uint8_t)(val%10);
val /= 10
dispData[i-1] = seven_seg[r];
}
}
fine and now?
Task three is the most difficult one. we need someone who says the output what to do.
Since the want to multiplex the 3 display elements that means:
deactivate the current display element
put the data of the next digit to the outport
activate the next display element
wait a bit.
and this 4 steps we want to do very fast so that the observer does not recognize that only one element is active at a time.
since this is totally independent of the other program logic, we need to do this in "background".
your main program flow simply calls that function and the background timer ISR worrries about displaying.
So we have to set up a timer and call the switching of element data in its interrupt service routine. (for setting up timer and timer interupts please refer another tutorial)
// this have to be called cyclic from timer isr
// frequency is not that important but should be at
// least NUM_7SEGS * 200 Hz to not look ugly
void cyclicDisplayTask()
{
static uint8_t currentElement = 0;
// disable all elements
PORTD = 0;
// put data on the port
PORTC = dispData[currentElement]; // this is why the volatile is necessary. without the compiler would not notice that values may be changed by the main program flow
// enable next element
PORTD = (1<<currentElement);
++currentElement;
if(currentElement>=NUM_7SEGS){
currentElement = 0;
}
}
of course you have to adapt the enabling of specific display elements to your hardware.
please also note that you may youse a transistor to drive the element. an AVR port pin is strong enogh to drive a single segment but the other side that drives the common anodes/cathodes of the segment may be overload. This of course depends on the leds within the segments. if this are low current leds (~2mA) it is ok.

How do you write and read to memory with PIC18?

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.

Resources