The AVR in use is ATmega2560.
I've got an input signal that has a pulse width of 1 second that is generated.
This signal is attached to an external interrupt pin on my AVR (INT0).
INT0 is being initialized as follows:
Code:
DDRD &= ~(1 << PD0);
PORTD |= (1 << PD0);
EIMSK = 1 << INT0; // enable
EICRA |= (1 << ISC00) | (1 << ISC01); // trigger on rising edge
sei(); //global interrupts
The mission of the ISR for this external interrupt is to a) figure out which edge (first case should be rising) and b) perform action based on which edge
The ISR looks something like this:
Code:
ISR(INT0_vect)
{
if(EICRA == 0x02)
{
// falling edge detected
doFallingEdgeFunction_lightLED0();
// quickly change the trigger to capture opposite edge
EICRA |= (1 << ISC00) | (1 << ISC01); // trigger on rising edge
}
else if(EICRA == 0x03)
{
doRisingEdgeFunction_lightLED1();
// change trigger on falling edge
EICRA = (1 << ISC01);
EICRA &= ~(1 << ISC00);
}
}
It is able to detect the edges; the correct LEDs light up, but for some reason changing the edge interrupt bit in the ISR decreases my input signal to 0.1 second width instead of the full 1 second width.
On the scope, I see the original signal being mirrored but with 10x less width! If I remove the "switching trigger" items, the signal is fine.
From section 15.2.2, p. 114
Note:
1. n = 3, 2, 1or 0. When changing the ISCn1/ISCn0 bits, the interrupt must be disabled by clearing its Interrupt Enable bit in the EIMSK
Register. Otherwise an interrupt can occur when the bits are changed.
EDIT:
EIFR – External Interrupt Flag Register
• Bits 7:0 – INTF7:0: External Interrupt Flags 7 - 0
When an edge or logic change on the INT7:0 pin triggers an interrupt
request, INTF7:0 becomes set (one). If the I-bit in SREG and the
corresponding interrupt enable bit, INT7:0 in EIMSK, are set (one),
the MCU will jump to the interrupt vector. The flag is cleared when
the interrupt routine is executed. Alternatively, the flag can be
cleared by writing a logical one to it.
Try clearing the flag in the interrupt
EIFR = (1 << INT0);
Don't use
EIFR |= (1<< INT0); // DO NOT USE
That will clear all pending flags.
Related
Win 10, STM32CubeIDE 1.8.0
STM32F746 Discovery board
MCU: STM32F746 (Datasheet, Reference Manual, Errata)
Flash: MT25QL128ABA1EW9-0SIT (Datasheet)
Equipment: Low end oscilloscope, low end logic analyzer with decoder
What I'm trying to achieve: I want to be able to send command via indirect write (works OK), read register with indirect read (fails miserably with consistent garbage on the lines), haven't even tried to read/write actual memory.
Connections (from the discovery board schematic):
Interestingly enough, the example provided by STMicoelectronics themselves also doesn't work as expected. More on that later.
Initially, I read the reference manual and tried to figure the stuff out on my own, as I always do when I learn to operate new peripheral. It didn't exactly work out, so I used TouchGFX-generated code to compare configuration (it's using memory mapped mode, but I could at least check clock and GPIO setup, and it was correct), then I also found pretty much the only other article that does what I do. I was close, but a few unexplained (not covered in reference manual) bits from the article made it work. (Article)
I used only the early code from there. Up to and including the first bit of code under "Initialization" section, but not anything further. I adjusted it for my Flash size (128Mbit).
I will safely assume my clock and GPIO initialization is correct, as it matches TouchGFX code, which utilizes QSPI Flash, as well as example program from STM32F7 package.
I'm configuring QSPI with 1MHz clock. While it's not exactly covered in specs of the Flash IC, it's easier on my scope and logic analyzer, both of which have 100MHz as documented upper bound, but they're not really usable there. I also tried to use 108MHz, which is a documented Flash IC spec, I still get garbage there (found via debugging output).
QSPI setup:
void qspi_setup_indirect_mode(void) {
/* ------------ QSPI Initialization --------------- */
/*
* Make sure QSPI is disabled
* */
QUADSPI->CR &= ~(QUADSPI_CR_EN);
/*
* Flash size 128Mb=16MB=2^24 bytes
* 24-bit Address
*
* */
QUADSPI->DCR = 0x00; //reset
QUADSPI->CCR = 0x00; //reset
QUADSPI->DCR |= (23U << QUADSPI_DCR_FSIZE_Pos);
QUADSPI->CCR |= (2U << QUADSPI_CCR_ADSIZE_Pos);
/*
* Sample shift 1/2 clock cycle
* Prescaler = 2 (216MHz/216 = 1MHz)
*
* */
QUADSPI->CR = 0x00; //reset
QUADSPI->CR |= (QUADSPI_CR_SSHIFT | (215U << QUADSPI_CR_PRESCALER_Pos));
/*
* Make sure all flags are cleared
*
* */
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF;
/*
* Enable peripheral
* */
//QUADSPI->CR |= (QUADSPI_CR_EN); (enable later for every transmission)
}
Then there is function, that sets command mode. It sets access mode (indirect write, read, polling, memory mapped), as well as on how many datalines instruction, address and so on are transmitted (from none to 4), and dummy cycles. Nothing fancy, very similar to the one from the example.
void qspi_set_command_mode(uint8_t fmode, uint8_t imode, uint8_t admode, uint8_t abmode, uint8_t dcyc, uint8_t dmode) {
/*
* Make sure QSPI is disabled
* */
QUADSPI->CR &= ~(QUADSPI_CR_EN);
/*
* Communication configuration register
* First, reset all mode values
* Set new values
* */
QUADSPI->CCR = QUADSPI->CCR & ~(QUADSPI_CCR_FMODE) & ~(QUADSPI_CCR_IMODE) & ~(QUADSPI_CCR_ADMODE) & ~(QUADSPI_CCR_ABMODE) & ~(QUADSPI_CCR_DCYC)
& ~(QUADSPI_CCR_DMODE);
QUADSPI->CCR = QUADSPI->CCR | (fmode << QUADSPI_CCR_FMODE_Pos) | (imode << QUADSPI_CCR_IMODE_Pos) | (admode << QUADSPI_CCR_ADMODE_Pos)
| (abmode << QUADSPI_CCR_ABMODE_Pos) | (dcyc << QUADSPI_CCR_DCYC_Pos) | (dmode << QUADSPI_CCR_DMODE_Pos);
}
I tried various minor changes to these functions, and write works if and only if I disable peripheral, configure the thing, enable, set the instruction. If I enable peripheral in the setup section, write doesn't work. This is not covered in reference manual, I found it in the article (where it's not pointed out).
void qspi_sendCommandIndirectWrite(uint8_t command) {
QUADSPI->CR &= ~(QUADSPI_CR_EN); //disable qspi to configure
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF; //clear all flags
qspi_set_command_mode(0x00, 0x01, 0x00, 0x00, 0x00, 0x00); //Set indirect write, only instruction on 1 line, everything else off
QUADSPI->CCR &= ~(0xFF << QUADSPI_CCR_INSTRUCTION_Pos); //clear instruction field
QUADSPI->CR |= (QUADSPI_CR_EN);
QUADSPI->CCR |= (command << QUADSPI_CCR_INSTRUCTION_Pos); //writing instruction starts communication
while (QUADSPI->SR & QUADSPI_SR_BUSY); // Wait for the transaction to complete, and disable the peripheral.
QUADSPI->CR &= ~(QUADSPI_CR_EN);
}
void qspi_sendCommandIndirectRead(uint8_t command, uint8_t receiveBuffer[], uint32_t length) {
QUADSPI->CR &= ~(QUADSPI_CR_EN); //disable qspi to configure
QUADSPI->FCR = QUADSPI_FCR_CTOF | QUADSPI_FCR_CSMF | QUADSPI_FCR_CTCF | QUADSPI_FCR_CTEF; //clear all flags
qspi_set_command_mode(0x01, 0x01, 0x00, 0x00, 0x01, 0x01); //Set indirect write, only instruction on 1 line, , data on 1 line, 1 dummy cycle, everything else off
QUADSPI->CCR &= ~(0xFF << QUADSPI_CCR_INSTRUCTION_Pos); //clear instruction field
QUADSPI->DLR = length;
QUADSPI->CR |= (QUADSPI_CR_EN);
QUADSPI->CCR |= (command << QUADSPI_CCR_INSTRUCTION_Pos); //writing instruction starts communication
uint32_t counter = 0x00;
while (counter < length) {
while (!(QUADSPI->SR & QUADSPI_SR_TCF)); //wait while data arrives to FIFO
receiveBuffer[counter] = (uint8_t) (0xFF & QUADSPI->DR);
counter++;
}
while (QUADSPI->SR & QUADSPI_SR_BUSY); // Wait for the transaction to complete, and disable the peripheral.
QUADSPI->CR &= ~(QUADSPI_CR_EN);
}
Finally, all of that is called in the main the following way:
#include "main.h"
void system_hw_setup(void);
void qspi_example(void);
int main(void) {
system_hw_setup(); //initialize hardware
system_msdelay(100U);
//qspi_sendCommandIndirectWrite(MT25QL128ABA1EW9_COMMAND_ENTER_QUAD_IO_MODE); //works OK
//qspi_example(); //example provided by STM32 w clock and GPIO setup
system_msdelay(100U);
uint8_t test[1];
while (1) {
qspi_sendCommandIndirectRead(MT25QL128ABA1EW9_COMMAND_READ_STATUS_REGISTER, test, 1);
//qspi_sendCommandIndirectRead(MT25QL128ABA1EW9_COMMAND_READ_ENHANCED_VOLATILE_CONFIGURATION_REGISTER, test, 1);
system_msdelay(100U);
toggle_stm32f746disco_ld1();
/*
test[0] = 0x00;
if (test[0] == 0x00) {
test[0] = (uint8_t) '0';
}
/usart_dma_sendArray(USART1, test, 1); */
}
}
void system_hw_setup(void) {
rcc_setup(); //clock for peripheral, clock will not be altered; therefore default HSI 16MHz
systick_setup(SYSTEM_FREQUENCY); //activate systick
gpio_setup(); //set pin modes and functions
dma_reset_flags(DMA2); //clear DMA2 flags for USART1
dma_reset_flags(DMA1); //clear DMA1 flags for I2C3
usart_dma_setup(USART1); //set control registers and settings for USART1 and its DMA connected to st-link
usart_enable(USART1); //enable uart1
usart_enable_tx(USART1); //enable tx line (wrapper)
usart_enable_rx(USART1); //enable rx line (wrapper)
qspi_setup_indirect_mode(); //enable qspi in indirect mode
nvic_setup(); //set interrupts and their priorities
}
which gives the following:
As per reference manual, data from the IC should come on DQ1, but it's not happening. Also, sometimes DQ3 randomly goes up for some time. The number of clock cycles is strange. Also, I have no idea why there is some 0x80 packet there, I'm sending only the instruction and nothing else. It could be related to my artificially lowered clock speed, but the same configuration also miserably fails if I set QSPI clock to proper value.
I'm pretty lost at what I'm doing wrong, and the reference manual section of the MCU is not much help at this point, and there are next to no resources on the internet that cover it in a meaningful (or any, at this point) way.
I would appreciate any help or advice with making QSPI work!
The main problem was the access to data register. QUADSPI->DR is a volatile uint32_t. So whenever I access QUADSPI->DR, even if I received 1 byte, it reads 4 bytes from the register, and it also produces gibberish with FIFO threshold because of that. The correct solution is to explicitly specify byte, half-word or word access to the QUADSPI->DR. I take address of the data register, cast it as a pointer to uint8_t or uint16_t, and dereference it:
uint32_t mydata = QUADSPI->DR;
uint16_t mydata = *(uint16_t*)(&QUADSPI->DR);
uint8_t mydata = *(uint8_t*)(&QUADSPI->DR);
I am trying to implement the WINC1500 MLA Driver to work with the ATMEGA2561 MCU and I have written my driver code and it's stuck on the line "while((SPSR & (1 << SPIF)) == 0);" in the m2mStub_SpiTxRx function.
I have no idea why it's not progressing through. I'm using the jumpstart ImageCraft IDE for this project.
Here's the implementation of it
void m2mStub_SpiTxRx(uint8_t *p_txBuf,
uint16_t txLen,
uint8_t *p_rxBuf,
uint16_t rxLen)
{
uint16_t byteCount;
uint16_t i;
// Calculate the number of clock cycles necessary, this implies a full-duplex SPI.
byteCount = (txLen >= rxLen) ? txLen : rxLen;
DEBUGOUTF("Calculate the number of clock cycles\n");
DEBUGOUTF("byteCount %d", byteCount, "\n");
DEBUGOUTF("txLen %d", txLen, "\n");
DEBUGOUTF("rxLen %d", rxLen, "\n");
// Read / Transmit.
for (i = 0; i < byteCount; ++i)
{
// Wait for transmitter to be ready. (This is causing the entire thing to crash)
while((SPSR & (1 << SPIF)) == 0);
// Transmit.
if (txLen > 0)
{
// Send data from the transmit buffer.
SPDR = (*p_txBuf++);
--txLen;
}
else
{
// No more Tx data to send, just send something to keep clock active.
SPDR = 0x00U;
}
// Wait for transfer to finish.
while((SPSR & (1 << SPIF)) == 0);
// Send dummy data to slave, so we can read something from it.
SPDR = 0x00U;
// Wait for transfer to finish.
while((SPSR & (1 << SPIF)) == 0);
// Read or throw away data from the slave as required.
if (rxLen > 0)
{
*p_rxBuf++ = SPDR;
--rxLen;
}
else
{
// Clear the registers
volatile uint8_t reg_clear = 0U;
reg_clear = SPDR;
(void)reg_clear;
}
}
}
I don't have enough information to say for sure, but my assumption is that your SPI connection is not set up correctly.
In particular, I guess you forgot to set /SS as output, same as this problem or this.
In the datasheet it says:
Master Mode When the SPI is configured as a master (MSTR in SPCR is
set), the user can determine the direction of the SS pin.
If SS is configured as an output, the pin is a general output pin
which does not affect the SPI system. Typically, the pin will be
driving the SS pin of the SPI slave.
If SS is configured as an input, it must be held high to ensure Master
SPI operation. If the SS pin is driven low by peripheral circuitry
when the SPI is configured as a master with the SS pin defined as an
input, the SPI system interprets this as another master selecting the
SPI as a slave and starting to send data to it. To avoid bus
contention, the SPI system takes the following actions:
The MSTR bit in SPCR is cleared and the SPI system becomes a slave. As a result of the SPI becoming a slave, the MOSI and SCK pins become
inputs.
The SPIF flag in SPSR is set, and if the SPI interrupt is enabled, and the I-bit in SREG is set, the interrupt routine will be executed.
Thus, when interrupt-driven SPI transmission is used in master mode,
and there exists a possibility that SS is driven low, the interrupt
should always check that the MSTR bit is still set. If the MSTR bit
has been cleared by a slave select, it must be set by the user to
re-enable SPI master mode.
So, you just need to configure the /SS pin as output and set to high in your init code, this should solve your problem:
DDRB |= (1 << PB0); // Set /SS (PB0) as output
PORTB |= (1 << PB0); // Set /SS (PB0) high
I'm trying to write a (fairly) basic bit of software for and Attiny204, which handles interrupts when a clock input is pulled high.
When I run the code in the debug simulator in Atmel Studio, and set the clock input high, there is no interrupt flag generated. The interrupts do trigger when I manually produce an interrupt flag.
I've tried using different pins, and even, the other port. I can't seem to get the simulator to produce the interrupt flag.
In the past, I have used the AtMega328P in the simulator with equivalent code, and it works fine.
ISR(PORTA_PORT_vect)
{
//In this function we must:
//1. Shift all data up
shiftUp();
//2. Get new 8th bit
bit8 = VPORTA.IN & (1 << 1);
//3. Set Data Output Pin to bit0
if(bit0 == 0)
VPORTA.OUT &= ~(1 << 3);
else
VPORTA.OUT |= (1 << 3);
//4. Calculate new dimValue and dimMilliseconds
calcDim();
calcDelay();
}
int main(void)
{
initVariables();
/*
Below this, we must set the Data Direction (DD) of each pin we assigned.
*/
//Below, set the ISC of the Zero Cross Pin and the Clock Pin to allow interrupts
PORTA_PIN0CTRL |= 0b00000001; //Zero Cross
//PORTA_PIN1CTRL = 0b00000000; //Data In
//PORTA_PIN2CTRL = 0b00000000; //Data Next
//PORTA_PIN3CTRL = 0b00000000; //Triac Control
PORTB_PIN0CTRL |= 0b00000001; //Clock
//VPORTB.INTFLAGS |= 0b00000001;
//Set Port direction.
VPORTA.DIR = 0x30;
VPORTB.DIR = 0x00;
/*
Below this, we must enable interrupts.
*/
sei();
/* Replace with your application code */
while (1)
{
}
}
Why are you writing to VPORTA and VPORTB? The Tiny204 doesn´t have this registers.
You enable interrupts on both edges for Pin 0 of Port A (PIN0CTRL bit 0 is set = BOTHEDGES) and you don´t clear the interrupt flag in your ISR of Port A. Please take a look at the data sheet:
The interrupt request remains active until the interrupt flag is cleared. See the peripheral's INTFLAGSregister for details on how to clear interrupt flags.
The following is the interrupt handler for LED0 to toggle when an interrupt occurs on the UART0 due to LIN transmission from master to slave, my device is the slave. But without connecting my device(slave) to the master that means without any LIN transmission over UART, the LED0 is toggling. I cannot understand, how can this happen automatically? How can an interrupt be generated and Toggle of LED0 can happen?
void FTM0_IRQHandler()
{
if (1==((FTM0_C0SC & FTM_CnSC_CHF_MASK)>>FTM_CnSC_CHF_SHIFT) ) /* If the CHF of the channel is equal to 0 */
{
(void)FTM0_C0SC; /* Read to clear flag */
FTM0_C0SC ^= FTM_CnSC_CHF_MASK; /* Clear flag */
FTM0_C0V = FTM0_C0V + 391 ; /* Refresh interrupt period */
if (LED_counter>=50){
/* Toggle LED for LIN transmission */
/* Reset counter */
LED0_TOGGLE;
LED_counter = 0;
}
LED_counter++;
}
}
In my main funtion I have called my timer initialization as follows:
lin_application_timer_FTM0();
And the above function is defined as follows:
void lin_application_timer_FTM0()
{
SIM_SCGC |= SIM_SCGC_FTM0_MASK; /* Enable Clock for FTM0 */
FTM0_SC |= FTM_SC_PS(7); /* Select Preescaler in this case 128. 20 Mhz /128 =156.25 Khz. */
/* Counter increase by one every 6.4 us */
/* Enable Channle 0*/
FTM0_C0SC |= FTM_CnSC_CHIE_MASK; /* Enable channel 0 interrupt */
FTM0_C0SC |= FTM_CnSC_MSA_MASK; /* Channel as Output compare mode */
/*Select interrupt frequency*/
FTM0_C0V = FTM_CnV_VAL(391) ; /* Interrupt every 2.5ms */
FTM0_SC |= FTM_SC_CLKS(1); /*FTM0 use system clock*/
/* Set the ICPR and ISER registers accordingly */
NVIC_ICPR |= 1 << ((INT_FTM0-16)%32);
NVIC_ISER |= 1 << ((INT_FTM0-16)%32);
}
Can anyone explain why is the LED0 toggling without any interrupt?
It’s possible that the interrupt is actually being triggered. Does your interrupt pin have a pull up or pull-down resistor attached? If it’s disconnected and just “floating”, electrical noise in the environment can cause spurious interrupts.
I am using STM32F4 board as slave receiver and nordic board as master transmitter. I am able to send the slave address as 0x30 which is acknowledged by the slave and I send the device register address as 0x10 and I then send some data using
i2c_write(0x30, 0x10, data, 4);
I am able to get the events in my interrupt service routine. I received 0x00020002 for "I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED". I then received 0x00020044 event and the clock stopped running. Can anyone please help me with this. By scoping, I saw Slave address, device register address and my first data with clock on the oscilloscope. But after that clock stopped.
I'm able to use STM32F4 as Master transmitter and read some sensors but I find it difficult to use STM32F4 as Slave receiver with nordic board as Master Transmitter
void i2c_init2()
{
GPIO_InitTypeDef gpio_init;
I2C_InitTypeDef i2c_init;
NVIC_InitTypeDef NVIC_InitStructure, NVIC_InitStructure2;
I2C_DeInit(I2C2 ); //Deinit and reset the I2C to avoid it locking up
I2C_SoftwareResetCmd(I2C2, ENABLE);
I2C_SoftwareResetCmd(I2C2, DISABLE);
/*!< I2C Periph clock enable */
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);
/* setup SCL and SDA pins
* SCL on PB10 and SDA on PB11
*/
gpio_init.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11; // we are going to use PB10 and PB11
gpio_init.GPIO_Mode = GPIO_Mode_AF; // set pins to alternate function
gpio_init.GPIO_Speed = GPIO_Speed_50MHz; // set GPIO speed
gpio_init.GPIO_PuPd = GPIO_PuPd_UP; //Pull up resistor
gpio_init.GPIO_OType = GPIO_OType_OD; //Open Drain
GPIO_Init(GPIOB, &gpio_init);
// Connect I2C2 pins to AF
GPIO_PinAFConfig(GPIOB, GPIO_PinSource10, GPIO_AF_I2C2 ); // SCL
GPIO_PinAFConfig(GPIOB, GPIO_PinSource11, GPIO_AF_I2C2 ); // SDA
/* Configure the Priority Group to 1 bit */
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitStructure.NVIC_IRQChannel = I2C2_EV_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
NVIC_InitStructure2.NVIC_IRQChannel = I2C2_ER_IRQn;
NVIC_InitStructure2.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure2.NVIC_IRQChannelSubPriority = 0;
NVIC_InitStructure2.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure2);
I2C_ITConfig(I2C2, I2C_IT_EVT, ENABLE);
I2C_ITConfig(I2C2, I2C_IT_ERR, ENABLE);
I2C_ITConfig(I2C2, I2C_IT_BUF, ENABLE);
i2c_init.I2C_ClockSpeed = 100000;
i2c_init.I2C_Mode = I2C_Mode_I2C;
i2c_init.I2C_DutyCycle = I2C_DutyCycle_2;
i2c_init.I2C_OwnAddress1 = 0x30;
i2c_init.I2C_Ack = I2C_Ack_Enable;
i2c_init.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_Init(I2C2, &i2c_init);
I2C_StretchClockCmd(I2C2, ENABLE);
I2C_Cmd(I2C2, ENABLE);
}
void I2C2_ER_IRQHandler(void)
{
/* Read SR1 register to get I2C error */
if ((I2C_ReadRegister(I2C2, I2C_Register_SR1 ) & 0xFF00) != 0x00)
{
STM_EVAL_LEDOn(LED6);
/* Clears error flags */
I2C2 ->SR1 &= 0x00FF;
}
}
void I2C2_EV_IRQHandler(void)
{
uint8_t dataRX;
Event = I2C_GetLastEvent(I2C2 );
printf("Event: 0x%x\n", Event);
switch (Event)
{
case I2C_EVENT_SLAVE_RECEIVER_ADDRESS_MATCHED :
{
printf("Slave Address Matched\n");
STM_EVAL_LEDOn(LED4);
I2C2 ->SR1;
I2C2 ->SR2;
break;
}
case I2C_EVENT_SLAVE_BYTE_RECEIVED :
{
printf("Slave Byte Received\n");
dataRX = I2C_ReceiveData(I2C2 );
break;
}
case I2C_EVENT_SLAVE_ACK_FAILURE :
{
STM_EVAL_LEDOn(LED3);
I2C2 ->SR1 &= 0x00FF;
break;
}
case I2C_EVENT_SLAVE_STOP_DETECTED :
{
I2C2 ->SR1;
I2C2 ->CR1 |= 0x1;
break;
}
}
}
"These conditions would seem to be met, explaining why the STM32F4 I2C slave is stretching (stalling) the clock. It would appear that you need to read from the data register to allow it to continue - in effect, match this as an event and do that."
I did exactly what you said and it works as expected. Read the reference manual bit late. :)
I then received 0x00020044 event and the clock stopped running.
The term "event" is being used a bit loosely. What ST's header files do is define events as certain combinations of flags. You have a slightly different combination. Breaking it down, the following bits are set:
#define I2C_FLAG_BUSY ((uint32_t)0x00020000)
#define I2C_FLAG_RXNE ((uint32_t)0x10000040)
#define I2C_FLAG_BTF ((uint32_t)0x10000004)
(There are actually two sets in different registers - by the definition of your known events it looks like that leading "1" from the latter group gets dropped when they are combined, but I'm not 100% sure of that)
Looking in the reference manual, there is the following:
If RxNE is set and the data in the DR register is not read before the end of the next data
reception, the BTF bit is set and the interface waits until BTF is cleared by a read from the
I2C_DR register, stretching SCL low
These conditions would seem to be met, explaining why the STM32F4 I2C slave is stretching (stalling) the clock. It would appear that you need to read from the data register to allow it to continue - in effect, match this as an event and do that.
I further suspect you get in this condition when you have actually received two words - the one in the receive buffer signified by RXNE, and another in the receiver itself signified by BTF. At that point it is stuck and cannot accept any more - you might consider catching RXNE by itself by adding an interrupt enable for that, possibly improving efficiency by claiming the first word earlier before the second has finished receiving.
If you manage to get it to completely work, feel free to write your own exactly-what-you-did answer and accept that.
Make sure you are handling all possible error conditions and interrupt causes:
I2C_IT_SMBALERT: SMBus Alert flag
I2C_IT_TIMEOUT: Timeout or Tlow error flag
I2C_IT_PECERR: PEC error in reception flag
I2C_IT_OVR: Overrun/Underrun flag (Slave mode)
I2C_IT_AF: Acknowledge failure flag
I2C_IT_ARLO: Arbitration lost flag (Master mode)
I2C_IT_BERR: Bus error flag
I2C_IT_TXE: Data register empty flag (Transmitter)
I2C_IT_RXNE: Data register not empty (Receiver)
I2C_IT_STOPF: Stop detection flag (Slave mode)
I2C_IT_ADD10: 10-bit header sent flag (Master mode)
I2C_IT_BTF: Byte transfer finished flag
I2C_IT_ADDR: Address sent flag (Master mode) "ADSL"
Some of these are cleared by a write to the bit in SR1. Some are cleared by a DR read, some by a DR read OR write. Some require a read of SR1 then a read of SR2. Some require a read of SR1 then a write to CR1. The I2C section of the reference manual has all the information, you just have to wade through it. It takes some time but it is worth it. Start with section 18.6.6 I2C Status register 1 (I2C_SR1) in the reference manual RM0368. Google "RM0268 stm32f4". Several of the interrupt causes (STOPF, ADDR, TXE, RXNE) have a weird way of being cleared. Some are standard and can be cleared by a write to the but in SR1.