How to connect a Flow to a Stream in SpinalHDL - hdl

I have a write sensitive APB register which shall push into a FIFO with clock domain crossing. I though I would write:
val myFlow = Flow(...)
busCtrl.driveFlow(myFlow,address=4)
val myFifo = StreamFifoCC(...)
myFifo.io.push << myFlow
But the << operator requires a Stream, so a type error happens.

myFifo.io.push << myFlow.toStream
Should be fine, if the fifo overflow, the myFlow transaction will be lost

Related

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.

How to detect termination character in SChannel-based HTTPS client

I've searched StackOverflow trying to find a similar problem, but haven't come across it, so I am posting this question.
I am trying to write an C++ HTTPS client using Microsoft's SChannel libraries, and I'm getting stochastic errors with chunked message transfer. This issue only seems to occur on very long downloads -- short ones generally work OK. Most of the time the code works properly -- even for long downloads -- but occasionally the recv() command gracefully timesout, disconnecting my TLS session, and other times, I get an incomplete last packet. The stochastic errors appear to be the result of the different size chunks and encryption blocks the server is using to pass the data. I know I need to handle this variation, but while this would be easy to solve on an unencrypted HTTP connection, the encryption aspect is causing me problems.
First, the timeout problem, which occurs about 5% of the time I request large HTTP requests (about 10 MB of data from a single HTTP GET request).
The timeout is resulting because on the last chunk I have specified a bigger receive buffer than the data remaining on a blocking socket. The obvious fix to this is to only request exactly the number of bytes I need for the next chunk, and that is what I did. But for some reason, the amount received from each request is less than what I request, yet appears to be missing no data after decryption. I'm guessing this must be due to some compression in the data stream, but I don't know. IN any event, if it is using compression, I have no idea how to translate the size of the decrypted uncompressed byte stream into the size of compressed encrypted byte stream including the encryption headers and trailers to request the exact right number of bytes. Can anyone help me do that?
The alternative approach is for me to just look for two CR+LFs in a row, which would also signal the end of the HTTPS response. But because the data is encrypted, I can't figure out how to look byte by byte. SChannel's DecryptMessage() seems to do its decryptions in blocks, not byte by byte. Can anyone in this forum provide any advice on how to do byte-by-byte decryption to enable me to look for the end of the chunked output?
The second problem is DecryptMessage sometimes erroneously thinks it is done decrypting before I reach the actual end of the message. The resultant behavior is I go on to the next HTTP request, and I get the rest of the previous response where I am expecting to see the header of the new request.
The obvious solution to this is to check the contents of the decrypted message to see if we actually reached the end, and if not, try to receive more data before sending the next HTTP request. But when I do this, and try to decrypt, I get a decryption error message.
Any advice/help anyone can provide on a strategies would be appreciated. I've attached the relevant code sections for the read/decrypt process of the HTTP body -- I'm not including the header read and parsing because that is working without any problems.
do
{
// Note this receives large files OK, but I can't tell when I hit the end of the buffer, and this
// hangs. Need to consider a non-blocking socket?
// numBytesReceived = recv(windowsSocket, (char*)inputBuffer, inputBufSize, 0);
m_ErrorLog << "Next read size expected " << nextReadSize << endl;
numBytesReceived = recv(windowsSocket, (char*)inputBuffer, nextReadSize, 0);
m_ErrorLog << "NumBytesReceived = " << numBytesReceived << endl;
if (m_BinaryBufLen + numBytesReceived > m_BinaryBufAllocatedSize)
::EnlargeBinaryBuffer(m_BinaryBuffer,m_BinaryBufAllocatedSize,m_BinaryBufLen,numBytesReceived+1);
memcpy(m_BinaryBuffer+m_BinaryBufLen,inputBuffer,numBytesReceived);
m_BinaryBufLen += numBytesReceived;
lenStartDecryptedChunk = decryptedBodyLen;
do
{
// Decrypt the received data.
Buffers[0].pvBuffer = m_BinaryBuffer;
Buffers[0].cbBuffer = m_BinaryBufLen;
Buffers[0].BufferType = SECBUFFER_DATA; // Initial Type of the buffer 1
Buffers[1].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 2
Buffers[2].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 3
Buffers[3].BufferType = SECBUFFER_EMPTY; // Initial Type of the buffer 4
Message.ulVersion = SECBUFFER_VERSION; // Version number
Message.cBuffers = 4; // Number of buffers - must contain four SecBuffer structures.
Message.pBuffers = Buffers; // Pointer to array of buffers
scRet = m_pSSPI->DecryptMessage(phContext, &Message, 0, NULL);
if (scRet == SEC_E_INCOMPLETE_MESSAGE)
break;
if( scRet == SEC_I_CONTEXT_EXPIRED )
{
m_ErrorLog << "Server shut down connection before I finished reading" << endl;
m_ErrorLog << "# of Bytes Requested = " << nextReadSize << endl;
m_ErrorLog << "# of Bytes received = " << numBytesReceived << endl;
m_ErrorLog << "Decrypted data to this point = " << endl;
m_ErrorLog << decryptedBody << endl;
m_ErrorLog << "BinaryData just decrypted: " << endl;
m_ErrorLog << Buffers[0].pvBuffer << endl;
break; // Server signalled end of session
}
if( scRet != SEC_E_OK &&
scRet != SEC_I_RENEGOTIATE &&
scRet != SEC_I_CONTEXT_EXPIRED )
{
DisplaySECError((DWORD)scRet,errmsg);
m_ErrorLog << "CSISPDoc::ReadDecrypt(): " << "Failed to decrypt message--Error=" << errmsg;
if (decryptedBody)
m_ErrorLog << decryptedBody << endl;
return scRet;
}
// Locate data and (optional) extra buffers.
pDataBuffer = NULL;
pExtraBuffer = NULL;
for(i = 1; i < 4; i++)
{
if( pDataBuffer == NULL && Buffers[i].BufferType == SECBUFFER_DATA )
pDataBuffer = &Buffers[i];
if( pExtraBuffer == NULL && Buffers[i].BufferType == SECBUFFER_EXTRA )
pExtraBuffer = &Buffers[i];
}
// Display the decrypted data.
if(pDataBuffer)
{
length = pDataBuffer->cbBuffer;
if( length ) // check if last two chars are CR LF
{
buff = (PBYTE)pDataBuffer->pvBuffer; // printf( "n-2= %d, n-1= %d \n", buff[length-2], buff[length-1] );
if (decryptedBodyLen+length+1 > decryptedBodyAllocatedSize)
::EnlargeBuffer(decryptedBody,decryptedBodyAllocatedSize,decryptedBodyLen,length+1);
memcpy_s(decryptedBody+decryptedBodyLen,decryptedBodyAllocatedSize-decryptedBodyLen,buff,length);
decryptedBodyLen += length;
m_ErrorLog << buff << endl;
}
}
// Move any "extra" data to the input buffer -- this has not yet been decrypted.
if(pExtraBuffer)
{
MoveMemory(m_BinaryBuffer, pExtraBuffer->pvBuffer, pExtraBuffer->cbBuffer);
m_BinaryBufLen = pExtraBuffer->cbBuffer; // printf("inputStrLen= %d \n", inputStrLen);
}
}
while (pExtraBuffer);
if (decryptedBody)
{
if (incompletePacket)
p1 = decryptedBody + lenStartFragmentedPacket;
else
p1 = decryptedBody + lenStartDecryptedChunk;
p2 = p1;
pEndDecryptedBody = decryptedBody+decryptedBodyLen;
if (lastDecryptRes != SEC_E_INCOMPLETE_MESSAGE)
chunkSizeBlock = true;
do
{
while (p2 < pEndDecryptedBody && (*p2 != '\r' || *(p2+1) != '\n'))
p2++;
// if we're here, we probably found the end of the current line. The pattern we are
// reading is chunk length, chunk, chunk length, chunk,...,chunk lenth (==0)
if (*p2 == '\r' && *(p2+1) == '\n') // new line character -- found chunk size
{
if (chunkSizeBlock) // reading the size of the chunk
{
pStartHexNum = SkipWhiteSpace(p1,p2);
pEndHexNum = SkipWhiteSpaceBackwards(p1,p2);
chunkSize = HexCharToInt(pStartHexNum,pEndHexNum);
p2 += 2; // skip past the newline character
chunkSizeBlock = false;
if (!chunkSize) // chunk size of 0 means we're done
{
bulkReadDone = true;
p2 += 2; // skip past the final CR+LF
}
nextReadSize = chunkSize+8; // chunk + CR/LF + next chunk size (4 hex digits) + CR/LF + encryption header/trailer
}
else // copy the actual chunk
{
if (p2-p1 != chunkSize)
{
m_ErrorLog << "Warning: Actual chunk size of " << p2 - p1 << " != stated chunk size = " << chunkSize << endl;
}
else
{
// copy over the actual chunk data //
if (m_HTTPBodyLen + chunkSize > m_HTTPBodyAllocatedSize)
::EnlargeBuffer(m_HTTPBody,m_HTTPBodyAllocatedSize,m_HTTPBodyLen,chunkSize+1);
memcpy_s(m_HTTPBody+m_HTTPBodyLen,m_HTTPBodyAllocatedSize,p1,chunkSize);
m_HTTPBodyLen += chunkSize;
m_HTTPBody[m_HTTPBodyLen] = 0; // null-terminate
p2 += 2; // skip over chunk and end of line characters
chunkSizeBlock = true;
chunkSize = 0;
incompletePacket = false;
lenStartFragmentedPacket = 0;
}
}
p1 = p2; // move to start of next chunk field
}
else // got to end of encrypted body with no CR+LF found --> fragmeneted chunk. So we need to read and decrypt at least one more chunk
{
incompletePacket = true;
lenStartFragmentedPacket = p1-decryptedBody;
}
}
while (p2 < pEndDecryptedBody);
lastDecryptRes = scRet;
}
}
while (scRet == SEC_E_INCOMPLETE_MESSAGE && !bulkReadDone);
TLS does not support byte-by-byte decryption.
TLS 1.2 breaks its input into blocks of up to 16 kiB, then encrypts them into ciphertext blocks that are slightly larger due to the need for encryption IVs/nonces and integrity protection tags/MACs. It is not possible to decrypt a block until the entire block is available. You can find the full details at https://www.rfc-editor.org/rfc/rfc5246#section-6.2.
Since you're already able to decrypt the first few blocks (containing the headers), you should be able to read the HTTP length so that you at least know the plaintext length that you're expecting, which you can then compare to the number of bytes that you've decrypted from the stream. That won't tell you how many bytes of ciphertext you need, though -- you can get an upper bound on the size of a fragment by calling m_pSPPI->QueryContextAttributes() and then should read either at least that number of bytes or until end of stream before trying to decrypt.
Have you tried looking at other examples? http://www.coastrd.com/c-schannel-smtp appears to contain a detailed example of an SChannel-based TLS client.
I was finally able to figure this out. I fixed this by decrypting each TCP/IP packet as it came in to check for the CR+LF+CR+LF in the decrypted packet instead of what I had been doing -- trying to consolidate all of the encrypted packets into one buffer prior to decrypting it.
On the "hang" problem, what I thought was happening was that recv() wasn't returning because the amount of data actually received was smaller than my expected receive size. But what actually happened was I had actually received the entire transmission, but I didn't realize it. Thus, I was making additional recv() calls when there was actually no more data to receive. The fact that there was no more data to receive was what caused the connection to time out (causing a "hang").
The truncation problem was occurring because I couldn't detect the CR+LF+CR+LF sequence in the encrypted stream, and I erroneously thought SChannel returned SEC_E_OK on DecryptMessage() only when the entire response was processed.
Both problems were eliminated once I was able to detect the true end of the message by decrypting in piecemeal fashion vs. in bulk.
In order to figure this out, I had to completely restructure the sample SChannel code from www.coastRD.com. While the www.coastRD.com code was very helpful in general, it was written for SMTP transfers, not chunked HTTP encoding. In addition, the way it was written, it was hard to follow the logic for processing variations in how messages were received and processed. Lastly, I spent a lot of time "hacking" Schannel to understand how it behaves and which codes are returned under which conditions, because unfortunately none of that is discussed in any of the Microsoft documentation (that I've seen).
The first thing I needed to understand was how SChannel tries to decrypt a message. In Schannel, the 1st 13 bytes of an encrypted message are the encryption header, and the last 16 bytes are the encryption trailer. I still don't know what the trailer does, but I did realize that the encryption header is never actually encrypted/decrypted. The 1st 5 bytes are just the TLS record header for "application data" (hex code 0x17), followed by two bytes defining the TLS version used, followed by 2 bytes of the TLS record fragment size, followed by leading 0s and one byte which I still haven't figured out.
The reason this matters is that DecryptMessage() only works if the record type is "application data". For any other record type (such as a TLS handshake "finished message), DecryptMessage() won't even try to decrypt it-- it will just return a SEC_E_DECRYPT_FAILURE code.
In addition, I needed to understand that DecryptMessage() often can't decrypt the entire contents of the receive buffer in one pass when using chunked transfer encoding. In order to successfully process the entire contents of the receive buffer and the remainder of the server HTTPS response, I needed to understand two key return codes from DecryptMessage() -- SEC_E_OK and SEC_E_INCOMPLETE_MESSAGE.
When I received SEC_E_OK, it meant DecryptMessage() was able to successfully decrypt at least part of the receive buffer. When this occurred, the 1st 13 bytes (the encryption header) remained unchanged. However, the bytes immediately following the header were decrypted in-place, followed by the encryption trailer (which is also unchanged). Often, there will be additional encrypted data still in the receive buffer after the end of the encryption trailer, which is also unchanged.
Since I was using the SecBufferDesc output buffer structures and 4 SecBuffer structures described in www.coastRD.com's code, I needed to understand that these are not actually 4 separate buffers -- they are just pointers to different locations within the receive buffer. The first buffer is a pointer to the encryption header. The second buffer is a pointer to the beginning of the decrypted data. The 3rd buffer is a pointer to the beginning of the encryption trailer. Lastly, the 4th buffer is a pointer to the "extra" encrypted data that DecryptMessage() was not able to process on the last call.
Once I figured that out, I realized that I needed to copy the decrypted data (the pointer in the second buffer) into a separate buffer, because the receive buffer would probably be overwritten later.
If there was no "extra" data in the 4th buffer, I was done done for the moment -- but this was the exception rather than the rule.
If there was extra data (the usual case), I needed to move that data forward to the very beginning of the receive buffer, and I needed to call DecryptMessage() again. This decrypted the next chunk, and I appended that data to the data I already copied to the separate buffer, and repeated this process until there was either no more data left in the receive buffer to decrypt, or I received a SEC_E_INCOMPLETE_MESSAGE.
If I received a SEC_E_INCOMPLETE_MESSAGE, the data remaining in the receive buffer was unchanged. It wasn't decrypted because it was an incomplete encryption block. Thus, I needed to call recv() again to get more encrypted data from the server to complete the encryption block.
Once that occurred, I appended newly received data to the receive buffer. I appended it to the contents of the receive buffer vs. overwriting it because the latter approach would have overwritten the beginning of the encryption block, producing a SEC_E_DECRYPT_FAILURE message the next time I called DecryptMessage().
Once I appended this new block of data to the receive buffer, I repeated the steps above to decrypt the contents of the receive buffer, and continued to repeat this whole process until I got a SEC_E_OK message on the last chunk of data left in the receive buffer.
But I wasn't necessarily done yet -- there may still be data being sent by the server. Stopping at this point is what caused the truncation issue I had occasionally encountered.
So I now checked the last 4 bytes of the decrypted data to look for CR+LF+CR+LF. If I found that sequence, I knew I had received and decrypted a complete HTTPS response.
But if I hadn't, I needed to call recv() again and repeat the process above until I saw the CR+LF+FR+LF sequence at the end of the data.
Once I implemented this process, I was able to definitively identify the end of the encrypted HTTPS response, which prevented me from making an unnecessary recv() call when no data was remaining, preventing a "hang", as well as prematurely truncating the response.
I apologize for the long answer, but given the lack of documentation on SChannel and its functions like DecryptMessage(), I thought this description of what I learned might be helpful to others who may have also been struggling to use SChannel to process TLS HTTP responses.
Thank you again to user3553031 for trying to help me with this over 7 months ago -- those attempts helped me narrow down the problem.

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

TensorFlow - how to serialize and send Status to remote

I am writing a distributed rendezvous implementation.
Requester - requests a tensor.
Responder - looks the tensor up and sends it to the requester.
If an error occurs on the responder side, I would like to send the error code to the requester which will propagate it to upper levels.
I saw that on GRPC the Status is converted to a ::grpc::status. Is that enough for Status serialization (i.e. can I simply mem-copy the ::grpc::status bytes inside my message body and send it, or is there another call required to flatten the data)? Thanks.
So, it seems clear that I can't mem-copy the ::grpc::status directly because it has the error_message and error_details fields of type ::grpc::string which is basically a typedef of std::string. So the data is not flat as far as I know, nor can I count on it.
I couldn't find a code example of how the serialization is done, so I did the following:
Create a Proto object with 3 fields: error_code, error_message, error_details.
When serializing, I would set those fields manually from the ::grpc::status and then call proto.SerializeToArray().
When deserializing, ParseProtoUnlimited(&proto) and then create a ::grpc::status from the proto fields.
Seems clean enough. Maybe just a little code duplication.
Proto:
message ErrorStatusProto {
int32 error_code = 1;
string error_message = 2;
string error_details = 3;
}
Serialize:
::grpc::Status gs = ToGrpcStatus(rm.status_);
ErrorStatusProto gsProto;
gsProto.set_error_code(gs.error_code());
gsProto.set_error_message(gs.error_message());
gsProto.set_error_details(gs.error_details());
gsProto.SerializeToArray(&message[kErrorStatusStartIndex],
gsProto.ByteSize());
if (gsProto.ByteSize() > kErrorStatusMaxSize) {
LOG(ERROR) << "Error status (" << gsProto.ByteSize() << " bytes) "
<< "is too big to fit in RDMA message ("
<< kErrorStatusMaxSize << " bytes). Truncated.";
}
Deserialize:
ErrorStatusProto gsProto;
CHECK(ParseProtoUnlimited(
&gsProto, &message[kErrorStatusStartIndex], kErrorStatusMaxSize))
<< "Failed to parse error status proto from message. Aborting.";
::grpc::Status gs((::grpc::StatusCode)gsProto.error_code(),
gsProto.error_message(), gsProto.error_details());
rm.status_ = FromGrpcStatus(gs);

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.