Hallo,
ich habe Probleme beim Programmieren eines STM SPI-Slaves.
Dazu habe ich mir einen Testaufbau aus zwei Nucleo-Boards aufgebaut.
Programmiert wird in STM32CubeIde, über dessen Oberfläche auch die
Hardware-Konfiguration vorgenommen wird.
Das erste Board ist der SPI-Master (Nucleo-F411RE):
Dieser Master sendet erst ein Datenbyte auf der MOSI-Leitung aus (8 CLK
Takte), danach gibt es eine kurze Pause, und anschließend erzeugt der
Master 8 weitere Takte, um ein Byte vom Slave auf der MOSI-Leitung
einzulesen.
Hier der zugehörige Code ausgeführt:
1 | //file: main.c (SPI Master)
|
2 | //... Setup functions generated by STM32CubeIde
|
3 | while (1)
|
4 | {
|
5 | //Send 1 byte
|
6 | HAL_SPI_Transmit(&hspi1, msg, 1, HAL_MAX_DELAY);
|
7 |
|
8 | //short break
|
9 | for(int j=0; j<300; j++)
|
10 | { asm volatile("NOP"); }
|
11 |
|
12 | //receive 1 byte
|
13 | HAL_SPI_Receive(&hspi1, ans, 1, HAL_MAX_DELAY);
|
14 |
|
15 | //wait and repeat
|
16 | HAL_Delay(300);
|
17 | }
|
(Die Daten, die vom Slave kommen werden auf dem Master nicht weiter
verarbeitet, das Master-Board ist hier nur "Taktgenerator" für die
CKL-Leitung. Der Master erzeugt die CLK-Takte und sendet Daten auf der
MOSI-Leitung, das konnte ich am Oszi überprüfen.)
Das zweite Board ist der SPI-Slave (Nucleo-F446RE). Dieses Board soll
warten, bis ein Command Byte vom Master empfangen wird und beim nächsten
Takt mit seinem eigenen Byte Antworten. (Eigentlich einfach, oder?)
Da der Slave noch andere (Mess-)Aufgaben ausführt und nicht blockieren
kann, bis der Master ein Byte anfragt, sendet muss die Kommunikation auf
dem Slave über Interrupts stattfinden. Dazu sollen die
HAL_SPI_Receive_IT() bzw. HAL_SPI_Transmit_IT() Funktionen verwendet
werden.
Hier ist der relevante Code der Slave-Main:
1 | //file: main.c (SPI Slave)
|
2 | //...
|
3 | SPI_HandleTypeDef hspi1;
|
4 | //...
|
5 | int main(void
|
6 | {
|
7 | //..
|
8 | MX_SPI1_Init();
|
9 | //...
|
10 | spi1_slave_init(&hspi1);
|
11 | while (1)
|
12 | {
|
13 | //leerer Loop, alles geschieht über Interrupts
|
14 | }
|
15 | //..
|
16 | }
|
17 |
|
18 | //..
|
19 |
|
20 | static void MX_SPI1_Init(void)
|
21 | {
|
22 | hspi1.Instance = SPI1;
|
23 | hspi1.Init.Mode = SPI_MODE_SLAVE;
|
24 | hspi1.Init.Direction = SPI_DIRECTION_2LINES;
|
25 | hspi1.Init.DataSize = SPI_DATASIZE_8BIT;
|
26 | hspi1.Init.CLKPolarity = SPI_POLARITY_LOW;
|
27 | hspi1.Init.CLKPhase = SPI_PHASE_2EDGE;
|
28 | hspi1.Init.NSS = SPI_NSS_SOFT;
|
29 | hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB;
|
30 | hspi1.Init.TIMode = SPI_TIMODE_DISABLE;
|
31 | hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE;
|
32 | hspi1.Init.CRCPolynomial = 10;
|
33 | if (HAL_SPI_Init(&hspi1) != HAL_OK)
|
34 | {
|
35 | Error_Handler();
|
36 | }
|
37 | }
|
38 |
|
39 | //...
|
die Funktion spi1_slave_init() ist in der Datei spi-com.c definiert,
genauso wie die entsprechenden Interrupt-Callbacks:
1 | //file: spi-com.c
|
2 | #include "spi-com.h"
|
3 |
|
4 | extern UART_HandleTypeDef huart2;
|
5 | extern SPI_HandleTypeDef hspi1;
|
6 |
|
7 | uint8_t spi_command[2] = {0x00, 0};
|
8 | uint8_t spi_tx_buf[2] = {0x3C, 0x55};
|
9 |
|
10 | void spi1_slave_init()
|
11 | {
|
12 | HAL_SPI_Receive_IT(&hspi1, spi_command, 1);
|
13 | }
|
14 |
|
15 | void HAL_SPI_TxCpltCallback (SPI_HandleTypeDef * hspi)
|
16 | {
|
17 | /* THIS IS NEVER EXECUTED!!! WHY?*/
|
18 |
|
19 | //rearm SPI Rx Interrupt
|
20 | HAL_SPI_Receive_IT(&hspi1, spi_command, 1);
|
21 | }
|
22 |
|
23 |
|
24 | void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef * hspi)
|
25 | {
|
26 | char buf[30];
|
27 | int uart_buf_len = 0;
|
28 |
|
29 | //print received SPI command
|
30 | uart_buf_len = sprintf(buf, "%02x\r\n", spi_command[0]);
|
31 | HAL_UART_Transmit(&huart2, (uint8_t *)buf, uart_buf_len, 1000);
|
32 |
|
33 | //nachdem ein byte empfangen, beim nächsten Takt ein byte senden
|
34 | HAL_SPI_Transmit_IT(&hspi1, spi_tx_buf, 1);
|
35 |
|
36 | }
|
PROBLEM:
Zwar wird der HAL_SPI_RxCpltCallback aufgerufen, wenn ein Byte vom
Master empfangen wurde. Obwohl im dort der Tx-Interrupt scharf
geschalten wird (mittels HAL_SPI_Transmit_IT()) wird der zugehörige
Tx-Callback nie aufgerufen. also:
Warum wird HAL_SPI_TxCpltCallback nicht aufgerufen?
Anmerkungen:
1. SPI Interrupts wurden in CubeMX aktiviert
2. Kommentare des automatisch erzeugten Codes habe ich hier entfernt,
sowie alle automatisch generierten Codeteile, die nichts mit SPI zu tun
haben.
3. Wenn in HAL_SPI_RxCpltCallback() nicht HAL_SPI_Transmit_IT() sondern
HAL_SPI_Receive_IT() aktiviert wird läuft das Programm ohne Probleme
weiter und reagiert auf jedes eingehende Byte. Das Problem tritt also
erst auf, wenn ich versuche die Tx-Funktionalität zu benutzen.
Viel Text, aber über Lösungsmöglichkeiten würde ich mich sehr freuen!
PS: Für dieses Projekt bin ich auf die HAL-Libraries beschränkt. Deshalb
bitte keine Vorschläge à la "HAL wegwerfen und alles selber in die
Register coden" ;)