Setting nss_soft in Master (SPI) - arm

I want to set the NSS pin to software mode in master using Nucleo STM32F103RB.
In the reference manual, they say,
In NSS Software mode, set the SSM and SSI bits in the SPI_CR1 register. If the NSS pin is required in output mode, the SSOE bit only should be set.
Why do we need to set the SSI bit with SSM?
What is the purpose of the SSOE bit?

It's related to the rarely used multi-master communication.
In a multi-master setup, the NSS signal controls access to the SPI bus. The ST documentation is unfortunately a bit vague there, but my understanding is that
NSS high input means the bus is free, and you are allowed to transmit
NSS low input means someone else is transmitting, and you become a slave.
Why do we need to set SSI bit with SSM?
If the SSM (Software Slave Management) bit is set in master mode, then the SSI (Slave Select Internal) bit becomes the source of the NSS signal instead of the pin. Setting SSI to 1 allows the master to transmit, setting it to 0 makes it a slave (clears the MSTR bit in CR1).
If you have a single master, just set
SPI->CR1 = SPI_CR1_MSTR | SPI_CR1_SPE | SPI_CR1_SSM | SPI_CR1_SSI
and don't worry about the rest. It's the most flexible way, and you can control as many slaves as you like with GPIO outputs connected to the CS lines separately. You can use the NSS pin as GPIO as well.
What is the purpose of SSOE bit?
It changes the NSS pin to an output. Initially set to high, it becomes low when the controller starts transmitting (when the DR register is written to). Note that it won't automatically become high again when the transfer is finished, but by setting SPI_CR1_SPE to 0.
Using SSOE can be useful when a single master is talking to a single slave, because CS is controlled by the SPI registers. Not having to talk to a GPIO peripheral at all, there isn't any need to load its base address to a register and holding it there, saving some cycles and a couple of bytes in flash, making it possible to use a register for something else by an optimizing compiler.

Related

Implementing an SSI slave interface on STM32 Board

I am trying to implement a SSI Slave Protocol on a STM32 Board. Since the STM32 Boards don't have a SSI interface, I used its SPI interface in Slave(Transmit only mode). The master SSI sends 24 clock signals and the slave reacts by sending its data(3 Bytes) over the MISO pins. The problem I am facing is that the data is always shifted on the left on every clock signal coming from the master. For example assuming I am constantly sending 0x010101 from slave.
At first transmission the master receives 0x010101
At Second transmission the master receives 0x020202
At third transmission the master receives 0x040404
Can someone please give me some hints on how to solve this problem?
The data-shift with each transmission can happen when the SPI slave recognizes an (unexpected) additional clock pulse. Looking at the SSI protocol description on Wikipedia this actually makes sense:
In order to transmit N bits of data the master emits N clock cycles, followed by another clock pulse to signal the end of the transfer (so-called "Monoflop Time" - referring to the original hardware implementation of the SSI interface). Since the SPI protocol / SPI slave does not know about this additional clock pulse, it begins to output the first bit of the next data byte, which is in turn not recognized by the SSI master. As a result this leads to a shift in the data bits recognized by the SSI master on the next SSI frame.
Unfortunately, it is not easy to handle the Monoflop time correctly with the SPI slave. In order to deal with the additional clock pulse, we could try to set the SPI frame size to 25 bits on the slave side. Since the STM32 hardware only supports SPI frame sizes between 4 bit and 16 bit, the only choice is to set it to 5 bit. This is not very convenient, since we need to convert the 3 byte (24 bit) output data into 5 blocks of 5 bit (24 bit output data + 1 bit dummy data), but it should work for a "normal" transfer.
Things get more complicated though, if we also want to handle the cases "Multiple transmissions" and "Interrupting transmission" correctly. We need to monitor the clock signal to be able to detect the monoflop timeout. This can be done using a STM32 hardware timer with an external trigger. When the timer expires, we need to reset the SPI unit (in order to handle an interrupted transmission) and update the output value. This "simple" task can be quite challenging since it requires a couple of instructions - requiring a fast MCU depending on the SSI clock frequency.
Alternatively the SSI protocol can be implemented using a software-only "bit banging" solution. But this requires a fast MCU as well in order to handle a fast SSI clock correctly.
IMHO the best solution is to use a small (inexpensive) FPGA to implement the SSI slave and let the MCU feed it with data over a traditional SPI interface.

STM32 I2C set SDA to low

Is there any way to set the SDA and SCL pin of the I2C1 connection of the STM32 to low or high signal?
I use a security chip and I have to send a wake condition, with the following condition:
if SDA is held low for a period of greater than 60us, the device will exit low power mode and
after a delay of 1500us, it will be ready to receive I2C commands.
I've already tried to toggle the actual pin with HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_9);, but this isn't working.
I've configured my project with STM32CubeMX.
Thanks for your help.
In I2C, the START condition requires a High to Low transition, if you then send a dummy address 0, a NACK will be generated (or rather the lack of any response will be interpreted as a NACK). In a normal transaction, the software would respond to the NACK by generating a repeated START or a STOP condition, however this must be done in software, so all you have to do is nothing for 1.5ms. Thereafter you can generate the START with the device's actual address, and if the device is running it will generate an ACK.
I am not familiar with the HAL library driver, and frankly the documentation is abysmal, but it is possible that it does not give you the necessary control, and you will have to access the I2C peripheral at the register level for at least this procedure. You might try a zero-length I2C_MasterRequestWrite() call to address zero followed by a delay. An oscilloscope would be useful here to ensure the expected signal timing is being generated.
When you initialize I2C, GPIO pins mode is set to ALTERNATE MODE,so writing HAL commands won't work on it.
Using normal HAL libraries won't help you in this. You have to configure I2C protocol on your own using stm32 registers.
I recommend that the ownaddress of the slave address using the device of the using I2C channel sets like the below code.
I2C_InitStructure.I2C_OwnAddress1 = 0x30; // the unique slave address of the deviecs
because the master could be send the broadcast operation not the unique operation.

STM32F1 - Using master SPI on bare metal

I've been trying to port some of my AVR code to drive a simple SPI LCD to ARM as a learning exercise (I'm very new to ARM in general). For this I just need to use SPI in master mode.
I looked in the datasheet for my device (STM32F103C8) and found that the SPI1 pins I need, SCK and MOSI are mapped as alternative functions of PA5 and PA7, respectively, along with other peripherals (pg.29). My understanding is that in order to use the SPI function on these pins, I need to make sure that anything else mapped to the same pin is disabled. When looking at the defaults for the peripheral clock control register, however, it looks like the other features are already disabled.
I looked at the SPI section in the reference manual, including section 25.3.3 - Configuring the SPI in master mode. First I enabled the SPI1 master clock in APB2ENR and followed the steps in this section to configure SPI1 to my needs. I also changed the settings for PA5/7 to set their mode to "Alternate Function Output push-pull" (9.1.4). Finally, I enabled SPI1 by setting CR1_SPE.
From my reading, I had thought that by loading a value into the SPI1 data register after configuring SPI as above, the data would be shifted out. However, after writing the data, the TXE flag in the SPI status register never becomes set, indicating that the data I wrote into it is just sat there.
At this point, I'm assuming that there is something else I've failed to configure correctly. For example, I'm not 100% sure about what to do with the PA5/7 pins. I've tried to understand what I can from the datasheets, but I'm not getting anywhere. Is there anything else that needs to be done before it'll work?
I'm almost certain that you did not set SSM and SSI bits in SPIx->CR1 register. SPI in these chips is pretty simple, for the polled transfers you need to set SSM, SSI, SPE, MSTR, correct format (LSBFIRST, CPOL, CPHA) and proper baudrate (BR) in SPIx->CR1 and you're good to go.

How can I use hardware NSS (SPI) on STM32F4?

I tried to use the hardware NSS signal with the HAL library, but I can't find any function to make the NSS pin have a low or high level. I also tried to find the answer in the HAL documentation, but there isn't any information there either. All examples on the Internet contain only software NSS.
How is one supposed to use hardware NSS?
Somewhere I read that NSS is driven low as long as the SPI Master is enabled and driven high again if the SPI Master is disabled.
I tried it using the HAL library (Cube/STM32CubeMX) from ST with an STM32L476 and polling SPI1. Initialization before and de-initialization after transmission did not set the NSS pin, but it took a lot of time:
Init structure:
hspi1.Instance = SPI1;
hspi1.Init.Mode = SPI_MODE_MASTER;
hspi1.Init.Direction = SPI_DIRECTION_2LINES;
hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
hspi1.Init.CLKPolarity = SPI_POLARITY_HIGH;
hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
hspi1.Init.NSS = SPI_NSS_HARD_OUTPUT;
hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_8;
hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
hspi1.Init.CRCPolynomial = 7;
hspi1.Init.CRCLength = SPI_CRC_LENGTH_DATASIZE;
hspi1.Init.NSSPMode = SPI_NSS_PULSE_DISABLE;
Transmit sequence:
HAL_SPI_Init( &hspi1 );
HAL_SPI_TransmitReceive( &hspi1, btx, brx, l, 5 ); // timeout 5msec;
while( hspi1.State == HAL_SPI_STATE_BUSY ); // wait for xmission complete
HAL_SPI_DeInit( &hspi1 );
So I decided setting the pin manually using GPIO (using SPI_NSS_SOFT in init):
HAL_GPIO_WritePin( NSS1_GPIO_Port, NSS1_Pin, GPIO_PIN_RESET ); // NSS1 low
HAL_SPI_TransmitReceive( &hspi1, btx, brx, l, 5 ); // timeout 5msec;
while( hspi1.State == HAL_SPI_STATE_BUSY ); // wait xmission complete
HAL_GPIO_WritePin( NSS1_GPIO_Port, NSS1_Pin, GPIO_PIN_SET ); // NSS1 high
I used blocking transmission (no DMA or Interrupt), because it was fast enough and no other tasks waiting. DMA setup proved to take unacceptably longer time to send only 24 bytes at 20 MHz. It would be an acceptable alternative.
As far as I can see in the STM32L4xx manual Chapter 38.4.12/13, the automatic NSS goes high after each byte/word transmission and thus is not well usable for longer streams holding NSS low for the whole transmission.
You can use the NSS pin as a standard GPIO and drive it with an interrupt routine. You should do that part by software. First set the NSS low and then send your frame (HAL_SPI_Transmit).
After the slave gets all the frame, use the HAL_SPI_RxCpltCallback function and set the NSS pin high in that interrupt.
Don’t forget to connect the GPIO pin to the NSS pin on the slave.
The NSS pin may need a pull-up resistor if HiZ (not documented).
Look at the reference manual for your STM32 chip. I don't know if they're all the same but according to the one for mine (STM32WB55xx) and one I was able to find a public web link to (STM32F0)...
See the SPI functional description for NSS pin management (section 28.5.5 for the STM32F0 document I linked to), where it describes three modes:
Software NSS management (SPIx_CR1 register SSM bit = 1). The internal slave select information is driven internally by the SSI bit in register SPIx_CR1. The external NSS pin is free for applications to use.
Hardware NSS management (SSM bit = 0). This has two possible configurations depending on the SSOE bit in register SPIx_CR1:
NSS output enable (SSOE = 1). Only used when the MCU is master. The NSS signal is driven low as soon as SPI is enabled in master mode (SPE=1) and is kept low until SPI is disabled (SPE=0). A pulse can be generated between continuous communications if NSS pulse mode is activated. The SPI can not work in multimaster configuration with this NSS setting
NSS output disable (SSOE = 0). If the MCU is master, this allows multimaster capability. If NSS is pulled low, SPI enters master mode fault state and the device is automatically reconfigured in slave mode. In slave mode, the NSS pin works as a standard "chip select" input and the slave is selected while the NSS line is at low level.
Looking at the headers for my SoC, soft mode is SSM=1 (software NSS management). Hard output mode is SSM=0 and SSOE=1 (NSS output enable). Hard input mode is all zeros (NSS output disable).
If you're using hard output mode, you may also want to look at NSS pulse mode (section 28.5.12 in the STM32F0 document I linked to). It describes (with a timing diagram) how the system will leave NSS low most of the time, pulsing it high between data frames. If your device uses NSS/CS to synchronize the data frames, then this may be useful. Or maybe not, if your device reacts to NSS going high by aborting the current operation, since the text seems to indicate that it will pulse NSS between every word you transfer, not between buffers.
Unfortunately, this doesn't look like the most flexible implementation. Depending on your application, you may find it easier to leave it in soft mode and just toggle the NSS pin via GPIOs.

Implementing a non-standard SPI variation on ARM Cortex M3

I need to create a driver for a flash memory chip connected to a STM32 Cortex M3 MCU. The chip is controlled via an SPI bus. I intended to use integrated SPI peripheral of the MCU, but unfortunately it only supports 8- or 16-bit data packets while the flash chip commands are 14 bit long. Thus, I have to implement the protocol from scratch using GPIOs. My question is: what is the right way to ensure correct timings of the signals? I currently think of inserting delays between asserting and deasserting GPIO lines with interrupts disabled, but it seems fairly unreliable to me. Are there any better methods?
Jeb's answer is the preferred method and you should use the hardware SPI if possible, and if DMA is an option that is nice as well.
If you for some reason find out that you cannot use the hardware SPI, but that you must implement it using "bit-banging" over GPIO, you should check what options there are available in the timer/PWM hardware on the MCU. You cannot and should not use blunt "hobbyist burn-away delays" as in the link you posted, the real-time performance will be crap and you will occupy the CPU 100%.
Most MCU timers come with a pin output feature, that would allow a pin to change state when the timer elapses. The pseudo code would then be:
Determine if the next bit to send is 1 or 0.
Set the MCU polarity register accordingly, so that it will switch the pin to a high or low level.
When the timer elapses, you need to set the polarity once again, likely through an interrupt. How to do this is very hardware-dependent.
At the same time as you bit-bang the data (MOSI), you also need to generate the clock and chip select. The clock can be generated in the same way as the data, or possibly through a PWM signal if that option is available. Chip select is the easiest part as you only need to pull a pin low during the data transmission.
Finally, there is most likely some application note or official example over how to write a software SPI for your particular MCU.
I would recommend to use the build in SPI and DMA if possible!
You could remapping your data into an array of bytes with a size of a multiple of 14bits.
So you have to send a multiple of 7*4Bits=28bytes each time.
Then you can use the standard SPI with 8Bit-size.
But this should be much faster with SPI/DMA than bit banging the GPIO's.
Some devices that use obscure data lengths are designed so that at the start of a transaction they will either ignore all "0" bits that are clocked in before the first "1", or all "1" bits that are clocked in before the first "0". If your device happens to be designed in such a fashion, you may be able to use 8- or 16-bit SPI mode by clocking out two "junk" bits along with the bits of interest.

Resources