Метеостанция на Uno с Ethernet-шилдом — сборка из двух готовых плат, каждая по отдельности проверена и работает. Собрал, залил пример CardInfo из библиотеки — и полчаса подряд слушал initialization failed. Карта та же, модуль тот же, без шилда заводился с пол-оборота. Виновник нашёлся в четвёртой строке примера: const int chipSelect = 10. На шилде карта посажена на пин 4, а на десятке живёт сетевой контроллер W5100. Пример с дефолтной десяткой не выбирал карту вовсе — он «звонил» сетевому чипу и ждал от него ответов карты.
CS — единственная линия шины, на которой держится порядок разговора. Такты SCK и байты MOSI слышат все устройства одновременно, и только CS отличает «разговор с тобой» от «разговора при тебе»: выбран тот, чей выбор кристалла опущен в LOW. Устройств на шине несколько — значит, в каждый момент ровно один CS в LOW, остальные стоят в HIGH и молчат. Правило не требует паяльника и библиотек — только дисциплины в скетче, и именно о нём молчат примеры из состава библиотеки.
Разберём, куда смотрит дефолтный аргумент SD.begin, как разданы CS на ходовых шилдах, зачем каждому соседу OUTPUT и HIGH даже без использования и где в этой истории уровни 3,3 и 5 В. Роли линий расписаны в уроке про SPI-распиновку, устройство модуля с буфером — в уроке про устройство модуля; здесь концентрируюсь на шине и выборе CS.
CS по умолчанию — это SS платы, а не вашей карты
Аргумент SD.begin(chipSelect) — номер пина, на который заведён выбор кристалла карты. Без аргумента библиотека берёт аппаратный SS: на Uno и Nano это пин 10, на Mega — 53. Дефолт честный, но слепой: он знает про плату и ничего не знает о том, что надето сверху. Шилд перенёс карту на другой пин — и «умолчание» превратилось в выбор соседа.
Расклад ходовых плат. Ethernet-шилд на W5100: карта — 4, сеть — 10. Старый WiFi-шилд — та же пара: карта на 4, радио на 10; Uno WiFi Rev2 с модулем NINA живёт на той же шине по тем же правилам. Шилд Adafruit для логирования держит карту на 10 — совпадает с SS Uno, потому заводится из коробки без правки примера. Отдельный модуль на проводах — CS на любом свободном цифровом, чаще всего на той же десятке: тогда дефолт случайно совпадает с вашим выбором и всё «просто работает», маскируя тему до первой встречи с шилдом.
Второе правило шины: у каждого CS — режим OUTPUT и уровень HIGH, включая устройства, которых скетч не трогает. Пин в воздухе ловит наводки и подсаживается то вверх, то вниз; сосед с дребезжащим CS отвечает на шину мусором ровно в те моменты, когда карта пишет сектор. Две строки в setup — pinMode и digitalWrite HIGH — стоят дешевле всего, что может сломаться после.
#include <SPI.h>#include <SD.h>#include <DHT.h> // Ethernet-шилд: карта посажена на 4, сетевой чип W5100 - на 10const int chipSelect = 4;const int ETH_CS = 10;DHT dht(2, DHT22);const int LED = 12; unsigned long lastLog = 0; void setup() { Serial.begin(9600); pinMode(LED, OUTPUT); // сосед по шине: пин обязан быть OUTPUT и держать HIGH, // даже если сетевой частью скетч не пользуется pinMode(ETH_CS, OUTPUT); digitalWrite(ETH_CS, HIGH); dht.begin(); if (!SD.begin(chipSelect)) { Serial.println("SD error"); while (true) { } } Serial.println("SD ok, пишу строку раз в 10 секунд");} void loop() { if (millis() - lastLog >= 10000UL) { lastLog = millis(); float t = dht.readTemperature(); float h = dht.readHumidity(); File f = SD.open("log.csv", FILE_WRITE); if (f) { f.print(millis() / 1000); f.print(","); f.print(t, 1); f.print(","); f.println(h, 1); f.close(); digitalWrite(LED, HIGH); // вспышка: окно записи delay(40); digitalWrite(LED, LOW); } else { Serial.println("open failed"); } }}Почему пример с десяткой на шилде дохлый. SD.begin(10) настраивает пин 10 как выбор карты и дёргает его при каждом обращении к файлу — на шилде этот пин ведёт на W5100. Сетевой чип получает «выбор» и байты, адресованные карте, пытается их разобрать и мусорит на шине ответами; карта остаётся невыбранной и молчит. Симптомы: initialization failed при заведомо живой карте, а если запись и начнётся — сетевая половина сыплется соединениями. На Mega к паре 4 и 10 добавляется 53: аппаратный SS обязан сидеть в OUTPUT, иначе ATmega2560 вываливается из мастер-режима SPI — это уже не про карту, а про сам контроллер.
Если инициализация с правильным номером всё равно валится — дальше по методике из урока про диагностику SD.begin: карта, питание, провода и только потом библиотека. И правило из диагностического чек-листа: chipSelect в скетче один, объявлен константой сверху и правится в одном месте — переезд с Uno на Mega стоит одной строки, а не вечера с грепом по коду.
Отдельный каприз — сам SS платы. На ATmega328P пин 10 не просто десятый пин, а аппаратный вход выбора мастера: если он окажется входом и на нём проскальзывает LOW, контроллер вываливается из мастер-режима SPI, и вся шина встаёт. Библиотека SD сама переводит SS в OUTPUT при begin, но в схеме с шилдом и соседями я держу это правило руками: все CS и SS — OUTPUT, ни один не висит в воздухе. На Mega роль SS играет 53 — там его и держим.
| Конфигурация | CS карты | CS соседа | Что обязательно в скетче |
|---|---|---|---|
| Ethernet-шилд (W5100) | 4 | 10 | SD.begin(4); пин 10 — OUTPUT и HIGH |
| Старый WiFi-шилд, Uno WiFi Rev2 | 4 | 10 | та же пара: карта — 4, радио — OUTPUT и HIGH |
| Модуль на проводах, один на шине | 10 или любой | нет | SD.begin(10) — явно, не полагаясь на умолчание |
| Mega 2560 + шилд | 4 | 10 и 53 | SD.begin(4); 10 и 53 — OUTPUT и HIGH |
Уровни модуля: минута перед разговором о шине
Коротко то, что разобрано по косточкам в уроке про уровни: карта живёт на 2,7–3,6 В, и пять вольт на её входах — за пределами допуска. Дешёвый модуль решает это парой резисторов на линию: 1 кОм сверху, 2 кОм снизу — на MOSI, SCK и CS карта видит 5 × 2 / 3 = 3,33 В; питание раздаёт стабилизатор AMS1117-3.3, поэтому модуль питают от 5 В. Обратная ловушка — «призрачное питание»: модуль обесточен, а на MOSI и SCK с Uno приходят пять вольт, и ток утекает через защитные диоды входов, питая карту сквозь сигнальные линии. Внешне всё работает, но кристалл греется — на такой связке я ловил биты, встающие криво после получаса. Модуль без питания — значит, и без сигналов. MISO ходит напрямую: 3,3 В от карты выше порога HIGH у Uno, который при пятивольтовом питании равен 3,0 В. Формула делителя и подбор номиналов — в уроке про расчёт делителя.
К шине делители имеют прямое отношение: резисторная пара сидит на линии глухо и тянет её к земле, пока говорит сосед, — через нижние 2 кОм всё время течёт около 1,7 мА, а фронты на общей шине размазываются. Модуль с буфером 74LVC125A по своему CS отпускает линии в высокоимпедансное состояние — на шине с двумя и больше устройствами это не роскошь, а условие выживания. На шилдах согласование разведено заводски: доставать его не нужно, но SS платы (10 на Uno) всё равно держать OUTPUT — по причинам из прошлого раздела.
В симуляторе ниже правило шины можно потрогать руками: кнопка изображает соседа, светодиод — окно записи карты. SD движок не эмулирует, зато механику «кто сейчас в LOW» показывает честно.
В модели окно записи занимает 15 мс каждые две секунды — на плате окно той же механики, 10-50 мс на строку из урока про скорость записи. Смотрите на две вещи. Первая: конфликт ловится только при наложении — сосед, выбранный между окнами, шине не мешает, и это ровно то поведение, на котором живут шилды: сеть молчит, пока карта пишет, и наоборот. Вторая: счётчик конфликтов в модели растёт честно, а на плате тот же наложенный выбор выглядит тише — мусорный ответ на MISO, потерянная строка, зависший чип без единой жалобы в Serial. Отсюда и цена правила: две строки в setup против вечера с мультиметром.
- В каждый момент на шине SPI ровно один CS в LOW: такты слышат все, отвечает выбранный
- Дефолт SD.begin — аппаратный SS платы (10 на Uno, 53 на Mega), а не CS вашей карты
- Ethernet-шилд: карта на 4, W5100 на 10 — SD.begin(4) плюс OUTPUT и HIGH на десятке
- Невостребованный CS — OUTPUT и HIGH: пин в воздухе ловит наводки и роняет соседей
- Дешёвый модуль согласует делителями 1 кОм + 2 кОм; буфер 74LVC125A умеет отпускать шину
- 1. SD.begin() вызван без аргумента на Uno. Какой пин библиотека объявит выбором кристалла карты?
- 2. Где на Ethernet-шилде сидят CS карты и CS сетевого чипа?
- 3. Сосед по шине скетчем не используется. Что делать с его CS?
- 4. Что умеет буфер 74LVC125A, чего не умеют резисторные делители?
- 5. На общей шине сидят SD-модуль с делителями и второе устройство. Чем это грозит?
Частые вопросы по теме урока
- SD.begin() вызван без аргумента на Uno. Какой пин библиотека объявит выбором кристалла карты?
- Без аргумента библиотека берёт аппаратный SS: на Uno это 10. Дефолт знает про плату, но не про шилд, на котором карта переехала на другой пин. 10 — аппаратный SS платы
- Где на Ethernet-шилде сидят CS карты и CS сетевого чипа?
- Карта — 4, W5100 — 10. Пример с chipSelect = 10 на шилде выбирает сетевой чип вместо карты — отсюда initialization failed при живой карте. карта — 4, сеть — 10
- Сосед по шине скетчем не используется. Что делать с его CS?
- Невостребованный CS держат в OUTPUT и HIGH. LOW выбрал бы соседа постоянно, а висящий в воздухе пин подсаживается от наводок в случайные моменты. OUTPUT и HIGH: пин в воздухе ловит наводки и мусорит на шине
- Что умеет буфер 74LVC125A, чего не умеют резисторные делители?
- Буфер по CS уходит в третье состояние и не трогает шину, пока говорит сосед. Делитель паян наглухо: тянет линию и ест 1,7 мА всё время. отпускать линии в высокоимпедансное состояние по своему CS
- На общей шине сидят SD-модуль с делителями и второе устройство. Чем это грозит?
- Пассивный делитель всегда подключён: подсаживает линии и размазывает фронты, пока шиной управляет кто-то другой. Для общей шины — буфер или 4-канальный преобразователь. делители глухо висят на линиях и тянут их, пока говорит сосед