#2 Апаратні питання » Подключение нескольких устройств паралельно по SPI » 2015-03-22 05:41:38

byzon
відповідей: 7

Доброго дня!
Собираю контроллер для автомойки самообслуживания. В принципе уже есть рабочий вариант, но решил добавить запись логов на SD карту.
В составе рабочего модуля:
arduino Leonardo.
7segment shield - 2шт - каждый на отдельной плате с процессором
RC522 - RFID считыватель для карт
Купюроприёмник - выдаёт имульсы.
Все шилды подключены по "железному" SPI паралельно т.к. на леонардо они не дублируются на выводы и вообще чтоб пины не забивать У каждого свой SS.
Вот работающий скетч:

/*
 * MFRC522 - Library to use ARDUINO RFID MODULE KIT 13.56 MHZ WITH TAGS SPI W AND R BY COOQROBOT.
 * The library file MFRC522.h has a wealth of useful info. Please read it.
 * The functions are documented in MFRC522.cpp.
 *
 * Based on code Dr.Leong   ( WWW.B2CQSHOP.COM )
 * Created by Miguel Balboa (circuitito.com), Jan, 2012.
 * Rewritten by Søren Thing Andersen (access.thing.dk), fall of 2013 (Translation to English, refactored, comments, anti collision, cascade levels.)
 * 
 * Released into the public domain.
 *
 * This sample shows how to setup a block on a MIFARE Classic PICC to be in "Value Block" mode.
 * In Value Block mode the operations Increment/Decrement/Restore and Transfer can be used.
 * 
 ----------------------------------------------------------------------------- empty_skull 
 
 - Aggiunti pin per arduino Mega
 - Scritto semplice codice per la scrittura e lettura 
 
 - add pin configuration for arduino mega
 - write simple read/write Code for new entry user
 
 http://mac86project.altervista.org/
 
 ----------------------------------------------------------------------------- Nicola Coppola
 * Pin layout should be as follows:
 * Signal     Pin              Pin               Pin
 *            Arduino Uno      Arduino Mega      MFRC522 board
 * ------------------------------------------------------------
 * Reset      9                5                 RST
 * SPI SS     10               53                SDA
 * SPI MOSI   11               51                MOSI
 * SPI MISO   12               50                MISO
 * SPI SCK    13               52                SCK
 *
 * The reader can be found on eBay for around 5 dollars. Search for "mf-rc522" on ebay.com. 
 */
// в этой версии деньги списывалсь сами при подключении к плате SD1
#include <SPI.h>
#include <MFRC522.h>

#define SS_PIN 6
#define RST_PIN 5



//ОБЪЯВЛЯЕМ ПЕРЕМЕННЫЕ
int ON = LOW;                    //Переопределяем значения цифровых входов
int OFF = HIGH;                  //Переопределяем значения цифровых входов
int Money;                       //переменная для подсчёта денег на карте
int FullSumm = 0;                //Сумма денег на мойке
int WaterCost = 125 ;            //"Цена" воды (миллисекунд на рубль)
int FoamCost = 25;             //"Цена" пены (миллисекунд на рубль)
byte LastCardUid[10];            //Идентификатор последней карты - массив из 10ти переменных типа byte
boolean CardsIdentical;          //Новая карта 
int tmpmoney = OFF;              //Временная переменная для счетчика импульсов
int impMoney = 0;                //Импульсы от купюроприемника
long oldtime = 0;                //Время, прошедшее с последнего импульса
int tmpPump = OFF;               //Временная переменная работы НВД
long oldMillis = 0;              //Временная переменная для списывания денег
long WorkingTime = 0;            //Время работы НВД
boolean SummMustShow = false;    //Флаг обновления экрана для показа суммы
boolean IsFoam = false;          //Флаг вода/пена
int tmp = 0;
int led4 = 9;
int led3 = 11;
//int led2 = 3;  // отключил для экранов
//int led1 = 2;  // отключил для экранов
int tmp2 = 0;

MFRC522 mfrc522(SS_PIN, RST_PIN);        // Create MFRC522 instance.

int Disp1 = 3; //экран



void setup() {
  
  
  //подтягиваем резисторы. теперь что бы подать сигнал на 11 и 13 нужно замкнуть их на землю
  digitalWrite(12, HIGH); 
  digitalWrite(13, HIGH); 
  digitalWrite(8, HIGH); 
  
  
  
        Serial.begin(9600);        // Initialize serial communications with the PC
        SPI.begin();                // Init SPI bus
        mfrc522.PCD_Init();        // Init MFRC522 card
        //Serial.println("Scan a MIFARE Classic PICC to demonstrate Value Blocks.");
        
        //Для купюроприемника    
        pinMode(7, OUTPUT);            //команда разрешения на прием купюр
        digitalWrite(7, HIGH);         //Подтягиваем резистор теперь отсутствие сигнала - HIGH, присутствие - LOW 
        pinMode(4, INPUT);            //Счетчик купюр
        digitalWrite(4, HIGH);        //Подтягиваем резистор теперь отсутствие сигнала - HIGH, присутствие - LOW
        
        //Датчики протока пены
        pinMode(8, INPUT);            //Датчик протекания воды
        //digitalWrite(2, HIGH);        //Подтягиваем резистор теперь отсутствие сигнала - HIGH, присутствие - LOW
        
        //Датчики работы насоса
        pinMode(2, INPUT);            //Датчик протекания воды
        //digitalWrite(2, HIGH);        //Подтягиваем резистор теперь отсутствие сигнала - HIGH, присутствие - LOW
        
//pinMode(led1, OUTPUT);
//pinMode(led2, OUTPUT);
pinMode(led3, OUTPUT); 
pinMode(led4, OUTPUT);        
 
 //экран--------------
  pinMode(Disp1, OUTPUT);    //Экран №1
  digitalWrite(Disp1, HIGH); //Отключаем пока
  SPI.setClockDivider(SPI_CLOCK_DIV64);
  ShowString("-00-", Disp1);
}

void loop() {
 if (FullSumm > 0) {
   digitalWrite(led3, HIGH); //если на мойке есть деньги, замыкаем цепь разрешение работы насоса
   }
  else digitalWrite(led3, LOW);
  
  tmp2 =  digitalRead(2); //
 Serial.print(tmp2);
   //if (digitalRead(10)==ON){
  //   Serial.print("pena");
 //  } 
   
 
  
  tmp = digitalRead(4); //считываем значение порта 4 к которому подключён pulse с купюроприёмника считаем деньгу
  
  if (tmp != tmpmoney){ //если значение на входе 4 менялось
    impMoney++;          //увеличиваем счётчик изменений на 1
    tmpmoney = tmp;      //отмечаем на какое значение менялось значение на входе 4
    oldtime = millis();  //запоминаем когда было последнее изменение
    
  }
  //Serial.print(impMoney);
    if ((millis()-oldtime) > 500 && (millis()-oldtime) < 530){    
    FullSumm = FullSumm+(impMoney*5); //?если импульсы с купюроприёмника не поступают 500 милисекунд то добавляем сосчитанные импульсы к сумме....
       
    impMoney = 0;  //обнуляем счётчик импульсов т.к. всё посчетали
   SummMustShow = true; //разрешаем показать сумму
   }
   
   
  //Показываем сумму
  if (SummMustShow == true){ //если показать сумму можно
  Serial.print(FullSumm);
    ShowValue(FullSumm, Disp1); //вывод на дисплей
   SummMustShow = false;       //запрет показа суммы
   
    //Serial.println("Full summ is: "+FullSumm); //?увеличивает значение на дисплее на FullSumm - сколько зачислили в купюроприёмник
  }
  
 //Смотрим переключатель вода/пена
  if (digitalRead(8) == OFF){ //данный переключатель почемуто работает наоборот а на 13-м не работает наверное это связано со втроеным резистором на 13-м
    IsFoam = false;
  }
  else IsFoam = true;   
  
 //Списываем деньги
  if ((digitalRead(12)== ON)&&(FullSumm > 0)){ //? если реле потока даёт сигнал и на мойке ещё есть деньги 
    if (oldMillis < millis()){                 // то
      WorkingTime++;
      oldMillis = millis();
    }

    if ((WorkingTime%WaterCost == 0) && (IsFoam==false)) { //если прошло ещё достаточно милисекунд для списания рублика за воду и пена выключена то списываем рублик за воду
      FullSumm--;
      SummMustShow = true;
      WorkingTime = 1;
    }

    if ((WorkingTime%FoamCost == 0) && (IsFoam==true)) { //если прошло ещё достаточно милисекунд для списания рублика за пену и пена влючена то списываем рублик за пену
      FullSumm--;
      SummMustShow = true;
      WorkingTime = 1;
    }
  } 


//Работа с картами___________________________________________________________________
  
        
        // Prepare key - all keys are set to FFFFFFFFFFFFh at chip delivery from the factory.
        MFRC522::MIFARE_Key key;
        for (byte i = 0; i < 6; i++) {
                key.keyByte[i] = 0xFF;
        }
        // Look for new cards
        if ( ! mfrc522.PICC_IsNewCardPresent()) {
                return;
        }

        // Select one of the cards
        if ( ! mfrc522.PICC_ReadCardSerial()) {
                return;
        }
        // Now a card is selected. The UID and SAK is in mfrc522.uid.
        
        // Dump UID
        Serial.print("Card UID:");
        for (byte i = 0; i < mfrc522.uid.size; i++) {
                Serial.print(mfrc522.uid.uidByte[i] < 0x10 ? " 0" : " ");
                Serial.print(mfrc522.uid.uidByte[i], HEX);
        } 
        Serial.println();

        // Dump PICC type
        byte piccType = mfrc522.PICC_GetType(mfrc522.uid.sak);
        Serial.print("PICC type: ");
        Serial.println(mfrc522.PICC_GetTypeName(piccType));
        if (        piccType != MFRC522::PICC_TYPE_MIFARE_MINI 
                &&        piccType != MFRC522::PICC_TYPE_MIFARE_1K
                &&        piccType != MFRC522::PICC_TYPE_MIFARE_4K) {
                //Serial.println("This sample only works with MIFARE Classic cards.");
                return;
        }
               
        // In this sample we use the second sector (ie block 4-7). the first sector is = 0
        // scegliere settore di lettura da 0 = primo settore 
        byte sector         = 1;
        // block sector 0-3(sector0) 4-7(sector1) 8-11(sector2)
        // blocchi di scrittura da 0-3(sector0) 4-7(sector1) 8-11(sector2)
        byte valueBlockA    = 4;
        byte valueBlockB    = 5;
        byte valueBlockC    = 6;
        byte trailerBlock   = 7;
        byte status;
        
        
        // Authenticate using key A.
        // avvio l'autentificazione A
        //Serial.println("Authenticating using key A...");
        status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
        if (status != MFRC522::STATUS_OK) {
                Serial.print("PCD_Authenticate() failed: ");
                Serial.println(mfrc522.GetStatusCodeName(status));
                return;
        }
        // Authenticate using key B.
        // avvio l'autentificazione B
        //Serial.println("Authenticating again using key B...");
        status = mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_B, trailerBlock, &key, &(mfrc522.uid));
        if (status != MFRC522::STATUS_OK) {
                Serial.print("PCD_Authenticate() failed: ");
                Serial.println(mfrc522.GetStatusCodeName(status));
                return;
        }
       
       /* 
        // Writing new value block A
        // Scrivo i valori per il settore A
        Serial.println("Writing new value block A(4) : the first of the sector TWO ");
                byte value1Block[] = { 1,2,3,4,  5,6,7,8, 9,10,255,12,  13,14,15,16,   valueBlockA,~valueBlockA,valueBlockA,~valueBlockA };
                status = mfrc522.MIFARE_Write(valueBlockA, value1Block, 16);
                if (status != MFRC522::STATUS_OK) {
                        Serial.print("MIFARE_Write() failed: ");
                        Serial.println(mfrc522.GetStatusCodeName(status));
                }
        /*
        
        // Writing new value block B
        // Scrivo i valori per il settore B
        Serial.println("Writing new value block B");
                byte value2Block[] = { 255,255,255,255,  0,0,0,0, 0,0,0,0,  255,255,255,255,   valueBlockB,~valueBlockB,valueBlockB,~valueBlockB };
                status = mfrc522.MIFARE_Write(valueBlockB, value2Block, 16);
                if (status != MFRC522::STATUS_OK) {
                        Serial.print("MIFARE_Write() failed: ");
                        Serial.println(mfrc522.GetStatusCodeName(status));
                }
        
        // Writing new value block D
        // Scrivo i valori per il settore C
        Serial.println("Writing new value block C");
                byte value3Block[] = { 255,255,255,255,  0,0,0,0, 0,0,0,0,  255,255,255,255,   valueBlockC,~valueBlockC,valueBlockC,~valueBlockC };
                status = mfrc522.MIFARE_Write(valueBlockC, value3Block, 16);
                if (status != MFRC522::STATUS_OK) {
                        Serial.print("MIFARE_Write() failed: ");
                        Serial.println(mfrc522.GetStatusCodeName(status));
                }
                
        */
        
        
        Serial.println("Read block A(4) : the first of the sector TWO");        
        byte buffer[18];
        byte size = sizeof(buffer);
        // change this: valueBlockA , for read anather block
        // cambiate valueBlockA per leggere un altro blocco
        status = mfrc522.MIFARE_Read(valueBlockA, buffer, &size);
      
       Money = buffer[0]*256+buffer[1]; //считывание денег из блока А и записть их в переменную Money
       
       
        Serial.print("Money on card: ");
        Serial.println(Money);             //вывод на порт суммы денег на карте
        
        Serial.println("Read cardholder name:");
    status = mfrc522.MIFARE_Read(valueBlockB, buffer, &size);
    for (byte i = 0; i < 16; i++){    
      Serial.print(char(buffer[i]));   //вывод на порт имени держателя карты поэлементно из массива buffer
      } 
      Serial.println();   // перенос cтрочки при выводе данных на порт
      
  /*   
    if (FullSumm == 0){
      //Сумма в мойке равна 0
      if (!CardsIdentical){
        //Карта уже отмечалась перед этим
        FullSumm = Money;//забрали деньги с карты на мойку можно написать FullSumm = FullSumm + Money; тогда деньги на мойке и на карте сложаться
        Money = 0;
      //  SummMustShow = true;
      }
    }
    else {
      //Сумма в мойке больше 0
      if (!CardsIdentical){
        //Карта уже отмечалась перед этим
       //так было FullSumm =  Money; //забрали деньги с карты на мойку можно написать FullSumm = FullSumm + Money; тогда деньги на мойке и на карте сложаться
       FullSumm = FullSumm + Money;
        Money = 0;    //деньги на карте обнулили
      }
      else{
        Money = FullSumm; //забрали деньги с мойки на карту
        FullSumm = 0;      //обнулли деньги на мойке
      }
      SummMustShow = true; //флаг обновления данных на дисплее
    }
  
   */
   Serial.print("FullSumm do:");
   Serial.print(FullSumm);
   Serial.println();
     Serial.print("Money do:");
   Serial.print(Money);
   Serial.println(); 
   
   if (FullSumm >= 0 && Money > 0 && SummMustShow != true ){ //флаг SummMustShow используется для того что бы выполнялось только одно из условий для перевода денег с карты на мойку или наоборот
      FullSumm = FullSumm + Money;
      Money = 0;
      SummMustShow = true;
      Serial.println("skinuli dengi s karti");
   }
     if (FullSumm > 0 && Money == 0 && SummMustShow != true ){ //флаг SummMustShow используется для того что бы выполнялось только одно из условий для перевода денег с карты на мойку или наоборот
      Money = FullSumm ; 
      FullSumm = 0;
      SummMustShow = true;
      Serial.println("skinuli dengi na kartu");
   }

  Serial.print("FullSumm posle:");
   Serial.print(FullSumm);
   Serial.println();
     Serial.print("Money posle:");
   Serial.print(Money);
   Serial.println();    
   
    //Записываем на карту сумму  либо добавляя то что осталось на карту, либо пишем ноль если забирали деньги с карты
    Serial.println("Writing ");
    byte value1Block[] = {byte(Money/256),byte(Money%256),0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0, valueBlockA,~valueBlockA,valueBlockA,~valueBlockA };
    status = mfrc522.MIFARE_Write(valueBlockA, value1Block, 16);
    if (status != MFRC522::STATUS_OK) {
      Serial.print("MIFARE_Write() failed: ");
      Serial.println(mfrc522.GetStatusCodeName(status));
    }
      
           Serial.println("pobitno blok A:");
    status = mfrc522.MIFARE_Read(valueBlockA, buffer, &size);
    for (byte i = 0; i < 16; i++){    
      Serial.print(byte(buffer[i]));   //вывод на порт побитно значений из блока А
      } 
      Serial.println();   // перенос cтрочки при выводе данных на порт  
     
        
        // risponde successo
        //Serial.println(mfrc522.GetStatusCodeName(status));
        
        // Dump the result
        //mfrc522.PICC_DumpMifareClassicSectorToSerial(&(mfrc522.uid), &key, sector);
        
        // Halt PICC
        mfrc522.PICC_HaltA();

        // Stop encryption on PCD
        mfrc522.PCD_StopCrypto1();
        
        
}


void ShowValue(int tempCycles, int Pin)
{
  char Str[4];
  boolean ShowZeros = false;
    
  if (tempCycles >= 10000){
    return;
  }
  
  if (tempCycles/1000 > 0){
    Str[0] = tempCycles/1000;
    ShowZeros = true;
  }
  else Str[0] = 32;
  tempCycles %= 1000;

  if ((tempCycles/100 > 0)||(ShowZeros)){
    Str[1] = tempCycles/100;
    ShowZeros = true;
  }
  else Str[1] = 32;
  tempCycles %= 100;
  
  if ((tempCycles/10 > 0)||(ShowZeros)){
    Str[2] = tempCycles/10;
    ShowZeros = true;
  }
  else Str[2] = 32;
  tempCycles %= 10;
  Str[3] = tempCycles;

  ShowString(Str, Pin);
}

void ShowString(char *toSend, int Pin){
  digitalWrite(Pin, LOW); //Включаем передачу для выбранного пина
  SPI.transfer('v');
  for(byte x = 0; x < 4; x++)
    SPI.transfer(toSend[x]); //Send a character from the array out over SPI
  digitalWrite(Pin, HIGH); //Выключаем передачу для выбранного пина
}

Теперь подключил Data logging shield v1.0 (на нём шина SPI заточена под UNO, я перепаял ноги 11,12,13 на железные выводы miso/mosi/sck)
Написал отдельный скетч на базе примера из библиотеки SD для сохранения в файл данных переменной Fullsumm и чтения этих данных из файла при перезагрузке контроллера.
вот скетч:

/*
  SD card read/write
 
 This example shows how to read and write data to and from an SD card file 	
 The circuit:
 * SD card attached to SPI bus as follows:
 ** UNO:  MOSI - pin 11, MISO - pin 12, CLK - pin 13, CS - pin 4 (CS pin can be changed)
  and pin #10 (SS) must be an output
 ** Mega:  MOSI - pin 51, MISO - pin 50, CLK - pin 52, CS - pin 4 (CS pin can be changed)
  and pin #52 (SS) must be an output
 ** Leonardo: Connect to hardware SPI via the ICSP header

 
 created   Nov 2010  by David A. Mellis
 modified 9 Apr 2012  by Tom Igoe
 
 This example code is in the public domain.
 	 
 */
 
#include <SPI.h>
#include <SD.h>

File myFile;

// change this to match your SD shield or module;
//     Arduino Ethernet shield: pin 4
//     Adafruit SD shields and modules: pin 10
//     Sparkfun SD shield: pin 8
const int chipSelect = 10;
//byte FullSumm = 3000;
int FullSumm = 30000;  
byte t1;
byte t2;
int  t3;
// byte start;
int start;
void setup()
{

  
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  Serial.print("Initializing SD card...");
  // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
  // Note that even if it's not used as the CS pin, the hardware SS pin 
  // (10 on most Arduino boards, 53 on the Mega) must be left as an output 
  // or the SD library functions will not work. 
   pinMode(SS, OUTPUT);
   
   
  if (!SD.begin(chipSelect)) {
    Serial.println("initialization failed!");
    return;
  }
  Serial.println("initialization done.");
  
  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
 
// мои код 
 myFile = SD.open("test.txt"); //открывает файл test.txt
 //Money = buffer[0]*256+buffer[1]; //считывание денег из блока А и записть их в переменную Money
// myFile.seek(0);
// t1 = myFile.read();
// Serial.println(t1);
// t2 = myFile.read();
// Serial.println(t2);
 //t3 = t1*256 + t2 ;
// start = t3; 
 start = (myFile.read())*256 + myFile.read() ; //читаем первый и второй байт в переменную start
 Serial.println(start); //выводим на серийный порт значение переменной старт
  myFile.close();
 //мой код
 
  myFile = SD.open("test.txt", FILE_WRITE);
  
  
  
  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test.txt...");
    myFile.seek(0);  //чтобы записывать с позиции 0
    myFile.write( byte (FullSumm/256));
    myFile.write( byte (FullSumm%256));
    
    //    byte(Money/256),byte(Money%256)
    
    
	// close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
  
  // re-open the file for reading:
  myFile = SD.open("test.txt");
  if (myFile) {
    Serial.write("test.txt:");
    
    // read from the file until there's nothing else in it:
    while (myFile.available()) {
    	Serial.write(myFile.read());
    }
    // close the file:
    myFile.close();
  } else {
  	// if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
}

void loop()
{
	// nothing happens after setup
}

По отдельности два скетча работают.
При обьединении компилятор ошибок не выдаёт, но вот устройства по SPI перестают работать. sad Экран выдаёт кракозябры, считыватель карт не работает, Serial выдаёт тишину.
Опытным путём, добавляя в основной скетч по отдельности разные строики из скетча чтение\запись что проблемы начинаются после добавления строки:
#include <SD.h>
Открыв данную библиотеку и почитав её описание обнаружил:
1.для леонардо есть особая версия библиотеки в которой в ручную виртуально привязываются выводы SPI к 11,12,13 пину. Я эту проблему решил перепайкой данных портов на железную шину SPI (которая сбоку из шести контактов)
2.в теле библиотеки есть строчка   return card.init(SPI_HALF_SPEED, csPin, mosi, miso, sck) &&  volume.init(card) && root.openRoot(volume);
предполагаю что модуль для SD карт работает на другой частоте шины SPI. но вот как разрешить проблему работы нескольких устройств по одной шине SPI ума не приожу... Они ведь все нужны.
Читал про продвинутые натройки шины SPI на DUO... но вот приобрести DUO по ка нет возможности...
Уважаемые Гуру, прошу подсказать:
1. Верны ли мои предположения?
2. Какие путы выхода из ситуации возможны?
Если нужна какая то дополнительная информация - готов предоставить. Спасибо.

Підвал форуму