Arduino:Хакинг/Добавление последовательных интерфейсов (SERCOM’ов) на SAMD-плату Arduino

Материал из Онлайн справочника
Перейти к навигацииПерейти к поиску

Перевод: Максим Кузьмин
Проверка/Оформление/Редактирование: Мякишев Е.А.



Добавление последовательных интерфейсов (SERCOM’ов) на SAMD-плату Arduino[1]

В этом руководстве мы объясним, как добавить больше последовательных интерфейсов (SERCOM’ов) на плату Arduino, оснащенную микропроцессором SAMD. Эти интерфейсы являются аппаратными и могут быть типов I2C, UART и SPI. Подключение дополнительных интерфейсов возможно, поскольку микроконтроллер SAMD имеет шесть встроенных модулей для последовательной коммуникации, которые можно настраивать отдельно друг от друга. На момент покупки платы настроены лишь четыре из этих модулей, поэтому оставшиеся два можно вывести на контакты платы. Это руководство как раз и объясняет, как это сделать.

Необходимое оборудование

Плата Arduino/Genuino Zero, MKRZero или MKR1000

Цепь

Для этого примера нужна только плата Arduino/Genuino.

Функции контактов

Одно из преимуществ платформы Arduino – в возможности назначить каждому контакту микроконтроллера одну из множества различных функций. Посмотреть назначения для каждого контакта можно в файле «variant.cpp». Давайте взглянем, к примеру, на такой файл к плате MKR1000.

В этой таблице за SERCOM отвечают следующие контакты:

/*
 +------------+------------------+---------+---------+----------+
 | Pin number |  MKR  Board pin  | Perip.C | Perip.D | Periph.G |
 |            |                  | SERCOMx | SERCOMx |    COM   |
 |            |                  | (x/PAD) | (x/PAD) |          |
 +------------+------------------+---------+---------+----------+
 | 00         | D0               |   3/00  |   5/00  |          |
 | 01         | D1               |   3/01  |   5/01  | USB/SOF  |
 | 02         | D2               |   0/02  |   2/02  | I2S/SCK0 |
 | 03         | D3               |   0/03  |   2/03  | I2S/FS0  |
 | 04         | D4               |         |   4/02  | I2S/MCK1 |
 | 05         | D5               |         |   4/03  | I2S/SCK1 |
 | 06         | D6               |   5/02  |   3/02  | I2S/SCK0 |
 | 07         | D7               |   5/03  |   3/03  | I2S/FS0  |
 +------------+------------------+---------+---------+----------+
 |            |       SPI        |         |         |          |
 | 08         | MOSI             |  *1/00  |   3/00  |          |
 | 09         | SCK              |  *1/01  |   3/01  |          |
 | 10         | MISO             |  *1/03  |   3/03  | I2S/SD0  |
 +------------+------------------+---------+---------+----------+
 |            |       Wire       |         |         |          |
 | 11         | SDA              |  *0/00  |   2/00  | I2S/SD1  |
 | 12         | SCL              |  *0/01  |   2/01  | I2S/MCK0 |
 +------------+------------------+---------+---------+----------+
 |            |      Serial1     |         |         |          |
 | 13         | RX               |         |  *5/03  |          |
 | 14         | TX               |         |  *5/02  |          |
 +------------+------------------+---------+---------+----------+
 | 16         | A1               |         |   5/00  |          |
 | 17         | A2               |         |   5/01  |          |
 | 18         | A3               |         |   0/00  |          |
 | 19         | A4               |         |   0/01  |          |
 | 20         | A5               |         |   0/02  |          |
 | 21         | A6               |         |   0/03  | I2S/SD0  |
 +------------+------------------+---------+---------+----------+
 |            | ATWINC1501B SPI  |         |         |          |
 | 26         | WINC MOSI        |  *2/00  |   4/00  |          |
 | 27         | WINC SCK         |  *2/01  |   4/01  |          |
 | 28         | WINC SSN         |   2/02  |   4/02  |          |
 | 29         | WINC MISO        |  *2/03  |   4/03  |          |
 +------------+------------------+---------+---------+----------+
 |            | ATWINC1501B PINS |         |         |          |
 | 32         | WINC WAKE        |         |   4/00  |          |
 | 33         | WINC IRQN        |         |   4/01  |          |
 +------------+------------------+---------+---------+----------+
 */

Как видите, SERCOM’ы можно вывести практически всюду. Кроме того, у вас одновременно может быть настроено более одного SERCOM’а и при этом задействовано больше одного контакта.

Как SERCOM’ы назначены по умолчанию

На гребешках платы MKR1000 можно найти интерфейсы SPI, I2C и UART, назначенные следующим образом:

  • SPI / SERCOM 1:
    • MOSI на контакте 8
    • SCK на контакте 9
    • MISO на контакте 10
  • I2C / SERCOM 0:
    • SDA на контакте 11
    • SCL на контакте 12
  • UART / SERCOM 5:
    • RX на контакте 13
    • TX на контакте 14

Также по умолчанию подключен еще один интерфейс SPI – он внутренне соединен с модулем WINC1500, и его контакты подключены следующим образом:

  • WINC1500 SPI / SEERCOM 2:
    • MOSI на контакте 26
    • SCK на контакте 27
    • MISO на контакте 29

Итак, убираем из таблицы уже назначенные интерфейсы, потому что наша задача – это добавление новых интерфейсов, а не изменение уже настроенных. В результате у нас остается следующее:

/*
 +------------+------------------+---------+---------+----------+
 | Pin number |  MKR  Board pin  | Perip.C | Perip.D | Periph.G |
 |            |                  | SERCOMx | SERCOMx |    COM   |
 |            |                  | (x/PAD) | (x/PAD) |          |
 +------------+------------------+---------+---------+----------+
 | 00         | D0               |   3/00  |   5/00  |          |
 | 01         | D1               |   3/01  |   5/01  | USB/SOF  |
 | 02         | D2               |   0/02  |   2/02  | I2S/SCK0 |
 | 03         | D3               |   0/03  |   2/03  | I2S/FS0  |
 | 04         | D4               |         |   4/02  | I2S/MCK1 |
 | 05         | D5               |         |   4/03  | I2S/SCK1 |
 | 06         | D6               |   5/02  |   3/02  | I2S/SCK0 |
 | 07         | D7               |   5/03  |   3/03  | I2S/FS0  |
 +------------+------------------+---------+---------+----------+
 | 16         | A1               |         |   5/00  |          |
 | 17         | A2               |         |   5/01  |          |
 | 18         | A3               |         |   0/00  |          |
 | 19         | A4               |         |   0/01  |          |
 | 20         | A5               |         |   0/02  |          |
 | 21         | A6               |         |   0/03  | I2S/SD0  |
 +------------+------------------+---------+---------+----------+
 */

Добавление нового интерфейса

Теперь давайте попробуем воспользоваться таблицей выше и добавить на плату MKR1000 новый интерфейс.

Добавление интерфейса через создание нового экземпляра класса Wire

Как видно из таблицы, контакты 0 и 1 могут управляться двумя SERCOM’ами – SERCOM3 и SERCOM5. Заглянув в даташит SAMD21, мы увидим, что SERCOM PAD0 можно использовать в качестве SDA, а SERCOM PAD1 – в качестве SCL. В скетче ниже показано, как это можно сделать:

/* 

Ведомое отправляющее устройство на контактах 0 и 1 (MKR1000) 
с помощью библиотеки Wire

Демонстрирует использование библиотеки Wire и создание 
дополнительного экземпляра класса Wire. 
Отправляет данные как ведомое устройство через интерфейс I2C/TWI.

Код написан на базе скетча «Ведущий считывает / Ведомый отправляет».

Создан 20 июня 2016 года.
Авторы:
Артуро Гуадалупи (Arturo Guadalupi; <a.guadalupi@arduino.cc>)
Сандип Мистри (Sandeep Mistry; s.mistry@arduino.cc)

*/

#include <Wire.h>
#include "wiring_private.h"

TwoWire myWire(&sercom3, 0, 1);   // создаем новый экземпляр класса Wire, назначая ему контакты 0 и 1

void setup()
{
  myWire.begin(2);                // подключаем шину I2C с адресом #2

  pinPeripheral(0, PIO_SERCOM);   // назначаем контакту 0 функцию SDA 
  pinPeripheral(1, PIO_SERCOM);   // назначаем контакту 1 функцию SCL 

  myWire.onRequest(requestEvent); // регистрируем событие
}

void loop()
{
  delay(100);
}

// Эта функция будет выполняться, когда ведущее устройство будет
// запрашивать данные. Она зарегистрирована как событие, 
// подробнее смотрите в блоке setup().

void requestEvent()
{
  myWire.write("hello "); // отвечаем сообщением из 6 байтов, которые ждет ведущее устройство
}

// подключаем к SERCOM’у обработчик прерываний:
extern "C" {
  void SERCOM3_Handler(void);

  void SERCOM3_Handler(void) {
    myWire.onService();
  }
}

Здесь в двух строчках используется внутренняя функция pinPeripheral(), у которой первый аргумент – это номер контакта, а второй – функция. С ее помощью осуществляется переназначение контактов:

pinPeripheral(0, PIO_SERCOM);   // назначаем контакту 0 функцию SDA 
pinPeripheral(1, PIO_SERCOM);   // назначаем контакту 1 функцию SCL

Эти строчки нужно поместить в блок setup(), чтобы переписать стандартное назначение контактов на плате и, образно говоря, вручить бразды правления цифровыми контактами в руки SERCOM.

Также в скетче используется функция обратного вызова:

void SERCOM3_Handler(void) {
  myWire.onService();
}

Она нужна для того, чтобы добавить в скетч возможность для коммуникации по I2C, поскольку библиотеке Wire требуются прерывания.

Добавление интерфейса через создание нового экземпляра класса Serial

/*

Считывание аналоговых данных через UART на контактах 0 и 1

Считывает входные аналоговые данные на контакте A0,
печатает результат на мониторе порта. 

Графическая репрезентация процесса доступна в меню, 
которое открывается при нажатии в IDE Arduino на 
Инструменты > Плоттер по последовательному соединению.

Подключите центральный контакт потенциометра к A0, 
а боковые контакты – к +3.3V и «земле».

При помощи перемычки соедините друг с другом контакты 0 и 1.

Создан 20 июня 2016
Автор:
Артуро Гуадалупи (Arturo Guadalupi a.guadalupi@arduino.cc)

Этот пример не защищен авторским правом.

*/

#include <Arduino.h>
#include "wiring_private.h"

Uart mySerial (&sercom3, 0, 1, SERCOM_RX_PAD_1, UART_TX_PAD_0); // создаем новый экземпляр класса Uart на контактах 0 и 1

// Блок setup() запускается, когда вы нажимаете на сброс:
void setup() {
  // Инициализируем последовательную коммуникацию 
  // на скорости 9600 бит/сек:
  Serial.begin(9600);
  mySerial.begin(9600);

  pinPeripheral(0, PIO_SERCOM); // назначаем контакту 0 функцию RX 
  pinPeripheral(1, PIO_SERCOM); // назначаем контакту 1 функцию TX 
}

// Блок loop() будет запускаться снова и снова:
void loop() {
  // Считываем входные данные на аналоговом контакте 0:
  int sensorValue = analogRead(A0);
  // Печатаем считанное значение на канал mySerial, 
  // подключенный с зацикливанием:
  mySerial.write(sensorValue);
  while (mySerial.available()) {
    Serial.print(mySerial.read());
  }
  Serial.println();
  delay(1);        // задержка между считываниями для стабильности
}

// Подключаем к SERCOM обработчик прерываний:
void SERCOM3_Handler()
{
  mySerial.IrqHandler();
}

Как и в случае с библиотекой Wire, пишем две строчки:

pinPeripheral(0, PIO_SERCOM); // назначаем контакту 0 функцию RX 
pinPeripheral(1, PIO_SERCOM); // назначаем контакту 1 функцию TX

Они нужны для того, чтобы переписать стандартные назначения для цифровых I/O контактов Arduino и вручить бразды правления в руки SERCOM.

Также в скетче используется функция обратного вызова:

void SERCOM3_Handler()
{
  mySerial.IrqHandler();
}

Она нужна, чтобы добавить в скетч возможность для коммуникации по I2C, поскольку библиотека Serial тоже работает при помощи прерываний.

См.также

Внешние ссылки