How to read from and write to EEPROM suing SPI communication - spi

I am using PIC32MX350F128L Microcontroller to read from and write to EEPROM(SST26VF032B) using SPI communication. SPI communication in this program is working, I have checked it by sending JEDEC code which is provided in the SST26VF032B datasheet. So when i send 0x9F i am getting 3 bytes of data as mentioned in the datasheet. When i run now i am sending a string of data to a specific address of eeprom and getting 0xff in return. I am erasing the eeprom before writing into it. So i think i am getting 0xff after erasing the eeprom. The writing, reading operations are not working. If i send a string of value or a BYTE i am getting 0xff in return. So can u guys please suggest me where i am going wrong. I am using UART for debugging purpose to read values recieved through spi communication. The complete code is below, i am using MPLAB X.
Best Regards
Sandesh
#include <xc.h>
#include <stdio.h>
#include <plib.h>
#include <p32xxxx.h>
/* Configuration Bits */
#pragma config FSRSSEL = PRIORITY_7 // Shadow Register Set Priority Select (SRS Priority 7)
#pragma config PMDL1WAY = ON // Peripheral Module Disable Configuration (Allow only one reconfiguration)
#pragma config IOL1WAY = ON // Peripheral Pin Select Configuration (Allow only one reconfiguration)
// DEVCFG2
#pragma config FPLLIDIV = DIV_2 // PLL Input Divider (2x Divider)
#pragma config FPLLMUL = MUL_20 // PLL Multiplier (20x Multiplier)
#pragma config FPLLODIV = DIV_1 // System PLL Output Clock Divider (PLL Divide by 1)
// DEVCFG1
#pragma config FNOSC = PRIPLL // Oscillator Selection Bits (Primary Osc (XT,HS,EC))
#pragma config FSOSCEN = ON // Secondary Oscillator Enable (Enabled)
#pragma config IESO = ON // Internal/External Switch Over (Enabled)
#pragma config POSCMOD = HS // Primary Oscillator Configuration (XT osc mode)
#pragma config OSCIOFNC = ON // CLKO Output Signal Active on the OSCO Pin (Enabled)
#pragma config FPBDIV = DIV_1 // Peripheral Clock Divisor (Pb_Clk is Sys_Clk/8)
#pragma config FCKSM = CSECME // Clock Switching and Monitor Selection (Clock Switch Disable, FSCM Disabled)
#pragma config WDTPS = PS1048576 // Watchdog Timer Postscaler (1:1048576)
#pragma config WINDIS = OFF // Watchdog Timer Window Enable (Watchdog Timer is in Non-Window Mode)
#pragma config FWDTEN = OFF // Watchdog Timer Enable (WDT Disabled (SWDTEN Bit Controls))
#pragma config FWDTWINSZ = WINSZ_25 // Watchdog Timer Window Size (Window Size is 25%)
// DEVCFG0
#pragma config DEBUG = OFF // Background Debugger Enable (Debugger is Disabled)
#pragma config JTAGEN = OFF // JTAG Enable (JTAG Disabled)
#pragma config ICESEL = ICS_PGx2 // ICE/ICD Comm Channel Select (Communicate on PGEC2/PGED2)
#pragma config PWP = OFF // Program Flash Write Protect (Disable)
#pragma config BWP = OFF // Boot Flash Write Protect bit (Protection Disabled)
#pragma config CP = OFF // Code Protect (Protection Disabled)
/* MACRO DEFINITIONS */
/* Defining the Slave Select Pin */
#define SS LATDbits.LATD9
/* Defining the System Clock Frequency */
#define SYSCLK 40000000
/* Macro to get array size in bytes
* note that array size can't be found after passing pointer to a function */
#define LEN(x) (sizeof(x) / sizeof(x[0]))
/* SST26VF032B EEPROM instructions */
/* Write Enable */
#define WREN 0x06
/* Write Disable */
#define WRDI 0x04
/* Initialize Start of Write Sequence */
#define WRITE 0x02
/* Initialize Start of Read Sequence */
#define READ 0x03
/* Erase all sectors of Memory */
#define CE 0xc7
/* Read STATUS Register */
#define RDSR 0x05
/* Function Prototypes */
/* UART bit configuration */
void Bitconfig_uart(void);
/* SPI Initialization */
void SPI1_Init(void);
/* UART Initialization */
void Init_uart(void);
/* Send a Character Byte through UART */
void UART5PutChar(char Ch);
/* Function to Read and Write SPI1 buffer */
int SPI1_transfer( int b);
/* Function to check the Status of SPI */
void waitBusy();
/* Function to erase the contents in EEPROM */
void eraseEEPROM();
/* Function to Read data from EEPROM */
void readEEPROM( int address, char* loadArray, int loadArray_size);
/* Function to Write to EEPROM */
void writeEEPROM( int address, char* storeArray, int storeArray_size);
/* Global Variables Declaration */
/* Declare variables to check the functionality of EEPROM */
int i,j = 0;
char st = 0x9F;
char rec;
int x,y,z;
/*******************************************************************************
* Function Name: main()
********************************************************************************
* Summary:
* Initializes SPI
* Erase EEPROM
* Writes to EEPROM
* Read from EEPROM
*
* Parameters:
* None.
*
* Return:
* None.
*
*******************************************************************************/
int main()
{
int i;
/* Clock Setting */
SYSTEMConfigPerformance(SYSCLK);
/* UART bit configuration */
Bitconfig_uart();
/* Set the Controller OScillator Register bits */
//OSCCON = 0x00002200;
/* Initialize a String to Write to EEPROM and an array to Read back contents */
char writeData[] = "123456789ABCDEF";
/* Array to read 35 bytes of data */
char readData[15];
/* SPI Initialization */
SPI1_Init();
/* UART Initialization */
Init_uart();
/* Erase contents of EEPROM */
eraseEEPROM();
/* Write contents of writeData array to address 180 */
writeEEPROM( 0x1000, writeData, LEN(writeData));
/*
JEDEC Code (working) getting output as per datasheet (0x9F = 159)
SS=0;
SPI1_transfer(159);
x=SPI1_transfer(0);
UART5PutChar(x);
y=SPI1_transfer(0);
UART5PutChar(y);
z=SPI1_transfer(0);
UART5PutChar(z);
*/
while(1)
{
/* Read contents of EEPROM into readData array
* start at address 180 and read up to 180+length(readData) */
readEEPROM( 0x1000, readData, LEN(readData) );
}
} /* END main() */
/*******************************************************************************
* Function Name: SPI1_Init()
********************************************************************************
* Summary:
* SPI1 Initialization
*
* Parameters:
* None.
*
* Return:
* None.
*
*******************************************************************************/
void SPI1_Init(void)
{
/* Configure Peripheral Pin Select (PPS) for the SPI1 module
* Note: SS will be toggled manually in code
* SCK is hardwired to pin 55 */
/* Output Pin Selection */
RPE5R = 8;
SDI1R = 3;
/* RB4 (Slave Select 1) : output */
TRISDbits.TRISD9 = 0;
/* SPI configuration */
/* SPI1CON Register Configuration
* MSTEN: Master Mode Enable bit = 1 (Master)
* CKP (clock polarity control) = 0
* CKE (clock edge control) = 1
* ON: SPI Peripheral On bit
* 8-bit, Master Mode */
SPI1CON = 0x8120;
/* SPI1BRG Register Configuration */
SPI1BRG = 0x4D;
//REFOCONbits.ON = 1;
// REFOCONbits.DIVSWEN = 1;
}
/*******************************************************************************
* Function Name: SPI1_transfer()
********************************************************************************
* Summary:
* Write to and Read from SPI1 buffer
*
* Parameters:
* char b - Writes a Character to Buffer
*
* Return:
* Char - Returns the Character Read from EEPROM
*
*******************************************************************************/
int SPI1_transfer( int b)
{
/* write to buffer for TX */
SPI1BUF = b;
/* wait transfer complete */
while(!SPI1STATbits.SPIRBF);
/* read the received value */
return SPI1BUF;
} /* END SPI1_transfer() */
/*******************************************************************************
* Function Name: waitBusy()
********************************************************************************
* Summary:
* Checks if EEPROM is ready to be modified and waits if not ready
*
* Parameters:
* None.
*
* Return:
* None.
*
*******************************************************************************/
void waitBusy()
{
char status = 0;
do{
/* Select EEPROM */
SS = 0;
/* Read EEPROM status register */
SPI1_transfer(RDSR);
/* send dummy byte to receive incoming data */
status = SPI1_transfer(0);
/* Release EEPROM */
SS = 1;
}
/* write-in-progress while status<0> set to '1' */
while( status & 0x01);
} /* END waitBusy() */
/*******************************************************************************
* Function Name: readEEPROM()
********************************************************************************
* Summary:
* Reads data from EEPROM
*
* Parameters:
* Inputs: address - EEPROM address
* loadArray - array to load EEPROM data to
* loadArray_size - number of bytes of EEPROM data to load into array
*
* Return:
* None.
*
*******************************************************************************/
void readEEPROM( int address, char* loadArray, int loadArray_size)
{
int i;
/* Wait until EEPROM is not busy */
waitBusy();
/* Select EEPROM */
SS = 0;
/* Initiate Read */
SPI1_transfer( READ);
/* Address must be 16-bits but we're transferring it in two 8-bit sessions */
SPI1_transfer( address >> 16);
SPI1_transfer( address >> 8);
SPI1_transfer( address);
/* Request and store loadArray_size number of bytes into loadArray */
for( i=0 ; i<loadArray_size ; i++)
{
/* send dummy byte to read 1 byte */
loadArray[i] = SPI1_transfer( 0x00);
}
/* Release EEPROM */
SS = 1;
/* UART Test */
for(i=0;i<35;i++)
{
UART5PutChar(loadArray[i]);
for(j=0;j<20000;j++)
{}
}
} /* END readEEPROM() */
/*******************************************************************************
* Function Name: writeEEPROM()
********************************************************************************
* Summary:
* Write data to EEPROM
*
* Parameters:
* Inputs: address - EEPROM address
* storeArray - array of which contents are stored in EEPROM
* storeArray_size - number of bytes in array to store into EEPROM
*
* Return:
* None.
*
*******************************************************************************/
void writeEEPROM( int address, char* storeArray, int storeArray_size)
{
int i;
/* Wait until EEPROM is not busy */
waitBusy();
/* Select EEPROM */
SS = 0;
/* Send WRITE_ENABLE command */
SPI1_transfer( WREN);
/* Release EEPROM */
SS = 1;
/* Select EEPROM again after WREN cmd */
SS = 0;
/* Initiate Write */
SPI1_transfer( WRITE);
SPI1_transfer( address >> 16 );
SPI1_transfer( address >> 8 );
SPI1_transfer( address );
/* write 1 byte at a time from array */
/* MSB at lowest address (0 - first letter in string) */
for( i=0 ; i<storeArray_size; i++)
{
/* Initiate Write */
SPI1_transfer( WRITE);
SPI1_transfer( (address+i) >> 16 );
SPI1_transfer( (address+i) >> 8 );
SPI1_transfer( address+i );
SPI1_transfer( storeArray[i]);
}
/* Release EEPROM */
SS = 1;
} /* END writeEEPROM() */
/*******************************************************************************
* Function Name: eraseEEPROM()
********************************************************************************
* Summary:
* Erase entire contents of EEPROM
*
* Parameters:
* None
*
* Return:
* None.
*
*******************************************************************************/
void eraseEEPROM()
{
/* Wait until EEPROM is not busy */
waitBusy();
/* Select EEPROM */
SS = 0;
/* Send WRITE_ENABLE command */
SPI1_transfer( WREN);
/* Release EEPROM */
SS = 1;
/* Select EEPROM again after WREN cmd */
SS = 0;
/* send CHIP_ERASE command */
SPI1_transfer( CE);
/* Release EEPROM */
SS = 1;
} /* END eraseEEPROM() */
/*******************************************************************************
* Function Name: Init_uart()
********************************************g************************************
* Summary:
* Initialize UART4
*
* Parameters:
* None
*
* Return:
* None.
*
*******************************************************************************/
void Init_uart()
{
/* Enable UART */
U5MODEbits.ON = 1 ;
/* set baud rate(9600) */
U5BRG = 521;
/* Set U4STA Register for Enabling tx and rx */
U5STA=0x9400;
}
/*******************************************************************************
* Function Name: UART4PutChar(unsigned char Ch)
********************************************************************************
* Summary:
* Send data from controller to putty GUI
*
* Parameters:
* input
* unsigned char Ch - To Send a byte of data over UART
*
* Return:
* None.
*
*******************************************************************************/
void UART5PutChar(char Ch)
{
while(U5STAbits.UTXBF == 1);
U5TXREG=Ch;
}
/*******************************************************************************
* Function Name: Bitconfig_uart()
********************************************************************************
* Summary:
* UART Pin Configuration
*
* Parameters:
* None
*
* Return:
* None.
*
*******************************************************************************/
void Bitconfig_uart(void)
{
/* UART4 Initialization */
// OSCCON=0x00002200;
/* Set pins as digital */
ANSELBbits.ANSB2 = 0;
ANSELBbits.ANSB0 = 0;
/* Set UART Tx pin as Output */
TRISBbits.TRISB0 = 0; //in controler tx
TRISBbits.TRISB2 = 1; // in controller RX
/* Peripheral Pin select for UART4 */
U5RXR=0x07;
RPB0R=0x04;
}

I faced the same problem for 3 long days until I found that there's a 18bytes long register called Block Protection Register BPR.
You need to set its bits to 0, according to the memory area you want to write.
So I read the BPR (send command 0x72 followed by 18 byte read) and I found it was not at zero everywhere in my case.
Reading on page 41 of the datasheet you can see that after a power on the BPR register is set to 5555 FFFFFFFF FFFFFFFF, so it protects from write the whole memory.
So for testing purpose I tried to clears it completely, and there's a specific command (0x98) for that purpose, allowing you to write anywhere in the whole memory.
But be sure to write enable the memory (command 0x06) before sending the clear BPR command (command 0x98).
At this point if you read the BPR (command 0x72) you will read 00 at every of its 10 bytes. (And this means the whole memory is now writeable)
In this state the write now finally works for me.
(I sent WriteEnable - SectorErase - SectorRead - WriteEnable - SectorWrite - SectorRead and it now works!)
Hope it helps, the datasheet is very chaotic about that.
P.S.
Somewhere the datasheet says that BPR is 18 bytes long, it is wrong, the BPR is only 10 bytes long

Related

Reading a non-null terminated input at serial?

I am working with mbed, the LPC1768. The serial input to the mbed is a unsigned character array which is not null-terminated. I don't get anything with getc(), how to get the input?
the null character is 0 so if you need get it u can using a Buffer Counter or a ovf variable to detect received a character!!
Config your UART0,1 by this Setting:
comment UART0 or UART1 if not use it!
so u can select UARTs with #define in: option->C/C++->Define:__UART0 or __UART1
void SER_Init (void) {
#ifdef __UART0 /* UART0 */
LPC_SC->PCONP |= ((1 << 3) | (1 << 15)); /* enable power to UART0 & IOCON */
LPC_PINCON->PINSEL0 |= (1 << 4); /* Pin P0.2 used as TXD0 */
LPC_PINCON->PINSEL0 |= (1 << 6); /* Pin P0.3 used as RXD0 */
#else /* UART1 */
LPC_SC->PCONP |= ((1 << 4) | (1 << 15)); /* enable power to UART1 & IOCON */
LPC_PINCON->PINSEL4 |= (2 << 0); /* Pin P2.0 used as TXD1 */
LPC_PINCON->PINSEL4 |= (2 << 2); /* Pin P2.1 used as RXD1 */
#endif
UART->LCR = 0x83; /* 8 bits, no Parity, 1 Stop bit */
UART->DLL = 9; /* 115200 Baud Rate # 25.0 MHZ PCLK*/
UART->FDR = 0x21; /* FR 1,507, DIVADDVAL=1, MULVAL=2 */
UART->DLM = 0; /* High divisor latch = 0 */
UART->LCR = 0x03; /* DLAB = 0 */
}
------> and use this func. for Get Character:
/*----------------------------------------------------------------------------
Read character from Serial Port (blocking read)
*----------------------------------------------------------------------------*/
int SER_GetChar (void) {
while (!(UART0->LSR & 0x01));
return (UART0->RBR);
}

Input Capture mapping using PPS

I am trying to configure input capture module (IC1) on dsPIC33EP32MC204 to use it for duty cycle measurement. As input I would like to use RP35 pin. To test the correctness of the configuration I set up an experiment: I connected a pulse generator with 1Hz PWM pulses and set up the input capture to capture every rising edge. I reckoned that these rising edges will trigger the input capture interrupt and each call of the interrupt will cause the LED to blink. Unfortunatelly it is not working. I would be grateful if someone could tell me where is the problem. The code:
void Init_InputCapture(void)
{
IC1CON1bits.ICSIDL = 0;
IC1CON1bits.ICTSEL = 0b111; // Peripheral clock (FP) is the clock source of the ICx
IC1CON1bits.ICI = 0b00; //
IC1CON1bits.ICM = 0b011; //Capture mode every edge rising
IC1CON2bits.ICTRIG = 0; // = Input source used to trigger the input capture timer (Trigger mode)
IC1CON2bits.SYNCSEL = 0b00000; //IC1 module synchronizes or triggers ICx
IC1CON2bits.IC32 = 0; // 16 bit mode only
// Enable Capture Interrupt And Timer2
IPC0bits.IC1IP = 1; // Setup IC1 interrupt priority level
IFS0bits.IC1IF = 0; // Clear IC1 Interrupt Status Flag
IEC0bits.IC1IE = 1; // Enable IC1 interrupt
}
The interrupt:
void __attribute__((__interrupt__, no_auto_psv)) _IC1Interrupt(void)
{
LATBbits.LATB9 = ~LATBbits.LATB9;
IFS0bits.IC1IF = 0;
}
And the related code from the main():
__builtin_write_OSCCONL(OSCCON & ~(1<<6));
RPINR7 = 0x23; ; // IC1 mapped to RP35
__builtin_write_OSCCONL(OSCCON | (1<<6));
During the setup I followed the instuctions from the family reference manual.
The code you have posted could be more complete. For dsPIC controllers it is essential to show exactly how all of the configuration bits are set and how the system clock is initialized.
This is your code corrected with the minimum required setup code:
/*
* file: main.c
* target: dsPIC33EP32MC204
* IDE: MPLABX v4.05
* Compiler: XC16 v1.35
*
* Description:
* Use Input Capture 1 module to catch a 1Hz pulse on GPIO RP35 and toggle LED on RB9.
*
*/
#pragma config ICS = PGD2 // ICD Communication Channel Select bits (Communicate on PGEC2 and PGED2)
#pragma config JTAGEN = OFF // JTAG Enable bit (JTAG is disabled)
#pragma config ALTI2C1 = OFF // Alternate I2C1 pins (I2C1 mapped to SDA1/SCL1 pins)
#pragma config ALTI2C2 = OFF // Alternate I2C2 pins (I2C2 mapped to SDA2/SCL2 pins)
#pragma config WDTWIN = WIN25 // Watchdog Window Select bits (WDT Window is 25% of WDT period)
#pragma config WDTPOST = PS32768 // Watchdog Timer Postscaler bits (1:32,768)
#pragma config WDTPRE = PR128 // Watchdog Timer Prescaler bit (1:128)
#pragma config PLLKEN = ON // PLL Lock Enable bit (Clock switch to PLL source will wait until the PLL lock signal is valid.)
#pragma config WINDIS = OFF // Watchdog Timer Window Enable bit (Watchdog Timer in Non-Window mode)
#pragma config FWDTEN = OFF // Watchdog Timer Enable bit (Watchdog timer enabled/disabled by user software)
#pragma config POSCMD = NONE // Primary Oscillator Mode Select bits (Primary Oscillator disabled)
#pragma config OSCIOFNC = OFF // OSC2 Pin Function bit (OSC2 is clock output)
#pragma config IOL1WAY = OFF // Peripheral pin select configuration (Allow multiple reconfigurations)
#pragma config FCKSM = CSECMD // Clock Switching Mode bits (Clock switching is enabled,Fail-safe Clock Monitor is disabled)
#pragma config FNOSC = FRC // Oscillator Source Selection (Internal Fast RC (FRC))
#pragma config PWMLOCK = ON // PWM Lock Enable bit (Certain PWM registers may only be written after key sequence)
#pragma config IESO = OFF // Two-speed Oscillator Start-up Enable bit (Start up with user-selected oscillator source)
#pragma config GWRP = OFF // General Segment Write-Protect bit (General Segment may be written)
#pragma config GCP = OFF // General Segment Code-Protect bit (General Segment Code protect is Disabled)
#include <xc.h>
/* Setup the clock to run at about 60 MIPS */
#define FOSC (7372800L) /* nominal fast RC frequency */
#define PLL_N1 (2L) /* PLLPRE CLKDIV<4:0> range 2 to 33 */
#define PLL_M (65L) /* PLLDIV PLLFBD<8:0> range 2 to 513 */
#define PLL_N2 (2L) /* PLLPOST CLKDIV<7:6> range 2, 4 or 8 */
#define FSYS (FOSC*PLL_M/(PLL_N1*PLL_N2))
#define FCYC (FSYS/2L)
/*
* Global constant data
*/
const unsigned long gInstructionCyclesPerSecond = FCYC;
/*
* Initialize this PIC
*/
void PIC_Init( void )
{
unsigned int ClockSwitchTimeout;
/*
** Disable all interrupt sources
*/
__builtin_disi(0x3FFF); /* disable interrupts for 16383 cycles */
IEC0 = 0;
IEC1 = 0;
IEC2 = 0;
IEC3 = 0;
IEC4 = 0;
IEC5 = 0;
IEC6 = 0;
IEC8 = 0;
IEC9 = 0;
__builtin_disi(0x0000); /* enable interrupts */
CLKDIV = 0; /* Disable DOZE mode */
if(!OSCCONbits.CLKLOCK) /* if primary oscillator switching is unlocked */
{
/* Select primary oscillator as FRC */
__builtin_write_OSCCONH(0b000);
/* Request switch primary to new selection */
__builtin_write_OSCCONL(OSCCON | (1 << _OSCCON_OSWEN_POSITION));
/* wait, with timeout, for clock switch to complete */
for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && OSCCONbits.OSWEN;);
/* Configure PLL prescaler, PLL postscaler, PLL divisor */
PLLFBD=PLL_M-2; /* M=65 */
#if PLL_N2==2
CLKDIVbits.PLLPOST=0; /* N2=2 */
#elif PLL_N2==4
CLKDIVbits.PLLPOST=1; /* N2=4 */
#elif PLL_N2==8
CLKDIVbits.PLLPOST=3; /* N2=8 */
#else
#error invalid PLL_N2 paramenter
#endif
CLKDIVbits.PLLPRE=PLL_N1-2; /* N1=2 */
/* Select primary oscillator as FRCPLL */
__builtin_write_OSCCONH(0b001);
/* Request switch primary to new selection */
__builtin_write_OSCCONL(OSCCON | (1 << _OSCCON_OSWEN_POSITION));
/* wait, with timeout, for clock switch to complete */
for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && OSCCONbits.OSWEN;);
/* wait, with timeout, for the PLL to lock */
for(ClockSwitchTimeout=10000; --ClockSwitchTimeout && !OSCCONbits.LOCK;);
/* at this point the system oscillator should be 119.808MHz */
}
/* make all inputs digital I/O */
ANSELA = 0x00;
ANSELB = 0x00;
ANSELC = 0x00;
/* Unlock PPS Registers */
__builtin_write_OSCCONL(OSCCON & ~(_OSCCON_IOLOCK_MASK));
/* map all PPS pins */
RPINR7bits.IC1R = 35; // Select RP35 (RB3/PGED1) as input for IC1
/* Lock PPS Registers */
__builtin_write_OSCCONL(OSCCON | (_OSCCON_IOLOCK_MASK));
}
/*
* Initialize Input Capture 1
*/
void Init_InputCapture( void )
{
IC1CON1bits.ICSIDL = 0;
IC1CON1bits.ICTSEL = 0b111; // Peripheral clock (FP) is the clock source of the ICx
IC1CON1bits.ICI = 0b00; //
IC1CON1bits.ICM = 0b011; //Capture mode every edge rising
IC1CON2bits.ICTRIG = 0; // = Input source used to trigger the input capture timer (Trigger mode)
IC1CON2bits.SYNCSEL = 0b00000; //IC1 module synchronizes or triggers ICx
IC1CON2bits.IC32 = 0; // 16 bit mode only
// Enable Capture Interrupt And Timer2
IPC0bits.IC1IP = 4; // Setup IC1 interrupt priority level
IFS0bits.IC1IF = 0; // Clear IC1 Interrupt Status Flag
IEC0bits.IC1IE = 1; // Enable IC1 interrupt
}
/*
* Interrupt Service Routine for IC1
*/
void __attribute__((__interrupt__, no_auto_psv)) _IC1Interrupt(void)
{
IFS0bits.IC1IF = 0;
LATBbits.LATB9 ^= 1; // toggle LED
}
/*
* Main process loop
*/
int main( void )
{
PIC_Init();
TRISBbits.TRISB9 = 0; // make RB9 an output
LATBbits.LATB9 = 0; // turn off LED
Init_InputCapture();
for(;;)
{
/* process loop */
}
return 0;
}

LPC824 microcontroller ADC demo HardFault problem

I'm trying to program LPC824 microcontroller board ([https://www.switch-science.com/catalog/2265/][1]) with LPCOpen.
I'm using it with LPCLink 2 debugger board.
My goal is to get some information from the "pressure sensor" with an ADC.
My code stops with a HardFault when executing a NVIC_EnableIRQ function(on line: 92).
If I don't use "NVIC interrupt controller" then my code works and I can get value from sensor with ADC.
What I am doing wrong?
Here is my adc.c code:
#include "board.h"
static volatile int ticks;
static bool sequenceComplete = false;
static bool thresholdCrossed = false;
#define TICKRATE_HZ (100) /* 100 ticks per second */
#define BOARD_ADC_CH 2
/**
* #brief Handle interrupt from ADC sequencer A
* #return Nothing
*/
void ADC_SEQA_IRQHandler(void) {
uint32_t pending;
/* Get pending interrupts */
pending = Chip_ADC_GetFlags(LPC_ADC);
/* Sequence A completion interrupt */
if (pending & ADC_FLAGS_SEQA_INT_MASK) {
sequenceComplete = true;
}
/* Threshold crossing interrupt on ADC input channel */
if (pending & ADC_FLAGS_THCMP_MASK(BOARD_ADC_CH)) {
thresholdCrossed = true;
}
/* Clear any pending interrupts */
Chip_ADC_ClearFlags(LPC_ADC, pending);
}
/**
* #brief Handle interrupt from SysTick timer
* #return Nothing
*/
void SysTick_Handler(void) {
static uint32_t count;
/* Every 1/2 second */
if (count++ == TICKRATE_HZ / 2) {
count = 0;
Chip_ADC_StartSequencer(LPC_ADC, ADC_SEQA_IDX);
}
}
/**
* #brief main routine for ADC example
* #return Function should not exit
*/
int main(void) {
uint32_t rawSample;
int j;
SystemCoreClockUpdate();
Board_Init();
/* Setup ADC for 12-bit mode and normal power */
Chip_ADC_Init(LPC_ADC, 0);
Chip_ADC_Init(LPC_ADC, ADC_CR_MODE10BIT);
/* Need to do a calibration after initialization and trim */
Chip_ADC_StartCalibration(LPC_ADC);
while (!(Chip_ADC_IsCalibrationDone(LPC_ADC))) {
}
/* Setup for maximum ADC clock rate using sycnchronous clocking */
Chip_ADC_SetClockRate(LPC_ADC, ADC_MAX_SAMPLE_RATE);
Chip_ADC_SetupSequencer(LPC_ADC, ADC_SEQA_IDX,
(ADC_SEQ_CTRL_CHANSEL(BOARD_ADC_CH) | ADC_SEQ_CTRL_MODE_EOS));
Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_SWM);
Chip_SWM_EnableFixedPin(SWM_FIXED_ADC2);
Chip_Clock_DisablePeriphClock(SYSCTL_CLOCK_SWM);
/* Setup threshold 0 low and high values to about 25% and 75% of max */
Chip_ADC_SetThrLowValue(LPC_ADC, 0, ((1 * 0xFFF) / 4));
Chip_ADC_SetThrHighValue(LPC_ADC, 0, ((3 * 0xFFF) / 4));
Chip_ADC_ClearFlags(LPC_ADC, Chip_ADC_GetFlags(LPC_ADC));
Chip_ADC_EnableInt(LPC_ADC,
(ADC_INTEN_SEQA_ENABLE | ADC_INTEN_OVRRUN_ENABLE));
Chip_ADC_SelectTH0Channels(LPC_ADC, ADC_THRSEL_CHAN_SEL_THR1(BOARD_ADC_CH));
Chip_ADC_SetThresholdInt(LPC_ADC, BOARD_ADC_CH, ADC_INTEN_THCMP_CROSSING);
/* Enable ADC NVIC interrupt */
NVIC_EnableIRQ(ADC_SEQA_IRQn);
Chip_ADC_EnableSequencer(LPC_ADC, ADC_SEQA_IDX);
SysTick_Config(SystemCoreClock / TICKRATE_HZ);
/* Endless loop */
while (1) {
/* Sleep until something happens */
__WFI();
if (thresholdCrossed) {
thresholdCrossed = false;
printf("********ADC threshold event********\r\n");
}
/* Is a conversion sequence complete? */
if (sequenceComplete) {
sequenceComplete = false;
/* Get raw sample data for channels 0-11 */
for (j = 0; j < 12; j++) {
rawSample = Chip_ADC_GetDataReg(LPC_ADC, j);
/* Show some ADC data */
if (rawSample & (ADC_DR_OVERRUN | ADC_SEQ_GDAT_DATAVALID)) {
printf("Chan: %d Val: %d\r\n", j, ADC_DR_RESULT(rawSample));
printf("Threshold range: 0x%x ",
ADC_DR_THCMPRANGE(rawSample));
printf("Threshold cross: 0x%x\r\n",
ADC_DR_THCMPCROSS(rawSample));
printf("Overrun: %s ",
(rawSample & ADC_DR_OVERRUN) ? "true" : "false");
printf("Data Valid: %s\r\n\r\n",
(rawSample & ADC_SEQ_GDAT_DATAVALID) ?
"true" : "false");
}
}
}
}
}
Hard fault usually means that you try to execute code outside allowed addresses. If you have not registered the interrupt in the vector table but enabled it, the MCU will jump to whatever address that's written there instead, after which the program crashes.
How to fix that depends on tool chain. Assuming LPCXpresso, you have several options to set up libraries (I don't know about LPCOpen specifically), so where to find the vector table is different from case to case. However, this works quite similar on most MCUs, ARM or not. Somewhere in a "crt start-up" file you should have something along the lines of this:
void (* const g_pfnVectors[])(void) = ...
This is an array of function pointers which will be the vector table allocated in memory at address 0 on Cortex M. You have to place your function at the relevant interrupt vector. For example it may say something like
PIN_INT0_IRQHandler, // PIO INT0
If that's the interrupt you should implement, then you replace that line:
#include "my_irq_stuff.h"
...
void (* const g_pfnVectors[])(void) =
...
my_INT0, // PIO INT0
Assuming my_irq_stuff.h contains the function prototype my_INT0 for the interrupt service routine. The actual routine should be implemented in the corresponding .c file.

STM32 SPI Receive DMA is getting garbage data

In my project, I am using Master SPI communication to get analog data from external ADC. My MCU is STM32F746ZGTX. My system needs to work real time so I used SPI DMA Receive and Transmit functions.
I am reading all external ADC data correctly with SPI polling without using DMA. In SPI polling, I am first sending control byte to external ADC, in that time program is waiting in while(SPI_Ready) loop and then starts to receive all ADC data. This scenario works perfectly.
But I do not want to wait in while(SPI_Ready) loop in my every ADC reading. Because It affects my real time calculations. That's why I switched my functions to DMA.
My new algorithm is like that in below:
Generate External GPIO Interrupt with falling edge trigger to sense data ready output of external ADC.
Make chip select pin low to start communication with external ADC
Send read command to External ADC with HAL_SPI_Transmit_DMA() function.
In HAL_SPI_TxCpltCallback function, trigger HAL_SPI_Receive_DMA()
In HAL_SPI_RxCpltCallback function, buffer received ADC data and make chip select pin high to terminate communication.
When I use this algorithm, I am getting always 0xFF values in my ADC buffer. It seems like even if ADC is not sending raw data, because of triggering receive DMA, My MCU sends clock and sense all logic high signal as a received data.
I am sharing my codes in below. If you have any sugggestion where I am wrong, please share your opinions.
/* SPI1 init function */
static void MX_SPI1_Init(void)
{
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi1.Init.NSS = SPI_NSS_SOFT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
if (HAL_SPI_Init(&hspi1) != HAL_OK)
{
}
}
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
GPIO_InitTypeDef GPIO_InitStruct;
if(hspi->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspInit 0 */
/* USER CODE END SPI1_MspInit 0 */
/* Peripheral clock enable */
__HAL_RCC_SPI1_CLK_ENABLE();
/**SPI1 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
GPIO_InitStruct.Pin = GPIO_PIN_5;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
GPIO_InitStruct.Alternate = GPIO_AF5_SPI1;
HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);
/* SPI1 DMA Init */
/* SPI1_RX Init */
hdma_spi1_rx.Instance = DMA2_Stream0;
hdma_spi1_rx.Init.Channel = DMA_CHANNEL_3;
hdma_spi1_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
hdma_spi1_rx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_rx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_rx.Init.Mode = DMA_NORMAL;
hdma_spi1_rx.Init.Priority = DMA_PRIORITY_LOW;
hdma_spi1_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi1_rx) != HAL_OK)
{
}
__HAL_LINKDMA(hspi,hdmarx,hdma_spi1_rx);
/* SPI1_TX Init */
hdma_spi1_tx.Instance = DMA2_Stream3;
hdma_spi1_tx.Init.Channel = DMA_CHANNEL_3;
hdma_spi1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
hdma_spi1_tx.Init.PeriphInc = DMA_PINC_DISABLE;
hdma_spi1_tx.Init.MemInc = DMA_MINC_ENABLE;
hdma_spi1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
hdma_spi1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
hdma_spi1_tx.Init.Mode = DMA_NORMAL;
hdma_spi1_tx.Init.Priority = DMA_PRIORITY_LOW;
hdma_spi1_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
if (HAL_DMA_Init(&hdma_spi1_tx) != HAL_OK)
{
}
__HAL_LINKDMA(hspi,hdmatx,hdma_spi1_tx);
/* SPI1 interrupt Init */
HAL_NVIC_SetPriority(SPI1_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(SPI1_IRQn);
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
/* USER CODE BEGIN SPI1_MspInit 1 */
/* USER CODE END SPI1_MspInit 1 */
}
}
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi)
{
if(hspi->Instance==SPI1)
{
/* USER CODE BEGIN SPI1_MspDeInit 0 */
/* USER CODE END SPI1_MspDeInit 0 */
/* Peripheral clock disable */
__HAL_RCC_SPI1_CLK_DISABLE();
/**SPI1 GPIO Configuration
PA5 ------> SPI1_SCK
PA6 ------> SPI1_MISO
PB5 ------> SPI1_MOSI
*/
HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6);
HAL_GPIO_DeInit(GPIOB, GPIO_PIN_5);
/* USER CODE BEGIN SPI1_MspDeInit 1 */
/* Peripheral DMA DeInit*/
HAL_DMA_DeInit(hspi->hdmarx);
HAL_DMA_DeInit(hspi->hdmatx);
/* Peripheral interrupt Deinit*/
HAL_NVIC_DisableIRQ(SPI2_IRQn);
/* USER CODE END SPI1_MspDeInit 1 */
}
}
/* External Interrupt for data ready output of ADC */
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
/* USER CODE END EXTI15_10_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
adc_selectADC(); /* Make Chip Select pin low */
HAL_SPI_Transmit_DMA (&hspi1, &controlByte, 1);
}
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi->Instance == hspi1.Instance)
{
/* Transmit is completed */
/* Trigger receive DMA to get raw data from external ADC */
HAL_SPI_Receive_DMA (&hspi1, (uint8_t*)adcRecBuffer, 24);
}
}
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi)
{
if (hspi->Instance == hspi1.Instance)
{
/* Receive is completed */
adc_deselectADC(); /* Make Chip Select pin high */
}
}
***Working Algorithm without using DMA or Interrupt:***
/* External Interrupt for data ready output of ADC */
void EXTI15_10_IRQHandler(void)
{
/* USER CODE BEGIN EXTI15_10_IRQn 0 */
/* USER CODE END EXTI15_10_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_15);
/* USER CODE BEGIN EXTI15_10_IRQn 1 */
adc_selectADC(); /* Make Chip Select pin low */
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
HAL_SPI_Transmit(&hspi1, &controlByte, 1,1);
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
HAL_SPI_Receive(&hspi1, (uint8_t*)adcRecBuffer, 24, 1);
while(HAL_SPI_GetState(&hspi1) != HAL_SPI_STATE_READY);
adc_deselectADC(); /* Make Chip Select pin high*/
}
SPI normally is full-duplex, meaning that "reading" is actually master generating clocks and transmitting zeroes. In STM HAL implementation, "receive" function just sends the data you have in read buffer. Might be zeroes, might be garbage, which your ADC interprets as some commands and enters some sort of bad state.
Try doing TransmitReceive of 2 25 byte buffers with your command ID first ("control byte"), followed by 24 zero bytes in TX buffer. In response you should get RX buffer of size 25, where first byte can be discarded. This way you need to handle only RXCplt interrupt, where you release ADC CS pin.

Embedded - SD card - SPI - C -Codevision AVR - Creating and writing a file in an SD card

I have an SD card (or SDHC card) connected to a microcontroller via SPI mode. I am using Chan’s FAT library. In this I used CodevisionAVR v2.05.3 example code from help. By using this I read a text document and opened the file using open existing.
However, when I created a new file or tried to write, I couldn't open the file (do f_open). Giving error ERROR: FR_INVALID_OBJECT.
How can I clear this issue?
And my code is:
/* ATmega128 I/O register definitions */
#include <io.h>
#if defined(_CHIP_ATMEGA32_)
#warning using ATMEGA32
#define TIMSK1 TIMSK
#define UCSR0A UCSRA
#define UCSR0B UCSRB
//#define UCSR0C UCSRC //this is commented out
#define UBRR0H UBRRH
#define UBRR0L UBRRL
#define TXEN0 TXEN
#endif
/* FAT on MMC/SD/SD HC card support */
#include <ff.h>
/* printf */
#include <stdio.h>
/* String functions */
#include <string.h>
#include <delay.h>
/* Timer1 overflow interrupt frequency [Hz] */
#define T1_OVF_FREQ 100
/* Timer1 clock prescaler value */
#define T1_PRESC 1024L
/* Timer1 initialization value after overflow */
#define T1_INIT (0x10000L-(_MCU_CLOCK_FREQUENCY_/(T1_PRESC*T1_OVF_FREQ)))
/* USART Baud rate */
#define BAUD_RATE 9600
#define BAUD_INIT (_MCU_CLOCK_FREQUENCY_/(BAUD_RATE*16L)-1)
/* Root directory path */
char path[] = "0:/fil.txt";
/* Text to be written to the file */
char text[]="I like CodeVisionAVR!";
/* 100 Hz timer interrupt generated by ATmega128 Timer1 overflow */
interrupt [TIM1_OVF] void timer_comp_isr(void)
{
/* Re-initialize Timer1 */
TCNT1H = T1_INIT >> 8;
TCNT1L = T1_INIT & 0xFF;
/* MMC/SD/SD HC card access low level timing function */
disk_timerproc();
}
/* Error message list */
flash char *flash error_msg[] = {
"", /* Not used */
"FR_DISK_ERR",
"FR_INT_ERR",
"FR_INT_ERR",
"FR_NOT_READY",
"FR_NO_FILE",
"FR_NO_PATH",
"FR_INVALID_NAME",
"FR_DENIED",
"FR_EXIST",
"FR_INVALID_OBJECT",
"FR_WRITE_PROTECTED",
"FR_INVALID_DRIVE",
"FR_NOT_ENABLED",
"FR_NO_FILESYSTEM",
"FR_MKFS_ABORTED",
"FR_TIMEOUT"
};
/* Display error message and stop */
void error(FRESULT res)
{
if ((res >= FR_DISK_ERR) && (res <= FR_TIMEOUT))
printf("ERROR: %p\r\n", error_msg[res]);
/* Stop here */
while (1);
}
void main(void)
{
/* FAT function result */
FRESULT res;
/* Will hold the information for logical drive 0: */
FATFS drive;
/* Will hold the file information */
FIL file;
/* Number of bytes written/read to the file */
unsigned int nbytes;
/* File read buffer */
char buffer[256];
/* Initialize Timer1 overflow interrupts in Mode 0 (Normal) */
TCCR1A = 0x00;
/* clkio/1024 */
TCCR1B = (1<<CS12)|(1<<CS10);
/* Timer overflow interrupts will occur with 100Hz frequency */
TCNT1H = T1_INIT >> 8;
TCNT1L = T1_INIT & 0xFF;
/* Enable Timer1 overflow interrupt */
TIMSK1 = 1 << TOIE1;
/* Initialize the USART0 TX, 8N1, Baud rate: 19200 */
UCSR0A = 0;
UCSR0B = 1 << TXEN0;
//UCSR0C = (1<<UCSZ01)|(1<<UCSZ00);
UBRR0H = BAUD_INIT >> 8;
UBRR0L = BAUD_INIT & 0xFF;
/* Globally enable interrupts */
#asm("sei")
PORTB.4 = 0;
delay_ms(1000);
printf("\n\nDirectory listing for root of logical drive 0:\r\n");
/* Mount logical drive 0: */
if ((res = f_mount(0, &drive)) == FR_OK)
printf("Logical drive 0: mounted OK\r\n");
else
/* An error occurred, display it and stop */
error(res);
printf("%s \r\n", path);
/* Open the file in read mode */
if ((res = f_open(&file, path, FA_READ)) == FR_OK)
printf("1_File %s opened OK\r\n", path);
else
/* An error occurred, display it and stop */
error(res);
/* Read and display the file's content.
make sure to leave space for a NULL terminator
in the buffer, so maximum sizeof(buffer)-1 bytes can be read */
if ((res=f_read(&file,buffer,sizeof(buffer)-1,&nbytes))==FR_OK)
{
printf("%u bytes read\r\n", nbytes);
/* NULL terminate the char string in the buffer */
buffer[nbytes+1]=NULL;
/* Display the buffer contents */
printf("Read text: \"%s\"\r\n", buffer);
}
else
/* An error occurred, display it and stop */
error(res);
/* Close the file */
if ((res = f_close(&file)) == FR_OK)
printf("1_File %s closed OK\r\n\n\n", path);
else
/* An error occurred, display it and stop */
error(res);
/* Create a new file in the root of drive 0:
and set write access mode */
if ((res = f_open(&file,path, FA_OPEN_EXISTING)) == FR_OK)
printf("File %s opened OK\r\n", path);
else
/* An error occurred, display it and stop */
error(res);
/* Close the file */
if ((res = f_close(&file)) == FR_OK)
printf("2_File %s closed OK\r\n\n\n", path);
else
/* An error occurred, display it and stop */
error(res);
if ((res = f_open(&file,path, FA_WRITE | FA_CREATE_ALWAYS)) == FR_OK)
printf("File %s created OK\r\n", path);
else
/* An error occurred, display it and stop */
error(res);
/* Write some text to the file,
without the NULL string terminator sizeof(data)-1 */
if ((res = f_write(&file, text, sizeof(text)-1,&nbytes)) == FR_OK)
printf("%u bytes written of %u\r\n", nbytes, sizeof(text)-1);
else
/* An error occurred, display it and stop */
error(res);
/* Close the file */
if ((res = f_close(&file)) == FR_OK)
printf("3_File %s closed OK\r\n\n\n", path);
else
/* An error occurred, display it and stop */
error(res);
/* Open the file in read mode */
if ((res = f_open(&file,path,FA_READ)) == FR_OK)
printf("1_File %s opened OK\r\n", path);
else
/* An error occurred, display it and stop */
error(res);
/* Read and display the file's content.
make sure to leave space for a NULL terminator
in the buffer, so maximum sizeof(buffer)-1 bytes can be read */
if ((res = f_read(&file, buffer, sizeof(buffer)-1, &nbytes)) == FR_OK)
{
printf("%u bytes read\r\n", nbytes);
/* NULL terminate the char string in the buffer */
buffer[nbytes+1] = NULL;
/* Display the buffer contents */
printf("Read text: \"%s\"\r\n", buffer);
}
else
/* An error occurred, display it and stop */
error(res);
/* Close the file */
if ((res = f_close(&file)) == FR_OK)
printf("4_File %s closed OK\r\n", path);
else
/* An error occurred, display it and stop */
error(res);
printf("done\r\n");
PORTB.4 = 1;
/* Stop here */
while (1);
}
From the documentation:
FR_INVALID_OBJECT
The file/directory object structure is invalid or a null pointer is given. All open objects of the logical drive are invalidated by the volume mount process.
Your file pointer is perhaps null?
Also to consider is that you have configured the library to actually support write operations. In ffconf.h you should have:
#define _FS_READONLY 0