Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Code Review: STM32407 SPI using RTFM and BBQueue #15

Open
justacec opened this issue May 18, 2020 · 1 comment
Open

Code Review: STM32407 SPI using RTFM and BBQueue #15

justacec opened this issue May 18, 2020 · 1 comment

Comments

@justacec
Copy link

justacec commented May 18, 2020

There are two programs here, the first is the firmware for the STM32F407 and the second is a small CLI app which is intended to be executed on a RPI which is communicating with the MCU via an SPI device (/dev/spidev1.1 in my case).

Firmware: https://github.com/justacec/stm32f407_play

CLI App: https://github.com/justacec/test_spi_cli

Core Concepts

  • Incoming messages can come in at anytime as long as the MCU is not trying to return data. This can be cross-checked by looking at the busy line (it should be low and then you are good to go to transmit a command)
  • Each command has a response to act as an ACK
  • There is no real error checking here
  • Commands are executed in FIFO style
  • Commands and responses are limited to 32 bytes of data

Protocol

The protocol is simple. This is a one-direction-at-a-time (C) approach. I thought of trying to do full duplex but it the juice was not worth the squeeze and it was really going to complicate things. (at least I think).

Packets are received by the MCU with the following structure:

Offset Size Description
0 2 Message ID
2 2 Opcode
4 N Message Data

The MCU knows when the packet is completed because it gets the close of the SS line on the SPI.

Each message gets a response. In this way, one can get some information that the MCU did get the message. The structure of the response is the following:

Offset Size Description
0 2 Message ID
2 2 Size in Bytes of follow-on data in this message
4 N Any data returned from the request

Concept of Operation

The firmware starts off by establishing two BBQueue's, one for incoming commands and one for outgoing responses. The SPI device and the DMA are configured and the spi_complete task is pended. The spi_complete task does two things, first it commits any existing write grant on the incoming command queue. It then establishes a new grant and configures the DMA with that memory address. By initially pending the spi_complete task, I keep all of the write grant code for the incoming command queue in a single function and reduce code duplication. A

After a command is delivered to the MCU, the spi_complete task executes because of the interrupt and commits the new command to the processing queue. The process_command task is then spawned which attempts to loop over all existing commands in the incoming command queue. It essentially performs the correct task, formats the return, writes the return to the outgoing response queue, and finally spawns the transmit_command_results task.

The transmit_command_results task changes the state of the SPI device to "SendingResponse", gets a read grant from the outgoing queue, and sets up the DMA to use it. Lastly, the task tells the master that there is data ready for pickup by raising a GPIO pin that the master can watch. This triggers the master to perform an spa read. The master does a mandatory 4-byte read on the bus and then decodes the follow-on message data size and the message id from the 4 bytes. If there is any more data it needs to pull, it will then do a follow-on spi read with the appropriate size.

The firmware knows all of the data has been read by the NCTR register of the TxDMA going to 0 and the DMA completed event is fired. This resets things and gets ready for the next round.

Potential Change to Firmware

A thought I had was to just get rid of the incoming command queue and have a simple buffer to receive commands. As each command was received, it would fire off the process_command task while directly passing it the raw command data. As soon as the command was processed, the result would go into the command response queue and the firmware would continue to operate as it currently does.

The benefit of this is a simpler application and the ability to complete tasks without blocking. For instance, if there were two commands which were executed, one long run-time and one short run-time. If the long one is sent first then the short one would have to wait for completion of the long runtime command. With this change, maybe the short runtime gets done quickly, while the long runtime task is running, and the result is made avail to the master in an out-of-sync fashion (command 2 response before command 1 response)?

Not sure, just food for thought.

The CLI Application

The CLI is most likely very very buggy and was just a copy to play with and give me an interface to generate requests. This is holding the place of a server which would be running on the RPI (prob a Rocket server?). The app just presents a simple screen with a table. When you hit the 's' command it would send a pre-formatted SPI command to the MCU and then grab the response when it is ready. Rows in red color do not have a response yet, rows in green have received a response from the MCU.

Targeted Questions

  1. Should I switch to the mode that I was talking about where I drop the rx queue?
  2. Should I be using other great things that you have put together such as Postcard?
  3. Am I crazy?
@justacec
Copy link
Author

BTW: This is very buggy code right now. Needs a lot of cross-checks and cleanup.

I just added the generation of random commands to the CLI and am basically able to kill the firmware when I try to send multiple packets.

I think this is because I am not explicitly checking for wait for command.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant