CMSIS USB firmware for stm32f401. Fails to send device descriptor with EOVERFLOW (-75) error - embedded

I am trying to get USB device code working on my stm32f401 microcontroller. So far, I'm able to read the packets from the host, send the first device request, receive and apply the assigned address. After that, the host requests for the device descriptor again, and my device doesn't response to that for some reason. I've been fighting this issue for weeks now, seems like I need help with it.
In my code, after initializing the peripherals, I set up my endpoint 0 and set its Tx FIFO size to 16. When the device receives a packet, it reads the packet status and reads the packet content in function setup_packet_handler(). This function prints the content word by word:
uint32_t data = *fifo;
printf("Rx from FIFO: 0x%08x\n", data);
void rxflvl_handler() {
uint32_t packet_status = USB_OTG_FS->GRXSTSP;
uint8_t endpoint_number = (packet_status & USB_OTG_GRXSTSP_EPNUM_Msk) >> USB_OTG_GRXSTSP_EPNUM_Pos;
uint16_t bcnt = (packet_status & USB_OTG_GRXSTSP_BCNT_Msk) >> USB_OTG_GRXSTSP_BCNT_Pos;
uint16_t pktsts = (packet_status & USB_OTG_GRXSTSP_PKTSTS_Msk) >> USB_OTG_GRXSTSP_PKTSTS_Pos;
printf("Received packet of status 0x%02x\n", pktsts);
switch (pktsts) {
case 0x06: // SETUP packet (includes data)
setup_packet_handler(endpoint_number, bcnt);
break;
case 0x02: // OUT packet (includes data)
break;
case 0x04: // SETUP stage has completed
case 0x03: // OUT transfer has completed
// Re-enable the endpoint
OUT_ENDPOINT(endpoint_number)->DOEPCTL |= USB_OTG_DOEPCTL_CNAK;
OUT_ENDPOINT(endpoint_number)->DOEPCTL |= USB_OTG_DOEPCTL_EPENA;
break;
}
}
My main() function watches the current number of packets to send and free space of the Tx FIFO for endpoint 0:
int main(void)
{
/* Pins initialization */
/* USB initialization */
in_packets = (in_endpoint->DIEPTSIZ & USB_OTG_DIEPTSIZ_PKTCNT_Msk) >> USB_OTG_DIEPTSIZ_PKTCNT_Pos;
fifo_space = in_endpoint->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk;
printf("FIFO free space: %i\n", fifo_space);
while (1)
{
// Checking for USB interrupts
usb_poll();
// Notify if packets number changed
uint32_t npkts = (in_endpoint->DIEPTSIZ & USB_OTG_DIEPTSIZ_PKTCNT_Msk) >> USB_OTG_DIEPTSIZ_PKTCNT_Pos;
if (npkts != in_packets) {
printf("Packets number update: %i\n", npkts);
in_packets = npkts;
}
// Notify if FIFO space changed
uint16_t new_space = in_endpoint->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk;
if (new_space != fifo_space) {
fifo_space = new_space;
printf("FIFO free space: %i\n", new_space);
}
}
}
Now the broken part, the code which sends the device descriptor. The function handle_descriptor_request() is called when the device descriptor request is received.
The function sets the size to transmit to 18; packet count to 3 (endpoint 0 max packet size is configured to 8); enables the endpoint and fills the FIFO. After that, prints the FIFO free space, to ensure it has decreased.
void handle_descriptor_request(void) {
printf("Received descriptor request\n");
uint32_t* fifo = (USB_OTG_FS_PERIPH_BASE + USB_OTG_FIFO_BASE +
0 * 0x1000);
USB_OTG_INEndpointTypeDef* in_endpoint = (USB_OTG_INEndpointTypeDef*)(
USB_OTG_FS_PERIPH_BASE +
USB_OTG_IN_ENDPOINT_BASE +
0
);
// flush_txfifo(0);
// in_endpoint->DIEPTSIZ = 0;
// in_endpoint->DIEPCTL |= USB_OTG_DIEPCTL_SNAK;
// in_endpoint->DIEPCTL |= USB_OTG_DIEPCTL_EPDIS;
printf("Manual PKTCNT to 3\n");
// Set Tx size to 18
in_endpoint->DIEPTSIZ &= ~USB_OTG_DIEPTSIZ_XFRSIZ_Msk;
in_endpoint->DIEPTSIZ &= ~USB_OTG_DIEPTSIZ_PKTCNT_Msk;
in_endpoint->DIEPTSIZ |= 18 << USB_OTG_DIEPTSIZ_XFRSIZ_Pos;
// Send 3 packets (8 bytes max for each)
in_endpoint->DIEPTSIZ |= 3 << USB_OTG_DIEPTSIZ_PKTCNT_Pos;
// Enable transmission
in_endpoint->DIEPCTL |= USB_OTG_DIEPCTL_CNAK;
in_endpoint->DIEPCTL |= USB_OTG_DIEPCTL_EPENA;
// Write a valid device descriptor to the Tx FIFO
*fifo = 0x02000112;
*fifo = 0x08000000;
*fifo = 0x13aa6666;
*fifo = 0x00000000;
*fifo = 0x0000012c;
fifo_space = in_endpoint->DTXFSTS & USB_OTG_DTXFSTS_INEPTFSAV_Msk;
printf("FIFO free space: %i\n", fifo_space);
}
Below is the log I'm getting from the device. I see that the first device request is transmitted successfully, the FIFO size drops from 16 to 11, than back to 16. Number of packets to send decreases from 3 to 2 (although it should decrease to 0, right?). I also see this descriptor with Wireshark. Then the device address is received and answered by a ZLP packet; then something goes wrong. My FIFO space keeps decreasing with every descriptor request, the packets doesn't seem to leave the device.
USB reset received
FIFO free space: 16
INEPNE Endpoint interrupt
TXFE Endpoint interrupt
// First descriptor request. Sent successfully
Received packet of status 0x06
// Device descriptor request in binary form
Rx from FIFO: 0x01000680
Rx from FIFO: 0x00400000
// Output from handle_descriptor_request()
Received descriptor request
Manual PKTCNT to 3
FIFO free space: 11
// Output from main()
Packets number update: 2
FIFO free space: 16
// Output from rxflvl_handler()
Received packet of status 0x04
Received packet of status 0x02
Received packet of status 0x03
INEPNE Endpoint interrupt
TXFE Endpoint interrupt
// Received address
Received packet of status 0x06
// Set address request in binary form
Rx from FIFO: 0x001d0500
Rx from FIFO: 0x00000000
Received address: 29
Writing zero-length packet
Manual PKTCNT to 1
Packets number update: 1
Received packet of status 0x04
Packets number update: 0
TXFRC Endpoint interrupt
TXFE Endpoint interrupt
// Another device descriptor request
Received packet of status 0x06
Rx from FIFO: 0x01000680
Rx from FIFO: 0x00120000
Received descriptor request
Manual PKTCNT to 3
FIFO free space: 11
Packets number update: 3
Received packet of status 0x04
// Looks unsuccessful. Try again...
Received packet of status 0x06
Rx from FIFO: 0x01000680
Rx from FIFO: 0x00120000
Received descriptor request
Manual PKTCNT to 3
FIFO free space: 6
// And so on. The FIFO free space decreases to 0
Here are the Wireshark views of the first and second descriptor responses. The second one fails with EOVERFLOW (-75). Sometimes I see -71 error when run 'dmesg' on my host. Can't figure out what is the reason for that.
If I will be flushing the 0th FIFO before the descriptor transmission, i.e. uncomment the
flush_txfifo(0);
Thing doesn't change a lot: the FIFO free space stops decreasing below 11, but the packets doesn't seem to be sent.
If I recover the endpoint before the descriptor tranmission, uncommenting the
in_endpoint->DIEPTSIZ = 0;
in_endpoint->DIEPCTL |= USB_OTG_DIEPCTL_SNAK;
in_endpoint->DIEPCTL |= USB_OTG_DIEPCTL_EPDIS;
then starting at the second try, the device seems to strart sending something, but still something wrong. The FIFO size increases to 15 words instead of 16
// ...
Rx from FIFO: 0x01000680
Rx from FIFO: 0x00120000
Received descriptor request
Manual PKTCNT to 3
FIFO free space: 11
FIFO free space: 15
Received packet of status 0x04
Received packet of status 0x02
Received packet of status 0x03
Also I can't understand why the number of packets drops from 3 to 2 for the successful transmission. I can see in Wireshark whole descriptor (18 bytes) and the endpoint size is configured to 8.
Could you point me to things I may need to check?

Related

STM32 USB Tx Busy

I have an application running on STM32F429ZIT6 using USB stack to communicate with PC client.
MCU receives one type of message of 686 bytes every second and receives another type of message of 14 bytes afterwards with 0.5 seconds of delay between messages. The 14 bytes message is a heartbeat so it needs to replied by MCU.
It happens that after 5 to 10 minutes of continuous operation, MCU is not able to send data because
hcdc->TxState is always busy. Reception works fine.
During Rx interruption, application only adds data to ring buffer, so that this buffer is later serialized and processed by main function.
static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len) {
/* USER CODE BEGIN 11 */
/* Message RX Completed, Send it to Ring Buffer to be processed at FMC_Run()*/
for(uint16_t i = 0; i < *Len; i++){
ring_push(RMP_RXRingBuffer, (uint8_t *) &Buf[i]);
}
USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]);
USBD_CDC_ReceivePacket(&hUsbDeviceHS);
return (USBD_OK);
/* USER CODE END 11 */ }
USB TX is also kept as simple as possible:
uint8_t CDC_Transmit_HS(uint8_t\* Buf, uint16_t Len) {
uint8_t result = USBD_OK;
/\* USER CODE BEGIN 12 */
USBD_CDC_HandleTypeDef hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceHS.pClassData;
if (hcdc-\>TxState != 0)
{
ZF_LOGE("Tx failed, resource busy\\n\\r"); return USBD_BUSY;
}
USBD_CDC_SetTxBuffer(&hUsbDeviceHS, Buf, Len);
result = USBD_CDC_TransmitPacket(&hUsbDeviceHS);
ZF_LOGD("TX Message Result:%d\\n\\r", result);
/ USER CODE END 12 \*/
return result;
}
I'm using latest HAL Drivers and software from CubeIDE (1.27.1).
I have tried expanding heap min size from 0x200 to larger values but result is the same.
Also Line Coding is set according to what recommended values:
case CDC_SET_LINE_CODING:
LineCoding.bitrate = (uint32_t) (pbuf[0] | (pbuf[1] << 8) | (pbuf[2] << 16) | (pbuf[3] << 24));
LineCoding.format = pbuf[4];
LineCoding.paritytype = pbuf[5];
LineCoding.datatype = pbuf[6];
ZF_LOGD("Line Coding Set\n\r");
break;
case CDC_GET_LINE_CODING:
pbuf[0] = (uint8_t) (LineCoding.bitrate);
pbuf[1] = (uint8_t) (LineCoding.bitrate >> 8);
pbuf[2] = (uint8_t) (LineCoding.bitrate >> 16);
pbuf[3] = (uint8_t) (LineCoding.bitrate >> 24);
pbuf[4] = LineCoding.format;
pbuf[5] = LineCoding.paritytype;
pbuf[6] = LineCoding.datatype;
ZF_LOGD("Line Coding Get\n\r");
break;
Thanks in advance, any support is appreciated.
I don't know enough about the STM32 libraries to really check your code, but I suspect you are forgetting to read the bytes transmitted by the STM32 on PC side. Try opening a terminal program like PuTTY and connecting to the STM32's virtual serial port. Otherwise, the Windows USB-to-serial driver (usbser.sys) will eventually have its buffers filled with data from your device and it will stop requesting more, at which point the buffers on your device will fill up as well.

The SPI shift register does not always send all data to the RX BUFFER (TI microcontroller)

I have connected 2 TI controllers via SPI. The TMS320F28055 controller is my master and the TMS320F2885 controller is my slave. I want to send complete data to the slave via spi. The data always ends up successfully in the SPIDAT register, i.e. the shift register. The shift register should then send the data to the SPIRXBUF - Buffer. Sometimes the data is successfully sent to the buffer and sometimes not it's always very random I've tried a lot. I don't use a FIFO. Does anyone know how I can fix the problem.
I made a table showing the data in the master and slave registers. I also send the configuration of the slave and master.
void spi_init(void)
{
SpiaRegs.SPICTL.all = 0x000E; //Normal SPI clocking scheme(Data in latch on rising edge)master, 4-pin option, No interrupt
SpiaRegs.SPICTL.bit.CLK_PHASE = 1; //1
SpiaRegs.SPIBRR = 0x0077; //BateRate 0.5MHz
SpiaRegs.SPICCR.all = 0x0087; //SPI is ready to transmit or receive the next character.
SpiaRegs.SPICCR.bit.CLKPOLARITY = 0; //0
SpiaRegs.SPIPRI.bit.FREE = 1;
}
This is the code from my master, I use the TMS320F28055:
void spi_init(void)
{
SpiaRegs.SPICCR.bit.SPISWRESET = 0;
SpiaRegs.SPICTL.all = 0x000A; //8 //Normal SPI clocking scheme(Data in latch on rising edge)slave, 4-pin option, No interrupt
SpiaRegs.SPICTL.bit.CLK_PHASE = 1; //1
SpiaRegs.SPIBRR = 0x0077; //BateRate 0.5MHz ist für den Slave nicht notwendig
SpiaRegs.SPICCR.all = 0x0087; //SPI is ready to transmit or receive the next character.
SpiaRegs.SPICCR.bit.CLKPOLARITY = 0; //0
SpiaRegs.SPICTL.bit.SPIINTENA = 1 ;
SpiaRegs.SPICTL.bit.OVERRUNINTENA = 1 ;
SpiaRegs.SPIPRI.bit.FREE = 1;
SpiaRegs.SPICCR.bit.SPISWRESET=1;
}
And this is the code from my slave TMS320F28035.
I'm using an interrupt here, but I've also tried it without an interrupt.
uint16_t pdata = 0x1234;
int dataH, dataL;
dataH = 0;
dataL = 0;
dataH = (pdata >> 8);
dataL = (pdata & 0x00FF);
spi_xmit(dataH);
spi_xmit(dataL);
And with that I send example data, in this case it would be the 0x1234. When I send it it arrives successfully in the shift register and buffer. But if I want to send it more often, the shift register does not completely shift the data into the buffer. To check I debug both microcontrollers at the same time. By the way, I send 8 bits twice in a row. the buffer has a size of 16 bits.

ATMEGA2561 WINC1500 Driver implementation SPI problem

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

SMBus (I2C) sending extra ACK then intended

I'm trying to get a basic handshake going. Below is the ISR for the C8051F120's SMBus (System Management Bus). I'm trying to implement an I2C device on it (ads1115 7addr 0x48 for those who are curious). Note this is mainly the example given by silicon labs for the F120.
void SMBUS_ISR (void) interrupt 7
{
bit FAIL = 0; // Used by the ISR to flag failed
// transfers
static unsigned char sent_byte_counter;
static unsigned char rec_byte_counter;
// Status code for the SMBus (SMB0STA register)
switch (SMB0STA)
{
// Master Transmitter/Receiver: START condition transmitted.
// Load SMB0DAT with slave device address.
case SMB_START: //0x08
// Master Transmitter/Receiver: repeated START condition transmitted.
// Load SMB0DAT with slave device address
case SMB_RP_START: //0x10
SMB0DAT = TARGET; // Load address of the slave.
SMB0DAT &= 0xFE; // Clear the LSB of the address for the
// R/W bit
SMB0DAT |= SMB_RW; // Load R/W bit
STA = 0; // Manually clear STA bit
rec_byte_counter = 1; // Reset the counter
sent_byte_counter = 1; // Reset the counter
break;
// Master Transmitter: Slave address + WRITE transmitted. ACK received.
// For a READ: N/A
//
// For a WRITE: Send the first data byte to the slave.
case SMB_MTADDACK: //0x18
SMB0DAT = SMB_DATA_OUT[sent_byte_counter-1];
sent_byte_counter++;
break;
// Master Transmitter: Slave address + WRITE transmitted. NACK received.
// Restart the transfer.
case SMB_MTADDNACK: //0x20
STA = 1; // Restart transfer
break;
// Master Transmitter: Data byte transmitted. ACK received.
// For a READ: N/A
//
// For a WRITE: Send all data. After the last data byte, send the stop
// bit.
case SMB_MTDBACK: //0x28
if (sent_byte_counter <= NUM_BYTES_WR)
{
// send data byte
SMB0DAT = SMB_DATA_OUT[sent_byte_counter-1];
sent_byte_counter++;
}
else
{
STO = 1; // Set STO to terminate transfer
SMB_BUSY = 0; // And free SMBus interface
}
break;
// Master Transmitter: Data byte transmitted. NACK received.
// Restart the transfer.
case SMB_MTDBNACK: //0x30
STA = 1; // Restart transfer
break;
// Master Receiver: Slave address + READ transmitted. ACK received.
// For a READ: check if this is a one-byte transfer. if so, set the
// NACK after the data byte is received to end the transfer. if not,
// set the ACK and receive the other data bytes.
//
// For a WRITE: N/A
case SMB_MRADDACK: //0x40
if (rec_byte_counter == NUM_BYTES_RD)
{
AA = 0; // Only one byte in this transfer,
// send NACK after byte is received
}
else
{
AA = 1; // More than one byte in this transfer,
// send ACK after byte is received
}
break;
// Master Receiver: Slave address + READ transmitted. NACK received.
// Restart the transfer.
case SMB_MRADDNACK: //0x48
STA = 1; // Restart transfer
break;
// Master Receiver: Data byte received. ACK transmitted.
// For a READ: receive each byte from the slave. if this is the last
// byte, send a NACK and set the STOP bit.
//
// For a WRITE: N/A
case SMB_MRDBACK: //0x50
if (rec_byte_counter < NUM_BYTES_RD)
{
SMB_DATA_IN[rec_byte_counter-1] = SMB0DAT; // Store received byte
AA = 1; // Send ACK to indicate byte received
rec_byte_counter++; // Increment the byte counter
}
else
{
AA = 0; // Send NACK to indicate last byte
// of this transfer
}
break;
// Master Receiver: Data byte received. NACK transmitted.
// For a READ: Read operation has completed. Read data register and
// send STOP.
//
// For a WRITE: N/A
case SMB_MRDBNACK: //0x58
SMB_DATA_IN[rec_byte_counter-1] = SMB0DAT; // Store received byte
STO = 1;
SMB_BUSY = 0;
AA = 1; // Set AA for next transfer
break;
// Master Transmitter: Arbitration lost.
case SMB_MTARBLOST: //0x38
FAIL = 1; // Indicate failed transfer
// and handle at end of ISR
break;
// All other status codes invalid. Reset communication.
default:
FAIL = 1;
break;
}
if (FAIL) // If the transfer failed,
{
SMB0CN &= ~0x40; // Reset communication
SMB0CN |= 0x40;
STA = 0;
STO = 0;
AA = 0;
SMB_BUSY = 0; // Free SMBus
FAIL = 0;
}
SI = 0; // Clear interrupt flag
}
//-----------------------------------------------------------------------------
// Support Functions
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// SMB_Write
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Writes a single byte to the slave with address specified by the <TARGET>
// variable.
// Calling sequence:
// 1) Write target slave address to the <TARGET> variable
// 2) Write outgoing data to the <SMB_DATA_OUT> array
// 3) Call SMB_Write()
//
void SMB_Write (void)
{
char SFRPAGE_SAVE = SFRPAGE; // Save Current SFR page
SFRPAGE = SMB0_PAGE;
while (SMB_BUSY); // Wait for SMBus to be free.
SMB_BUSY = 1; // Claim SMBus (set to busy)
SMB_RW = 0; // Mark this transfer as a WRITE
STA = 1; // Start transfer
SFRPAGE = SFRPAGE_SAVE; // Restore SFR page detector
}
//-----------------------------------------------------------------------------
// SMB_Read
//-----------------------------------------------------------------------------
//
// Return Value : None
// Parameters : None
//
// Reads a single byte from the slave with address specified by the <TARGET>
// variable.
// Calling sequence:
// 1) Write target slave address to the <TARGET> variable
// 2) Call SMB_Write()
// 3) Read input data from <SMB_DATA_IN> array
//
void SMB_Read (void)
{
char SFRPAGE_SAVE = SFRPAGE; // Save Current SFR page
SFRPAGE = SMB0_PAGE;
while (SMB_BUSY); // Wait for bus to be free.
SMB_BUSY = 1; // Claim SMBus (set to busy)
SMB_RW = 1; // Mark this transfer as a READ
STA = 1; // Start transfer
while (SMB_BUSY); // Wait for transfer to complete
SFRPAGE = SFRPAGE_SAVE; // Restore SFR page detector
}
The main continuously does the following: Sends 3 bytes. The first byte is the device register pointer. Then reads the same register (since the pointer is already set). It does do this.
while (1)
{
TARGET = SLAVE_ADDR; // Target the Slave for next SMBus
// transfer
SMB_DATA_OUT[0] = 0x01; // Device register
SMB_DATA_OUT[1] = 0x0A; // Register MSByte
SMB_DATA_OUT[2] = 0x03; // Register LSbyte
SMB_Write(); // Initiate SMBus write
// SMBus Read Sequence
TARGET = SLAVE_ADDR; // Target the Slave for next SMBus
// transfer
SMB_Read();
}
And here is a trace capture of transfer:
Looks to me like the master receive is sending an extra ACK. So my main focus has been on cases:
SMB_MRADDACK: //0x40
SMB_MRADDNACK: //0x48
SMB_MRDBACK: //0x50
My main focus is more so SMB_MRADDNACK: //0x48 and the number of times it goes through that if statement during the ISR calls. I'm having a little trouble wrapping my head around the exact failure point. So where is this extra ACK coming from? I'll look back here Monday afternoon if I don't figure it out myself by then.
Bonus question: Is there a embedded stack exchange of some sort? Didn't see anything that stood out for me in the communities..
Your trace shows (excluding addressing) three bytes sent and three bytes read. I'm assuming you desired to write three bytes and then only read two bytes. If that's true, then the problem is more than just a spurious ACK because your master continues clocking the third byte in as well.
If you desire to read only two bytes with the sample code from SiLabs1, you need to define NUM_BYTES_RD to 2 instead of the provided 3. That value is used in the SMB_MRADDACK and SMB_MRDBACK states to decide whether to ACK or STOp.
Just in case (since you ask about ACKs instead of extra bytes), if your question is about the final drop in the SDL line on your trace (after the 0xff) because you're afraid that's an extra ACK, then worry not. That is a STO (rises during high SCL) and is correct behavior for a Master terminating a transmission.
Edit: klamb is correct in the comments below below, there is bug in the SMB_MRDBACK state. Saving SMB0DAT and incrementing rec_byte_counter should happen before checking rec_byte_counter against NUM_BYTES_RD. Suprising that got out of SiLabs like that.
case SMB_MRDBACK: //0x50
SMB_DATA_IN[rec_byte_counter-1] = SMB0DAT; // Store received byte
rec_byte_counter++; // Increment the byte counter
if (rec_byte_counter < NUM_BYTES_RD)
{
AA = 1; // Send ACK to indicate byte received
}
else
{
AA = 0; // Send NACK to indicate last byte
// of this transfer
}
break;

STM32F4 I2C Slave Receiver

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.