stm32f103c8t6 использование 2-х SPI

vadim_kkkk
Offline
Зарегистрирован: 21.07.2019

Здравствуйте.

Суть проблемы подключил к плате два SPI устройства

SPI 1 модуль nrf24L01

SPI 2 TFT млдуль на ILI9341

суть проблемы не работает SPI 1

дисплей работает нормально, nrf24L01 не принимает данные(работает только на приём)

вот код

#include <SPI.h>
#include <SoftWire.h>
#include "Wire.h"
#include "Adafruit_GFX_AS.h"
#include "Adafruit_ILI9341_STM.h"
#include "uRTCLib.h"

#include "nRF24L01_STM32.h"
#include "RF24_STM32.h"
RF24 radio(PA4,PA1);// CE, CSN

const uint64_t pipe = 0xF0F1F2F3F4LL;

SPIClass SPI_2(2); 

SoftWire SWire(PB6, PB7, SOFT_FAST);//SCL, SDA, 
uRTCLib rtc(0x68, 0x57);


#define TFT_CS        PB12
#define TFT_DC        PB11
#define TFT_RST       PB10

Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST); // Use hardware SPI
void setup() {
 // SPI.setModule(1);
  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  //SPI.setClockDivider(SPI_CLOCK_DIV16);
  radio.begin();
  delay(10);
  radio.setChannel(9);
  //radio.setRetries(15,15);
  radio.setDataRate(RF24_1MBPS); 
  radio.setPALevel(RF24_PA_HIGH);
  radio.openReadingPipe(1,pipe); // открываем первую трубу с индитификатором "pipe"
  radio.startListening(); // включаем приемник, начинаем слушать трубу

  Wire.begin();
  
  tft.begin(SPI_2, 16000000);  // set direct frequency value in MHz
  tft.fillScreen(ILI9341_BLACK);

}


void loop(void) {
 // for(uint8_t rotation=0; rotation<4; rotation++) {
   tft.setRotation(1);
   
  //  testText();
    delay(1000);
    
  tft.setTextColor(ILI9341_YELLOW,ILI9341_BLACK);  tft.setTextSize(4);
  rtc.refresh();
  tft.setCursor(0, 0);
  tft.print("           ");
  tft.setCursor(0, 0);
  tft.print(rtc.hour());
  tft.print(':');
  tft.print(rtc.minute());
  tft.print(':');
  tft.println(rtc.second());

  tft.print("Temp: ");
  tft.println(rtc.temp());

  int data; 
  
  if (radio.available()){ // проверяем не пришло ли чего в буфер.
    radio.read(&data, sizeof(data)); // читаем данные, указываем сколько байт читать
    
  tft.print("Nrf: ");
  tft.println(data);
  }

}

если в эту же плату записываю другой код только на передачу с NRF24L01 ,но без других устройств (часы,дисплей) то передача работает. Вот код

#include <SPI.h>
#include "nRF24L01_STM32.h"
#include "RF24_STM32.h"

// Set up nRF24L01 radio on SPI-1 bus (MOSI-PA7, MISO-PA6, SCLK-PA5) ... IRQ not used?
RF24 radio(PA4,PA1);// CE, CSN

const uint64_t pipe = 0xF0F1F2F3F4LL;  // Radio pipe addresses for the 2 nodes to communicate.
int data = 1;
void setup(){
   
  Serial1.begin(9600);
  delay(1000);
  Serial.println("\n\rRF24 Sensor Receiver");
  
  SPI.begin();
  SPI.setDataMode(SPI_MODE0);
  SPI.setBitOrder(MSBFIRST);

  // Setup and configure rf radio
  
  radio.begin();
  
  // optionally, increase the delay between retries & # of retries
  //radio.setRetries(15,15);
  radio.setChannel(9);
  radio.setDataRate(RF24_1MBPS); 
  radio.setPALevel(RF24_PA_HIGH);


  // Open pipes to other nodes for communication

  // This simple sketch opens two pipes for these two nodes to communicate
  // back and forth.
  // Open 'our' pipe for writing
  // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading)

  radio.openWritingPipe(pipe); // открываем трубу на передачу.
}

void loop(){
  
delay(1000);
  data++; // читаем значение
  
  radio.write(&data, sizeof(data)); // отправляем данные и указываем сколько байт пакет
  
  Serial1.print("data: ");
  Serial1.println(data);
}

Может кто сталкивался с подобной проблемой...

nik182
Offline
Зарегистрирован: 04.05.2015

А прием без других устройств? Зачем передачу показывать, если прием не работает?

vadim_kkkk
Offline
Зарегистрирован: 21.07.2019

Придётся пробовать, но тогда дисплей придётся другой цеплять......

nik182
Offline
Зарегистрирован: 04.05.2015

Зачем дисплей? Выводи в сериал по шнурку, которым шьёш.

vadim_kkkk
Offline
Зарегистрирован: 21.07.2019

попробовал простой скрипт работает

#include <SPI.h>


#include "nRF24L01_STM32.h"
#include "RF24_STM32.h"
RF24 radio(PA4,PA1);// CE, CSN

const uint64_t pipe = 0xF0F1F2F3F4LL;

//SPIClass SPI_2(2); 

int data; 

void setup() {
  Serial1.begin(9600);
  Serial1.print("Yesy nfr24l01");
  pinMode(PC13, OUTPUT);

  SPI.begin();
  SPI.setBitOrder(MSBFIRST);
  SPI.setDataMode(SPI_MODE0);
  //SPI.setClockDivider(SPI_CLOCK_DIV16);
  radio.begin();
  delay(10);
  radio.setChannel(9);
 // radio.setRetries(15,15);
  radio.setDataRate(RF24_1MBPS); 
  radio.setPALevel(RF24_PA_HIGH);
  radio.openReadingPipe(1,pipe); // открываем первую трубу с индитификатором "pipe"
  radio.startListening(); // включаем приемник, начинаем слушать трубу

  
}


void loop(void) {
 
 
  
  
  if (radio.available()){ // проверяем не пришло ли чего в буфер.
    radio.read(&data, sizeof(data)); // читаем данные, указываем сколько байт читать
    digitalWrite(PC13, LOW);
     delay(100);
   digitalWrite(PC13, HIGH);
  Serial1.println("Yesy nfr24l01");
  Serial1.print(data);
  }

}

а основной с TFT бесконечно быстро выдаёт пустые данные, тоесть вроде бы данные приходят но пусто и быстро данные должны приходить раз в секунду

#include <SPI.h>
#include <SoftWire.h>
#include "Wire.h"
#include "Adafruit_GFX_AS.h"
#include "Adafruit_ILI9341_STM.h"
#include "uRTCLib.h"

#include "nRF24L01_STM32.h"
#include "RF24_STM32.h"
RF24 radio(PA4,PA1);// CE, CSN
#define SPI1_NSS_PIN PA4

const uint64_t pipe = 0xF0F1F2F3F4LL;
int data;
SPIClass SPI_2(2);
//SPIClass SPI(1);

SoftWire SWire(PB6, PB7, SOFT_FAST);//SCL, SDA,
uRTCLib rtc(0x68, 0x57);

#define TFT_CS        PB12
#define TFT_DC        PB11
#define TFT_RST       PB10

Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST); // Use hardware SPI
void setup() {
 
 // SPI.setModule(1);
 Serial1.begin(9600);
  SPI.begin();
  SPI.setBitOrder(MSBFIRST); // Set the SPI_1 bit order
  SPI.setDataMode(SPI_MODE0); //Set the  SPI_2 data mode 0
  SPI.setClockDivider(SPI_CLOCK_DIV16);      // Slow speed (72 / 16 = 4.5 MHz SPI_1 speed)
  pinMode(SPI1_NSS_PIN, OUTPUT);

  radio.begin();
  delay(10);
  radio.setChannel(9);
  //radio.setRetries(15,15);
  radio.setDataRate(RF24_1MBPS);
  radio.setPALevel(RF24_PA_HIGH);
  radio.openReadingPipe(1,pipe); // открываем первую трубу с индитификатором "pipe"
  radio.startListening(); // включаем приемник, начинаем слушать трубу

  Wire.begin();
 
  tft.begin(SPI_2, 16000000);  // set direct frequency value in MHz
  tft.fillScreen(ILI9341_BLACK);

}

void loop(void) {
 // for(uint8_t rotation=0; rotation<4; rotation++) {
   tft.setRotation(1);
   
  //  testText();
    //delay(1000);
    
  tft.setTextColor(ILI9341_YELLOW,ILI9341_BLACK);  tft.setTextSize(4);
  rtc.refresh();
  tft.setCursor(0, 0);
  tft.print(rtc.hour());
  tft.print(':');
  tft.print(rtc.minute());
  tft.print(':');
  tft.println(rtc.second());

  tft.print("Temp: ");
  tft.println(rtc.temp());

 
 
  if(radio.available()){ // проверяем не пришло ли чего в буфер.
    radio.read(&data, sizeof(data)); // читаем данные, указываем сколько байт читать
    Serial1.print("Yesy nfr24l01-");
  Serial1.println(data);
  tft.print("Nrf: ");
  tft.println(data);
  }

}

 

nik182
Offline
Зарегистрирован: 04.05.2015

А в сериал что выходит из последней программы? Вроде одно и тоже должно быть на экране и в сериале. К тому же экран очень быстро перерисовывается. Зачем в лупе каждый раз инициализировать экран? Экран у Вас перерисовывается много раз в секунду. Повесте перерисовку экрана раз в секунду с помощью блинк без делэя.

vadim_kkkk
Offline
Зарегистрирован: 21.07.2019

В сериал постоянно очень быстро лелит Yesy nfr24l01-

то есть срабатывает команда

 

Serial1.print("Yesy nfr24l01-");

 

экспериментальным путём убрав всё что связано с SPI2 и построчно добавляя,

понял что проблемы вознмкаю после добавления

 

tft.begin(SPI_2, 16000); 

наверное какой то конфликт с SPI1 возникает

по поводу прорисовки дисплея пока не заморачивался потому что главное с SPI разобраться.

Спасибо за советы.

nik182
Offline
Зарегистрирован: 04.05.2015

Это не конфликт spi. Это конфликт библиотеки и назначения spi. Плохо написанная библиотека не понимает что ей инстанс поменяли. Нужно лезть в библиотеку и руками править назначение spi. При прямом програмировании всё бы работало без проблем. Как помочь не знаю. Смотрите в библиотеке место где идет инициализация. Должна быть команда привязки к конкретному spi. Для себя раз можно поменять. Может быть поможет это https://www.stm32duino.com/viewtopic.php?t=278

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Как вариант можно попробывать не создавать доп. класс, а менять каждый раз SPI, к которому идёт обращение, SPI.setModule();

vadim_kkkk
Offline
Зарегистрирован: 21.07.2019

нашёл вроде что то полезное , https://www.stm32duino.com/viewtopic.php?t=3813

но в англитйском не очень, попробовал библиотеку с изменениями дисплей не заработал

вообще,

зарегистрировался на

Arduino for STM32 но почемуто нет ссылок не на открытие тем не на посты, думаю там бы помогли грустно.

nik182
Offline
Зарегистрирован: 04.05.2015

В теме что Вы нашли всё написано. Надо модифицировать либу, хотя на это и ругаются, что не правильно либу править, но другого решения не предложили. Как вариант самому написать кусок вывода точки и подсунуть его либе.

vadim_kkkk
Offline
Зарегистрирован: 21.07.2019

Кому интересно проблему решил, заменил файлы библиотеки на файлы из этой темы https://www.stm32duino.com/viewtopic.php?t=3813

вот рабочая версия скрипта

#include <SPI.h>
#include <SoftWire.h>
#include "Wire.h"
#include "Adafruit_GFX_AS.h"
#include "Adafruit_ILI9341_STM.h"
#include "uRTCLib.h"

#include "nRF24L01_STM32.h"
#include "RF24_STM32.h"
RF24 radio(PA4,PA1);// CE, CSN
//#define SPI1_NSS_PIN PA4 

const uint64_t pipe = 0xF0F1F2F3F4LL;
int data; 
SPIClass SPI_2(2); 

SoftWire SWire(PB6, PB7, SOFT_FAST);//SCL, SDA, 
uRTCLib rtc(0x68, 0x57);


#define TFT_CS        PB12
#define TFT_DC        PB11
#define TFT_RST       PB10

Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST,SPI_2); // Use hardware SPI
void setup() {
  
  //SPI.setModule(2);
 Serial1.begin(9600);
  SPI.begin();
  SPI.setBitOrder(MSBFIRST); // Set the SPI_1 bit order
  SPI.setDataMode(SPI_MODE0); //Set the  SPI_2 data mode 0
  SPI.setClockDivider(SPI_CLOCK_DIV16);      // Slow speed (72 / 16 = 4.5 MHz SPI_1 speed)
   //pinMode(SPI1_NSS_PIN, OUTPUT);

SPI_2.begin(); //Initialize the SPI_2 port.
  SPI_2.setBitOrder(MSBFIRST); // Set the SPI_2 bit order
  SPI_2.setDataMode(SPI_MODE0); //Set the  SPI_2 data mode 0
  SPI_2.setClockDivider(SPI_CLOCK_DIV16);

  radio.begin();
  delay(10);
  radio.setChannel(9);
  //radio.setRetries(15,15);
  radio.setDataRate(RF24_1MBPS); 
  radio.setPALevel(RF24_PA_HIGH);
  radio.openReadingPipe(1,pipe); // открываем первую трубу с индитификатором "pipe"
  radio.startListening(); // включаем приемник, начинаем слушать трубу

  Wire.begin();
  
  tft.begin(16000000);  // set direct frequency value in MHz
  tft.fillScreen(ILI9341_BLACK);

}


void loop(void) {
 
   tft.setRotation(1);
   
  
  tft.setTextColor(ILI9341_YELLOW,ILI9341_BLACK);  tft.setTextSize(4);
  rtc.refresh();
  tft.setCursor(0, 0);
  tft.print(rtc.hour());
  tft.print(':');
  tft.print(rtc.minute());
  tft.print(':');
  tft.println(rtc.second());

  tft.print("Temp: ");
  tft.println(rtc.temp());

  
  
  if(radio.available()){ // проверяем не пришло ли чего в буфер.
    radio.read(&data, sizeof(data)); // читаем данные, указываем сколько байт читать
    Serial1.print("Yesy nfr24l01-");
  Serial1.println(data);
  tft.print("Nrf: ");
  tft.println(data);
  }

}

 

МАА
Offline
Зарегистрирован: 30.03.2021

Как всегда, dimax прав. Для STM32F103C8T6 во всяком случае. Порядок такой:

SPI.setModule(1);

... (обращение к устройству 1)

SPI.setModule(2);

... (обращение к устройству 2)

Для указанной платы SPI1 - это PA7/PA6/PA5/PA4, SPI2 - PB15/PB14/PB13/PB12 (соответственно MOSI, MISO, SCK, SS).

b707
Онлайн
Зарегистрирован: 26.05.2017

МАА пишет:

Как всегда, dimax прав. Для STM32F103C8T6 во всяком случае. Порядок такой:

SPI.setModule(1);

... (обращение к устройству 1)

SPI.setModule(2);

... (обращение к устройству 2)

этот вариант, как и писал Dimax - требуется только для старых библиотек, в которых жестко задан один SPI.  Сейчас уже в большинстве библиотек есть поддержка SPIClass и описанного выше извращения не требуется.

Делайте так, как описана у Вадима в сообщении #11

SPIClass SPI_1(1); 
SPIClass SPI_2(2); 

I
void setup() {
  
 
  SPI_1.begin();  //Initialize the SPI_1 port.
  SPI_1.setBitOrder(MSBFIRST); // Set the SPI_1 bit order
  SPI_1.setDataMode(SPI_MODE0); //Set the  SPI_2 data mode 0
  SPI_1.setClockDivider(SPI_CLOCK_DIV16);      // Slow speed (72 / 16 = 4.5 MHz SPI_1 speed)
  

SPI_2.begin(); //Initialize the SPI_2 port.
  SPI_2.setBitOrder(MSBFIRST); // Set the SPI_2 bit order
  SPI_2.setDataMode(SPI_MODE0); //Set the  SPI_2 data mode 0
  SPI_2.setClockDivider(SPI_CLOCK_DIV16);

}

 

МАА
Offline
Зарегистрирован: 30.03.2021

b707, спасибо за замечание. Но тогда при передаче данных и прочем общении необходимо будет писать что-то вроде SPI_2.transfer. А эта передача скрыта в библиотеке. Неудобно. Проще перед обращением к конкретному устройству (методами библиотеки) указать SPI.setModule(1) или SPI.setModule(2).

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

МАА пишет:

b707, спасибо за замечание. Но тогда при передаче данных и прочем общении необходимо будет писать что-то вроде SPI_2.transfer. А эта передача скрыта в библиотеке. Неудобно. Проще перед обращением к конкретному устройству (методами библиотеки) указать SPI.setModule(1) или SPI.setModule(2).

Это неправильно.

Один экземпляр класса не должен работать более чем с одним набором пинов. 

Кроме того, один экземпляр класса SPI может поочередно работать с несколькими устройствами.

Так что то, что кому-то "проще", не всегда оказывается правильным.

Тем более, что в своей аргументации Вы явно не правы: заводится два (или больше) экземпляра одного и того же класса, которые и можно назвать, скажем SPI_1 и SPI_2 (принципиально, что это именно экземпляры одного и того же класса), и каждый сконфигурировать на определенные пины и на работу с конкретным устройством.

Если библиотека "привыкла" к использованию имени экземпляра "SPI", то никто не мешает назвать один из экземпляров именно так.

В принципе, конечно, можно сделать о обертку над классом, в котором предусмотреть функции типа setModule, но это явно увеличивает ресурсоемкость, снижает универсальность, да и вообще можно рассматривать как костыли. Причем, не совсем удобные. Но некоторые склонны рассматривать привычку как удобство, так что кому-то такой подход может показаться и удобным. Только не нужно его пропагандировать, т.к. это явно шаг не в ту сторону.

b707
Онлайн
Зарегистрирован: 26.05.2017

МАА пишет:

b707, спасибо за замечание. Но тогда при передаче данных и прочем общении необходимо будет писать что-то вроде SPI_2.transfer. А эта передача скрыта в библиотеке. Неудобно.

в большинстве современных библиотек для СТМ32 в конструкторе предусмотрен параметр, принимающий ссылку на конкретный экземпляр SPI, поэтому ничего внутри библиотеки переписывать не придется.

Снова говорю -  смотрите код в сообщении #11 - там есть пример при обращении к дисплею, обратите внимание на последний парметр

Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST,SPI_2);

 

МАА
Offline
Зарегистрирован: 30.03.2021

b707 пишет:

в большинстве современных библиотек для СТМ32 в конструкторе предусмотрен параметр, принимающий ссылку на конкретный экземпляр SPI, поэтому ничего внутри библиотеки переписывать не придется.

Снова говорю -  смотрите код в сообщении #11 - там есть пример при обращении к дисплею, обратите внимание на последний парметр

Adafruit_ILI9341_STM tft = Adafruit_ILI9341_STM(TFT_CS, TFT_DC, TFT_RST,SPI_2);

Если предусмотрен, то, конечно, хорошо. Но не всегда:

Arduino_ST7789 tft = Arduino_ST7789(TFT_DC, TFT_RST);

МАА
Offline
Зарегистрирован: 30.03.2021

andriano пишет:

МАА пишет:

b707, спасибо за замечание. Но тогда при передаче данных и прочем общении необходимо будет писать что-то вроде SPI_2.transfer. А эта передача скрыта в библиотеке. Неудобно. Проще перед обращением к конкретному устройству (методами библиотеки) указать SPI.setModule(1) или SPI.setModule(2).

Это неправильно.

Один экземпляр класса не должен работать более чем с одним набором пинов. 

Кроме того, один экземпляр класса SPI может поочередно работать с несколькими устройствами.

Так что то, что кому-то "проще", не всегда оказывается правильным.

Тем более, что в своей аргументации Вы явно не правы: заводится два (или больше) экземпляра одного и того же класса, которые и можно назвать, скажем SPI_1 и SPI_2 (принципиально, что это именно экземпляры одного и того же класса), и каждый сконфигурировать на определенные пины и на работу с конкретным устройством.

Если библиотека "привыкла" к использованию имени экземпляра "SPI", то никто не мешает назвать один из экземпляров именно так.

В принципе, конечно, можно сделать о обертку над классом, в котором предусмотреть функции типа setModule, но это явно увеличивает ресурсоемкость, снижает универсальность, да и вообще можно рассматривать как костыли. Причем, не совсем удобные. Но некоторые склонны рассматривать привычку как удобство, так что кому-то такой подход может показаться и удобным. Только не нужно его пропагандировать, т.к. это явно шаг не в ту сторону.

"Привыкнуть" к использованию экземпляра с именем "SPI" может не только одна, но и другие библиотеки, одновременно используемые. Что тут поделаешь - их авторы так распорядились. Универсальные средства бывают, но редко. В каждом конкретном случае приходится чем-то поступаться: ресурсами, временем, надежностью. Пропаганда - это навязывание, да еще и в свою пользу. Мы же с вами ищем решения, удобные для любого.

b707
Онлайн
Зарегистрирован: 26.05.2017

МАА пишет:

Если предусмотрен, то, конечно, хорошо. Но не всегда:

Arduino_ST7789 tft = Arduino_ST7789(TFT_DC, TFT_RST);

эта строчка ничего не доказывает, у библиотеки может быть несколько конструкторов - один с указанием SPI. другой без. Надо видеть исходники библиотеки.

Но даже если там нет поддержки SPIClass - добавить ее в общем-то несложно, это можно сделать почти автозаменой SPI. на какое-нить my_SPI и определить my_SPI  в парметрах

если только в библиотеке есть поддержка ДМА - тогда чуть сложнее... но не намного.

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

МАА пишет:

"Привыкнуть" к использованию экземпляра с именем "SPI" может не только одна, но и другие библиотеки, одновременно используемые. Что тут поделаешь - их авторы так распорядились.

Я рискну напомнить, что SPI позволяет единственному устройству работать со многими внешними устройствами. Так что, если библиотеки "заточены" под единственный SPI, можно им и не мешать - пусть работают с единственным (у меня во многих проектах по SPI подключается несколько устройств, но использовать два канала SPI мне не приходилось еще ни разу).

Цитата:

Универсальные средства бывают, но редко. В каждом конкретном случае приходится чем-то поступаться: ресурсами, временем, надежностью.

Универсальность - это когда уже чем-то поступились. Обычно имеющимися ресурсами (универсальное решение, как правило, проигрывает по производительности  потребляет больше ресурсов).

Цитата:

Мы же с вами ищем решения, удобные для любого.

Для этого нужно договориться, что мы подразумеваем под удобством. В частности, включаем ли мы в понятие удобства привычки. Если включаем, то найти решение "удобное для любого", скорее всего, не получится.

МАА
Offline
Зарегистрирован: 30.03.2021

Совершенно верно: понятия должны быть закреплены. Удовлетворение привычкам, очевидно, в понятие "удобство" входит. Для конкретного индивида.

МАА
Offline
Зарегистрирован: 30.03.2021

Конструктор один единственный:

Arduino_ST7789(int8_t DC, int8_t RST, int8_t CS = -1);

Вы предлагаете корректировать библиотеку. Можно. Но можно, как мы видели, обойтись и без этого. В любом случае полезно знать имеющиеся варианты решения задачи.