Операции записи и чтения через синхронный последовательный порт (Arduino Uno + ADNS2610)
1.Предварительные данные
У меня есть оптический датчик ADNS2610 (см. техническое описание здесь), который я подключаю к плате Arduino Uno R3.
Можно либо прочитать с любого, либо записать на датчик. Вот что говорится в техническом описании (стр. 12):
Операция записи
Операции записи, при которых данные передаются из микроконтроллера в ADNS-2610 всегда инициируется микроконтроллером и состоит из два байта. Первый байт содержит адрес (семь бит) и имеет «1» в качестве своего MSB для указания направления данных. Второй байт содержит данные. Передача синхронизируется по SCK. Микроконтроллер изменяет SDIO на спадающих фронтах SCK. ADNS-2610 считывает SDIO на восходящих фронтах SCK.
Операция чтения
Операция чтения, означающая передачу данных из ADNS-2610 в микроконтроллер, всегда инициируется микроконтроллером и состоит из двух байтов. Первый байт, содержащий адрес, записанный микроконтроллером и имеющий «0» в качестве старшего бита, что указывает на Направление данных. Второй байт содержит данные и управляется ADNS-2610. Передача синхронизирована по SCK. SDIO изменен на спадающих фронтах SCK и считывать на каждом нарастающем фронте SCK. микроконтроллер должен перейти в состояние High-Z после последних адресных данных бит. ADNS-2610 перейдет в состояние High-Z после получения последних данных бит. Еще одна вещь, которую следует отметить во время операции чтения; SCK необходимо задержать после последнего бита адресных данных, чтобы у ADNS-2610 было не менее 100 мкс для подготовки запрошенных данных. Это показано на временных диаграммах ниже (см. рисунки по 23).
2 программы Arduino
ФункцииWriteRegister
и ReadRegister
заимствованы (хотя и немного изменены) из проекта OptiMouse от zapmaker.
2.1 Функция SetAddress
Эта функция записывает первый байт: направление данных (MSB) и адрес регистра (семь бит), из которого пользователь намерен прочитать или в который записать данные.
void ADNS2610::SetAddress( uint8_t address, DataDirection direction ) const
{
pinMode( sdioPin, OUTPUT );
/*Specify data direction:
*MSB = 0 ===> read operation
*MSB = 1 ===> write operation*/
switch( direction )
{
case( toSensor ) :
address |= 0b10000000 ;
break;
case( fromSensor ) :
address &= ~0b10000000 ;
break;
}
for( uint8_t mask = 0b10000000; mask; mask >>= 1 )
{
digitalWrite( sckPin, LOW );
digitalWrite( sdioPin, address & mask );
digitalWrite( sckPin, HIGH );
}
/*Wait for 100 microseconds between address write operation
*and read data operation (see p.15 of the datasheet)*/
delayMicroseconds( 100 );
}
2.2 Функция ReadRegister
uint8_t ADNS2610::ReadRegister( uint8_t address ) const
{
SetAddress( address, fromSensor );
uint8_t data = 0;
/*Read data from a register*/
pinMode( sdioPin, INPUT );
for( int i = 7; i >= 0; i++ )
{
digitalWrite( sckPin, LOW );
digitalWrite( sckPin, HIGH );
data |= ( digitalRead( sdioPin ) << i );
}
/*Wait for 0.25 microseconds between read and either
*read or write operations (see p.15 of the datasheet)*/
delayMicroseconds( 1 );
return data;
}
2.3 Функция WriteRegister
void ADNS2610::WriteRegister( uint8_t address, uint8_t data ) const
{
SetAddress( address, toSensor );
/*Write data to a register*/
for( uint8_t mask = 0b10000000; mask; mask >>= 1 )
{
digitalWrite( sckPin, LOW );
digitalWrite( sdioPin, data & mask );
digitalWrite( sckPin, HIGH );
}
/*Wait for 100 microseconds between write and either
*read or write operations (see p.15 of the datasheet)*/
delayMicroseconds( 100 );
}
3.Обсуждение
3.1 Операция записи
Согласно техническому паспорту
... Микроконтроллер изменяет SDIO по спадам SCK. ADNS-2610 считывает SDIO по нарастающим фронтам SCK.
И вот как я понимаю код (см. раздел 2.1):
digitalWrite( sckPin, LOW );//создаем задний фронт
digitalWrite( sdioPin, address & mask );//изменить SDIO по заднему фронту
digitalWrite( sckPin, HIGH );//создаем нарастающий фронт (ADNS читает здесь)
Здесь два вопроса:
Первая строка выше создает падающий фронт только если предыдущее состояние SCK было
HIGH
, не так ли? Так что было бы лучше иметь следующее:digitalWrite( sckPin, HIGH); digitalWrite( sckPin, LOW );//создаем задний фронт digitalWrite( sdioPin, address & mask );//изменить SDIO по заднему фронту digitalWrite( sckPin, HIGH );//создаем нарастающий фронт (ADNS читает здесь)
На рисунке 20 (см. раздел 1, Операция записи) вы можете видеть, что требуется некоторая минимальная длительность тактовых импульсов (250 нс). Обеспечивает ли приведенный выше код такую длительность? Я знаю, что ATMega328p имеет частоту 16 МГц, поэтому кажется, что время между двумя операциями может быть меньше 250 нс. Я прав?
3.2 Операция чтения
Согласно техническому паспорту
SDIO изменяется на нисходящих фронтах SCK и считывается на каждом восходящем фронте SCK. Микроконтроллер должен перейти в состояние High-Z после последнего бита данных адреса. ADNS-2610 перейдет в состояние High-Z после последнего бита данных
И вот как я понимаю этот код (см. раздел 2.2):
digitalWrite( sckPin, LOW );//создаем задний фронт (здесь изменяется SDIO)
digitalWrite( sckPin, HIGH );//создаем нарастающий фронт
data |= ( digitalRead( sdioPin ) << i );//чтение SDIO по переднему фронту
Здесь три вопроса:
Первая строка в коде выше создает падающий фронт только если предыдущее состояние SCK было
HIGH
, не так ли? Было бы лучше иметь следующее:digitalWrite( sckPin, HIGH); digitalWrite( sckPin, LOW );//создаем задний фронт (здесь изменяется SDIO) digitalWrite( sckPin, HIGH );//создаем нарастающий фронт data |= ( digitalRead( sdioPin ) << i );//чтение SDIO по переднему фронту
На рисунке 23 (см. раздел 1, Операция чтения) видно, что требуется некоторая минимальная длительность тактовых импульсов (250 нс). Обеспечивает ли приведенный выше код такую длительность?
Как перевести микроконтроллер в состояние High-Z после последнего бита адресных данных?
@LRDPRDX, 👍2
1 ответ
Вопрос 1
Первая строка в коде выше создает падающий фронт только в том случае, если Предыдущее состояние SCK было HIGH, не так ли? Было бы лучше иметь следующее:
digitalWrite( sckPin, HIGH); digitalWrite( sckPin, LOW );//создаем задний фронт (здесь изменяется SDIO) digitalWrite( sckPin, HIGH );//создаем нарастающий фронт data |= ( digitalRead( sdioPin ) << i );//чтение SDIO по переднему фронту
Если начать с первого цикла.. Да. Глядя на форму сигнала цикла чтения, SCLK должен быть высоким перед инициированием запроса на чтение или запись и после завершения связи.
Вопрос 2
На рисунке 23 (см. раздел 1, Операция чтения) вы можете видеть, что это требуется некоторая минимальная длительность тактовых импульсов (250 нс). приведенный выше код обеспечивает такую продолжительность?
Here is the code for digital write function
void digitalWrite(uint8_t pin, uint8_t val)
{
uint8_t timer = digitalPinToTimer(pin);
uint8_t bit = digitalPinToBitMask(pin);
uint8_t port = digitalPinToPort(pin);
volatile uint8_t *out;
if (port == NOT_A_PIN)
return;
// Если вывод поддерживает выход ШИМ, нам нужно его отключить
// перед выполнением цифровой записи.
if (timer != NOT_ON_TIMER)
turnOffPWM(timer);
out = portOutputRegister(port);
uint8_t oldSREG = SREG;
cli();
if (val == LOW) {
*out &= ~bit;
}
else {
*out |= bit;
}
SREG = oldSREG;
}
Пожалуйста, не давайте никакой задержки. Приведенная выше функция DigtalWrite уже потребует задержки в тысячи наносекунд. Этого достаточно.
Вопрос 3:
Как перевести микроконтроллер в состояние High-Z после последнего бит адресных данных?
Я бы сделал следующее. Настройте вывод, который вы хотите установить на высокий импеданс, как входной вывод. Вы всегда можете перевести его в режим вывода, прежде чем захотите использовать его снова.
- Arduino Преобразование std:string в String
- Почему эта программа на C++ не может прочитать Serial.write() моего arduino?
- Как работает последовательная связь на Arduino?
- Последовательная связь между ПК и Arduino через RS232 с использованием C++
- Как изменить переменную при нажатии кнопки, подключенной к контакту 2
- Что является более быстрой альтернативой parseInt()?
- Как напечатать несколько номеров через Serial с очень небольшим количеством строк кода?
- Акцептант векселей ИКТ