Hello I am learning how to use the Uart by using interrupts in Nios and I am not sure how to start. I have made it in polling, but I am not sure how to start using interrupts.
Any help would be appreciated
Here is my code
#include <stdio.h> // for NULL
#include <sys/alt_irq.h> // for irq support function
#include "system.h" // for QSYS defines
#include "nios_std_types.h" // for standard embedded types
#define JTAG_DATA_REG_OFFSET 0
#define JTAG_CNTRL_REG_OFFSET 1
#define JTAG_UART_WSPACE_MASK 0xFFFF0000
#define JTAG_UART_RV_BIT_MASK 0x00008000
#define JTAG_UART_DATA_MASK 0x000000FF
volatile uint32* uartDataRegPtr = (uint32*)JTAG_UART_0_BASE;
volatile uint32* uartCntrlRegPtr = ((uint32*)JTAG_UART_0_BASE +
JTAG_CNTRL_REG_OFFSET);
void uart_SendByte (uint8 byte);
void uart_SendString (uint8 * msg);
//uint32 uart_checkRecvBuffer (uint8 *byte);
uint32 done = FALSE;
void uart_SendString (uint8 * msg)
{
int i = 0;
while(msg[i] != '\0')
{
uart_SendByte(msg[i]);
i++;
}
} /* uart_SendString */
void uart_SendByte (uint8 byte)
{
uint32 WSPACE_Temp = *uartCntrlRegPtr;
while((WSPACE_Temp & JTAG_UART_WSPACE_MASK) == 0 )
{
WSPACE_Temp = *uartCntrlRegPtr;
}
*uartDataRegPtr = byte;
} /* uart_SendByte */
uint32 uart_checkRecvBuffer (uint8 *byte)
{
uint32 return_value;
uint32 DataReg = *uartDataRegPtr;
*byte = (uint8)(DataReg & JTAG_UART_DATA_MASK);
return_value = DataReg & JTAG_UART_RV_BIT_MASK;
return_value = return_value >> 15;
return return_value;
} /* uart_checkRecvBuffer */
void uart_RecvBufferIsr (void* context)
{
} /* uart_RecvBufferIsr */
int main(void)
{
uint8* test_msg = (uint8*)"This is a test message.\n";
//alt_ic_isr_register ( ); // used for 2nd part when interrupts are enabled
uart_SendString (test_msg);
uart_SendString ((uint8*)"Enter a '.' to exist the program\n\n");
while (!done)
{
uint8 character_from_uart;
if (uart_checkRecvBuffer(&character_from_uart))
{
uart_SendByte(character_from_uart);
}
// do nothing
} /* while */
uart_SendString((uint8*)"\n\nDetected '.'.\n");
uart_SendString((uint8*)"Program existing....\n");
return 0;
} /* main */
I am suppose to use the uart_RecvBufferIsr instead of uart_checkRecvBuffer. How can tackle this situation?
You will need to register your interrupt handler by using alt_ic_isr_register(), which will then be called when an interrupt is raised. Details can be found (including some sample code) in this NIOS II PDF document from Altera.
As far as modifying your code to use the interrupt, here is what I would do:
Remove uart_checkRecvBuffer();
Change uart_RecvBufferIsr() to something like (sorry no compiler here so can't check syntax/functioning):
volatile uint32 recv_flag = 0;
volatile uint8 recv_char;
void uart_RecvBufferIsr(void *context)
{
uint32 DataReg = *uartDataRegPtr;
recv_char = (uint8)(DataReg & JTAG_UART_DATA_MASK);
recv_flag = (DataReg & JTAG_UART_RV_BIT_MASK) >> 15;
}
The moral of the story with the code above is that you should keep your interrupts as short as possible and let anything that is not strictly necessary to be done outside (perhaps by simplifying the logic I used with the recv_char and recv_flag).
And then change your loop to something like:
while (!done)
{
if (recv_flag)
{
uart_SendByte(recv_byte);
recv_flag = 0;
}
}
Note that there could be issues with what I've done depending on the speed of your port - if characters are received too quickly for the "while" loop above to process them, you would be losing some characters.
Finally, note that I declared some variables as "volatile" to prevent the compiler from keeping them in registers for example in the while loop.
But hopefully this will get you going.
Related
I am reading the data from a "Torque Wrench" using "USB Host Shield2.0" and Arduino UNO. I am receiving correct data from my "Torque Wrench" Data is receiving in a array.
But when I started reading data after "for" loop inside Void loop() I am receiving incorrect data. I attached Both output pictures correct and incorrect data.
Basically I am read data from Torque Wrench and send to receiver using Nrf24l01. I am receiving incorrect data.
My question is :- Why I am reading Incorrect data outside "for" loop.
Correct Data inside "for" loop :- enter image description here
Incorrect Data outside "for" loop :- enter image description here
#include <SPI.h> // for SPI communication
#include <nRF24L01.h>
#include <RF24.h>
#include <cdcacm.h>
#include <usbhub.h>
//#include "pgmstrings.h"
// Satisfy the IDE, which needs to see the include statment in the ino too.
#ifdef dobogusinclude
#include <spi4teensy3.h>
#endif
#include <SPI.h>
RF24 radio(7, 8); // CE, CSN
const byte address[6] = {'R','x','A','A','A','B'}; // the address the the module
class ACMAsyncOper : public CDCAsyncOper
{
public:
uint8_t OnInit(ACM *pacm);
};
uint8_t ACMAsyncOper::OnInit(ACM *pacm)
{
uint8_t rcode;
// Set DTR = 1 RTS=1
rcode = pacm->SetControlLineState(3);
if (rcode)
{
ErrorMessage<uint8_t>(PSTR("SetControlLineState"), rcode);
return rcode;
}
LINE_CODING lc;
lc.dwDTERate = 9600;
lc.bCharFormat = 0;
lc.bParityType = 0;
lc.bDataBits = 8;
rcode = pacm->SetLineCoding(&lc);
if (rcode)
ErrorMessage<uint8_t>(PSTR("SetLineCoding"), rcode);
return rcode;
}
USB Usb;
//USBHub Hub(&Usb);
ACMAsyncOper AsyncOper;
ACM Acm(&Usb, &AsyncOper);
void setup() {
Serial.begin(9600);
radio.begin();
radio.openWritingPipe(address);
radio.setPALevel(RF24_PA_MAX);
radio.stopListening();
#if !defined(__MIPSEL__)
while (!Serial);
#endif
Serial.println("Start");
if (Usb.Init() == -1)
Serial.println("USB Not Connected");
delay( 200 );
}
void loop() {
Usb.Task();
if( Acm.isReady()) {
uint8_t rcode;
/* reading the keyboard */
if(Serial.available()) {
uint8_t data= Serial.read();
/* sending to the phone */
rcode = Acm.SndData(1, &data);
if (rcode)
ErrorMessage<uint8_t>(PSTR("SndData"), rcode);
}
delay(10);
uint8_t buf[64];
uint16_t rcvd = 64;
char text[64];
rcode = Acm.RcvData(&rcvd, buf);
if (rcode && rcode != hrNAK)
ErrorMessage<uint8_t>(PSTR("Ret"), rcode);
if ( rcvd ) {
for(uint16_t i=0; i < rcvd; i++ )
{
// Serial.print((char)buf[i]); // correct Data read from torque wrench
text[i] = (char)buf[i];
}
Serial.println(text); // reading wrong data here
//radio.write(&text, sizeof(text));
//Serial.println(text);
}
delay(10);
}
}
Character arrays must be null-terminated to count as C strings.
After the for loop, add text[rcvd] = '\0';
Also, your rcvd is fixed at 64. It needs to be one less than the array size for the null terminator to fit.
I am currently using "STM32F429I-DISC1" with joystick. I am trying to draw something on the LCD screen and using joystick move this object. My drawing is working fine, but I have the error: " void value not ignored as it ought to be".
This two lines have problems...
localX = Joy_ReadXY(CTRL_REG_IN3);
localY = Joy_ReadXY(CTRL_REG_IN4);
Can someone please tell me, how I can fix this error?
And why I see this error?
Main.c
#include "stm32f429i_discovery_lcd.h"
#define CTRL_REG_IN3 0b00011000
#define CTRL_REG_IN4 0b00100000
SemaphoreHandle_t xMutex;
Joystick_data xy;
void vTaskFunction1(void *pvParameters) {
uint16_t localX;
uint16_t localY;
for(;;) {
localX = Joy_ReadXY(CTRL_REG_IN3);
localY = Joy_ReadXY(CTRL_REG_IN4);
xSemaphoreTake( xMutex, portMAX_DELAY );
xy.x = localX;
xy.y = localY;
xSemaphoreGive( xMutex );
HAL_Delay(10);
}
}
void vTaskFunction2(void *pvParameters) {
uint32_t xCoord = 240/2;
uint32_t yCoord = 320/2;
uint8_t reads = 0;
uint8_t ballRadius = 5;
uint16_t xLimitMin = ballRadius+25;
uint16_t xLimitMax = 240-ballRadius-25;
uint16_t yLimitMin = ballRadius+25;
uint16_t yLimitMax = 320-ballRadius-25;
for(;;) {
xSemaphoreTake( xMutex, portMAX_DELAY );
if (xy.x > 3000 && !(xCoord < xLimitMin))
xCoord -= 5;
if (xy.x < 1000 && !(xCoord > xLimitMax))
xCoord += 5;
if (xy.y > 3000 && !(yCoord < yLimitMin))
yCoord -= 5;
if (xy.y < 1000 && !(yCoord > yLimitMax))
yCoord += 5;
reads++;
BSP_LCD_Clear(LCD_COLOR_WHITE);
BSP_LCD_DrawCircle(xCoord, yCoord, ballRadius);
BSP_LCD_FillCircle(xCoord, yCoord, ballRadius);
xSemaphoreGive(xMutex);
HAL_Delay(20);
}
}
int main(void)
{
HAL_Init();
SystemClock_Config();
MX_GPIO_Init();
MX_SPI4_Init();
MX_TIM1_Init();
MX_USART1_UART_Init();
// LCD Things
BSP_LCD_Init();
BSP_LCD_LayerDefaultInit(1, LCD_FRAME_BUFFER);
BSP_LCD_SelectLayer(1);
BSP_LCD_SetBackColor(LCD_COLOR_WHITE); // Vali meelepärane värv
BSP_LCD_Clear(LCD_COLOR_WHITE);
BSP_LCD_SetTextColor(LCD_COLOR_DARKBLUE); // Vali meelepärane värv
MX_FREERTOS_Init();
if ( xMutex == NULL )
{
xMutex = xSemaphoreCreateMutex();
if ( ( xMutex ) != NULL )
xSemaphoreGive( ( xMutex ) );
}
xTaskCreate(vTaskFunction1, "Task 1", 100, NULL, 1, NULL);
xTaskCreate(vTaskFunction2, "Task 2", 100, NULL, 1, NULL);
vTaskStartScheduler();
osKernelStart();
while (1)
{
}
}
Read joystick function (joystick.c)
#include <stdio.h>
#include <main.h>
#include "gpio.h"
#include "spi.h"
#define READ_SLAVE_OPERATION 0b10000000
#define READ_INCR_SLAVE_OPERATION 0b11000000
#define WRITE_SLAVE_OPERATION 0b00000000
#define CTRL_REG_IN3 0b00000011
#define CTRL_REG_IN4 0b00000100
#define OUT_X_L 0x28
#define OUT_X_H 0x29
#define OUT_Y_L 0x2A
#define OUT_Y_H 0x2B
#define OUT_Z_L 0x2C
#define OUT_Z_H 0x2D
#define JOY_CS_LOW() HAL_GPIO_WritePin(JOY_CS_GPIO_PORT, JOY_CS_PIN, 0)
#define JOY_CS_HIGH() HAL_GPIO_WritePin(JOY_CS_GPIO_PORT, JOY_CS_PIN, 1)
#define JOY_CS_GPIO_PORT GPIOC
#define JOY_CS_PIN GPIO_PIN_13
int16_t Joy_ReadXY(uint8_t reg1){
uint8_t pTxData1[2] = {reg1, 0};
uint8_t pRxData1[2] = {0, 0};
JOY_CS_LOW();
HAL_SPI_TransmitReceive(&hspi4, pTxData1, pRxData1, 2, HAL_MAX_DELAY);
JOY_CS_HIGH();
return pRxData1[0] << 8 | pRxData1[1];
}
Here, in Main.c, you call the function before telling the compiler about what parameters and what return value types it has.
localX = Joy_ReadXY(CTRL_REG_IN3);
localY = Joy_ReadXY(CTRL_REG_IN4)
That confused the compiler and it starts "guessing" about them.
Guessing that it is a void-returning function, the compiler then complains that you are expecting a return value from a function which does return void i.e. nothing.
The returned void should be ignored, instead of attempting to write it to a variable. At least that is what the compiler thinks...
To fix it, you should explain to the compiler that there is a function elsewhere, with name, parameters and return value type. That is done by providing the prototype
int16_t Joy_ReadXY(uint8_t reg1);
It needs to be done before the function body in which the the extern function is first called. (And you already confirmed in comments that it fixes the described problem in your code.)
Note that for the other shown functions this is not needed, because they are defined (with head and body) before they are called.
Similar for other functions, which have their prototype provided in the header you include early on.
Actually, putting the prototype of your function into a header and including that similarily would be the best way to solve this.
does anyone know about an elegant (efficient) alternative to using large global arrays in C for an embedded system, whereby the array is written to in an interrupt service routine and it is read elsewhere asynchronously:
I have no issues with the current implementation, however I was just wondering if it is the best option.
for example:
uint8_t array_data[20] = {0};
volatile bool data_ready = false;
someIsr(void){
for(uint8_t i = 0; i < 20; i++){
array_data[i] = some_other_data[i];
}
data_ready = true;
}
main(void){
for(;;){
if(data_ready){
write_data_somewhere(&array_data[0]);
data_ready = false;
}
}
}
Thanks
Using global arrays is often the best approach unless one would need to use the storage for other purposes when the interrupt routine isn't running. An alternative is to use a global pointer to data that may be stored elsewhere, but one must be very cautious changing that pointer while interrupts are enabled.
An important caveat with your code, by the way: although the Standard regards the implications of a volatile qualifier as implementation-defined, allowing for the possibility that implementations may treat a volatile write as a potential "memory clobber", the authors of gcc require the use of compiler-specific intrinsics to prevent operations on "ordinary" objects from being reordered across operations on volatile-qualified ones.
For example, given:
volatile unsigned short out_count;
int *volatile out_ptr;
int buffer[10];
__attribute__((noinline))
void do_write(int *p, unsigned short count)
{
__asm("");
out_ptr = p;
out_count = count;
do {} while(out_count);
__asm("");
}
void test(void)
{
buffer[0] = 10;
buffer[1] = 20;
do_write(buffer, 2);
buffer[0] = 30;
buffer[1] = 40;
buffer[2] = 50;
do_write(buffer, 3);
}
because the __asm intrinsics don't use gcc-specific syntax to indicate that they might "clobber" the contents of memory in ways the compiler can't understand (even though many compilers support the use of empty __asm intrinsics for that express purpose, and such intrinsics wouldn't really serve any other purpose), and because gcc can see that there's no way that do_write could alter the contents of buffer, it "optimizes out" the code that would store the values 10 and 20 into buffer before the first call to do_write.
Clang doesn't seem quite as bad as gcc. It doesn't seem to reorder writes across volatile writes, it seems to refrain from reordering reads across functions that are not in-line expanded, and it seems to treat empty asm directives as potential memory clobbers, but I I'm not familiar enough with its documentation to know whether such restraint is by design, or merely a consequence of "missed optimizations" which might be "fixed" in future versions.
Consider using translation unit scope rather than global scope. That is declare the array static in the translation unit in which it is used. That translation unit should contain in this case the ISR that writes the data and an access function to read the data. Anything else, including main() should be in other translation units in order that that do not have direct access to the array:
#include <stdbool.h>
#include <stdint.h>
static volatile uint8_t array_data[DATA_LEN] = {0};
static volatile bool data_ready = false;
void someIsr(void)
{
for(uint8_t i = 0; i < 20; i++)
{
array_data[i] = some_other_data[i];
}
data_ready = true;
}
bool getdata( char* dest )
{
bool new_data = data_ready ;
if( data_ready )
{
memcpy( desr, array_data, sizeof(array_data) ) ;
data_ready = false ;
}
}
Then main() in some other translation unit might have:
#include "mydevice.h"
int main( void )
{
uint8_t somewhare[DATA_LEN] = {0};
for(;;)
{
if( getdata( somewhere ) )
{
// process new data
}
}
}
The above is based on your example, and the aim here is to isolate the array so that outside of the ISR the access is enforced to be read-only. In practice it is likely that you will need a "safer" data structure or access method such as a critical-section, double-buffering or a ring buffer so that the data can be accessed without risk of it being modified while it is being read.
This is no less efficient that your original global access, it is simply a restriction of the visibility and accessibility of the array.
As I mentioned in my top comment, one of best ways is to implement a ring queue.
Although I done a few ring queue implementations, here's one I just cooked up for illustration purposes. It is a [cheap] simulation of an Rx ISR for a uart [which is fairly common in embedded systems].
It is fairly complete, but I've not debugged it, so it may have some issues with the queue index calculations.
Anyway, here's the code:
// queue.c -- a ring queue
#include <stdlib.h>
#include <unistd.h>
enum {
QMAX = 1024
};
typedef unsigned char qdata_t; // queue data item
typedef struct {
int qenq; // index for enqueue
int qdeq; // index for dequeue
int qmax; // maximum number of elements in queue
int qover; // number of queue overflows
qdata_t *qbuf; // pointer to queue's buffer
} queue_t;
queue_t *rxisr_q; // pointer to Rx qeueue
// cli -- disable interrupts
void
cli(void)
{
}
// sti -- enable interrupts
void
sti(void)
{
}
// uart_ready -- uart is ready (has Rx data available)
int
uart_ready(void)
{
int rval = rand();
rval = ((rval % 100) > 95);
return rval;
}
// uart_getc -- get character from uart receiver
int
uart_getc(void)
{
int rval = rand();
rval &= 0xFF;
return rval;
}
// qwrap -- increment and wrap queue index
int
qwrap(queue_t *que,int qidx,int inc)
{
int qmax = que->qmax;
qidx += inc;
if (inc > 0) {
if (qidx >= qmax)
qidx -= qmax;
}
else {
if (qidx < 0)
qidx += qmax;
}
return qidx;
}
// qavail_total -- total amount of space available (for enqueue)
int
qavail_total(queue_t *que)
{
int qlen;
qlen = que->qdeq - que->qenq;
if (qlen < 0)
qlen += que->qmax;
qlen -= 1;
return qlen;
}
// qavail_contig -- total amount of space available (for enqueue) [contiguous]
int
qavail_contig(queue_t *que)
{
int qlen;
qlen = que->qdeq - que->qenq;
if (qlen < 0)
qlen = que->qmax - que->qenq;
qlen -= 1;
return qlen;
}
// qready_total -- total amount of space filled (for dequeue)
int
qready_total(queue_t *que)
{
int qlen;
qlen = que->qenq - que->qdeq;
if (qlen < 0)
qlen += que->qmax;
return qlen;
}
// qready_contig -- total amount of space filled (for dequeue) [contiguous]
int
qready_contig(queue_t *que)
{
int qlen;
qlen = que->qenq - que->qdeq;
if (qlen < 0)
qlen = que->qmax - que->qdeq;
return qlen;
}
// qfull -- is queue full?
int
qfull(queue_t *que)
{
int next;
next = qwrap(que,que->qenq,1);
return (next == que->qdeq);
}
// qpush -- push single value
int
qpush(queue_t *que,qdata_t chr)
{
int qenq = que->qenq;
int qnxt;
int push;
qnxt = qwrap(que,qenq,1);
push = (qnxt != que->qdeq);
if (push) {
que->qbuf[qenq] = chr;
que->qenq = qnxt;
}
return push;
}
// qalloc -- allocate a queue
queue_t *
qalloc(int qmax)
{
queue_t *que;
que = calloc(1,sizeof(*que));
que->qbuf = calloc(qmax,sizeof(qdata_t));
return que;
}
// uart_rx_isr -- ISR for uart receiver
void
uart_rx_isr(void)
{
int chr;
queue_t *que;
que = rxisr_q;
while (uart_ready()) {
chr = uart_getc();
#if 0
if (qfull(que)) {
++que->qover;
break;
}
#endif
if (! qpush(que,chr)) {
++que->qover;
break;
}
}
}
int
main(int argc,char **argv)
{
int qlen;
int qdeq;
queue_t *que;
rxisr_q = qalloc(QMAX);
que = rxisr_q;
while (1) {
cli();
qlen = qready_contig(que);
if (qlen > 0) {
qdeq = que->qdeq;
write(1,&que->qbuf[qdeq],qlen);
que->qdeq = qwrap(que,qdeq,qlen);
}
sti();
}
return 0;
}
In my project I'm using a global variable but it's not working as expected because it is initialized everytime it's executed and honestly I don't know what could be going on.
The variable is cookingSignalReceived.
The program is structured as follows:
//File Controller.c:
while (1)
{
Controller_Run_State_Machine();
}
void Controller_Run_State_Machine(void)
{
/* start of activity code */
Inputs_ReadSensors();
Comms_CheckReceivedData();
Controller_UpdateSTM();
}
The problem is inside Comms file:
//File Comms.c
uint8_t cookingSignalReceived = 0;
void Comms_CheckReceivedData(void)
{
/* start of activity code */
uint8_t uartDataAvailable = Comms_R_UART0_checkIfDataAvailable();
if (uartDataAvailable == 1)
{
Comms_ParseReceivedCommand();
}
}
void Comms_ParseReceivedCommand(void)
{
/* start of activity code */
/* UserCode{499E2AA6-1F61-4753-9221-77F85E7B5D92}:YjMeKqu95e */
uint8_t CRC_check_OK = 0;
uint8_t* buffer;
/* UserCode{499E2AA6-1F61-4753-9221-77F85E7B5D92} */
Comms_R_UART0_resetFlag_dataAvailable();
buffer = Comms_R_UART0_getBuffer();
CRC_check_OK = Comms_crcCheck(buffer);
if (CRC_check_OK == 1)
{
Comms_processMessage(buffer); //<-- Variable is used in this function
}
}
Global variable is used inside Comms_processMessage(). The issue is that every time that the function is called, the global variable is set to the initial value. Do you find anything strange here?
EDITED:
void Comms_processMessage(uint8_t* buffer)
{
/* UserCode{BCB3B791-2DF9-492b-B53B-6FEB24BD8F77}:eyCoSfmCKb */
uint8_t message = buffer[0];
uint8_t param1 = buffer[1];
uint8_t param2 = buffer[2];
//---------------------------------------------------------------------
// START COOKING 1ST STEP REQUEST
//---------------------------------------------------------------------
if (message == MSG_COOK_1ST && param1 == START_PARAM)
{
// Wait for second frame
cookingSignalReceived = 1;
#ifdef DEBUG
R_UART0_Send("Cook 1st step!!", sizeof(char) * 15);
#endif
}
//---------------------------------------------------------------------
// START COOKING 2ND STEP REQUEST
//---------------------------------------------------------------------
else if (message == MSG_COOK_2ND && param1 == START_PARAM)
{
// Wait for second frame
if (cookingSignalReceived == 1)
{
Controller_signalsBufferEnqueue(cookingSignal);
}
#ifdef DEBUG
R_UART0_Send("Cook 2nd step!!", sizeof(char) * 15);
#endif
}
}
Note that my original idea was to use a local static variable but I was having the same issue so I tried with a global variable.
In certain cases the MCUs restart because unrecoverable errors or bad hardware settings. This should be the problem! A cause of this MCUs behaviour may be also bad pointers management.
I have PIC18F87J11 with 25LC1024 external EEPROM, and I would like to store some data on it and be able to read it later on. I have done some research, but unfortunately I could not find a tutorial that uses similar board as mine. I am using MPLAB IDE with C18 compiler.
PIC18F87J11
Note: two more links are written as comment below.
This is where my problem is ...
In order to write to the 25LC1024 external EEPROM I followed the tutorial from microchip. The first problem is that this tut is written for PIC18F1220 and I'm using PIC18F87J11. So upon opening the project I get two files not found error, but I simply ignored them.
PICTURE
I copied the file AN1018.h and AN1018_SPI.c to the project I am working on, and I copied some piece of code from AN1018.c file.
Code from AN1018.c file
void main(void)
{
#define PAGESIZE 16
static unsigned char data[PAGESIZE]; // One-page data array
static unsigned char i;
init(); // Initialize PIC
data[0] = 0xCC; // Initialize first data byte
/* Low-density byte function calls */
LowDensByteWrite(data[0], 0x133); // Write 1 byte of data at 0x133
data[0] = 0xFF;
LowDensByteRead(data, 0x133);
printf("%x",data);
while(1){};
}
void init(void)
{
ADCON1 = 0x7F; // Configure digital I/O
PORTA = 0x08; // Set CS high (inactive)
TRISA = 0b11110111; // Configure PORTA I/O
PORTB = 0; // Clear all PORTB pins
TRISB = 0b11111100; // Configure PORTB I/O
}
My second problem is that the output message is always 1e0. In other words, I do not know if the write was successfully made or not. Also I am not sure about what I might be missing.
If I can receive some kind of help, I would appreciate it. To sum up everything, I want to store data to my external EEPROM and retain it when needed. Please know I am a beginner with Microcontroller programming.
As a first step (before reading & writing) you have to be sure that your SPI interface (hardware and software) is correctly configured. To check this step you can read the "Status Register" from the 25LC1024. Look the datasheet for "RDSR", the instruction to send to the eeprom should be 0b00000101 so (int)5.
Here some code for 18F* + 25LC* wirtten in sdcc of a really old project. The code is very basic, no external library used, you just have to replace register variable names and init config for your pic.
Some code comes from here, thanks to bitberzerkir!
spi.c
#ifndef SPI_HH
#define SPI_HH
#define SpiWrite(x) spiRW(x)
#define SpiRead() spiRW(0)
unsigned char spiRW(unsigned char data_){
SSPBUF = data_;
while(!PIR1bits.SSPIF);
PIR1bits.SSPIF = 0;
return SSPBUF;
}
void SpiInit() {
SSPSTAT = 0x40; // 01000000
SSPCON1 = 0x20; // 00100000
PIR1bits.SSPIF = 0;
}
#endif
eeprom.c
Note: Since the addr of 25LC1024 are 3x8bits make sure your compiler 'long' type has at least 24bit
#ifndef EEPROM_HH
#define EEPROM_HH
#include "spi.c"
#define CS PORTCbits.RC2
void EepromInit() {
SpiInit();
CS = 1;
}
unsigned char EReadStatus () {
unsigned char c;
CS = 0;
SpiWrite(0x05);
c = SpiRead();
CS = 1;
return c;
}
unsigned char EWriting() {
unsigned char c;
CS = 0;
SpiWrite(0x05);
c = SpiRead();
CS = 1;
return c & 1;
}
unsigned char EReadCh (unsigned long addr) {
unsigned char c;
// Send READ command and addr, then read data
CS = 0;
SpiWrite(0x03);
// Address in 3x8 bit mode for 25lc1024
SpiWrite(addr>>16);
SpiWrite(addr>>8);
SpiWrite((unsigned char) addr);
c = SpiRead();
CS = 1;
return c;
}
void EWriteCh (unsigned char c, unsigned long addr) {
// Enable Write Latch
CS = 0;
SpiWrite(0x06);
CS = 1;
// Send WRITE command, addr and data
CS = 0;
SpiWrite(0x02);
SpiWrite(addr>>16);
SpiWrite(addr>>8);
SpiWrite((unsigned char) addr);
SpiWrite(c);
CS = 1;
}
#endif
main.c
Set your init according to the datasheet
#include <pic18fregs.h>
#include "eeprom.c"
void main(void) {
char out;
TRISB = 0x01;
TRISC = 0x00;
PORTB = 0x00;
PORTC = 0x00;
EepromInit();
EWriteCh('a', 0x00);
out = EReadCh(0x00);
while(1);
}
If you want to read/write a buffer take care of pagination. Eg here:
// Page byte size, 64 for 25lc256 and 256 for 25lc1024
#define PSIZE 256
// Addr mem limit 7FFF for 25lc256, 1FFFF for 25lc1024
#define MLIMIT 0x1FFFF
void EReadBuff (unsigned char c[], unsigned long dim, unsigned long addr) {
unsigned int i;
// Send READ command and addr, then read data
CS = 0;
SpiWrite(0x03);
SpiWrite(addr>>16);
SpiWrite(addr>>8);
SpiWrite((unsigned char) addr);
for(i = 0; i < dim; ++i)
c[i] = SpiRead();
CS = 1;
}
void EWriteBuff (unsigned char c[], unsigned long dim, unsigned long addr) {
unsigned char i;
unsigned int begin = 0;
unsigned int end = dim > PSIZE ? PSIZE : dim;
while (end > begin && addr + end <= MLIMIT) { // check if addr is a siutable address [0, MLIMIT]
// Enable Write Latch
CS = 0;
SpiWrite(0x06);
CS = 1;
// Send WRITE command, addr and data
CS = 0;
SpiWrite(0x02);
SpiWrite(addr>>8);
SpiWrite((unsigned char) addr);
for(i = begin; i < end; ++i)
SpiWrite(c[i]);
CS = 1;
while(EWriting());
dim -= PSIZE;
begin += PSIZE;
addr += PSIZE;
end = begin + (dim > PSIZE ? PSIZE : dim);
}
}
#endif
I think before directly using the AN1018.h/AN1018_spi.c you will need to verify that it is compatible with your micro-controller. I recommend to check the datasheet of both micro-controllers and see the difference specifically for SPI module as the external EEPROM which you are using will be connected to SPI bus. If these two micro-controller has same register configuration/module for SPI then you can use it else you will have to write the driver on your own. You can use AN1018_spi.c for reference I guess you will just need to change some registers if required.
Then in you init function, you are not initializing SPI module, you will need to specify correct SPI clock, SPI mode based on your external device. Once you have properly initialize SPI module. You will need to write EEPROM_Read/EEPROM_Write function. In which you will have to following protocol given in datasheet of your external device for sending/receiving data from device using.
hi i googled and get a very good website Where i found post on Interfacing external EEPROM with PIC Microcontroller via i2c protocol with FM24C64 and the code which they given in post which i tested and working fine. i give that link may it help you. http://www.nbcafe.in/interfacing-external-eeprom-with-pic-microcontroller/