Затык с обработкой трёхбайтного значения от SPI
- Войдите на сайт для отправки комментариев
Вс, 29/10/2017 - 23:18
Здравствуйте, досточтимые носители семи пядей во лбу!
Уповаю на вашу помощь.
Юзаю 24-разрядную АЦП-уху с SPI-интерфейсом, и обломался на выводе результата.
Монитор несёт какую-то хрень, а на вид, вроде, всё правильно задано.
Своими мОзгами иссяк, теперь либо в пивную, либо к знатокам :)
Ниже привожу нарезки из скетча.
Спасибо.
#include <SPI.h> byte byte_1, byte_2, byte_0; volatile long sensorValue = 0; // *** void ADC_read() { digitalWrite(ADC_CS_Pin, LOW); //Чтение регистра Data: SPI.transfer(0x42); byte_0=SPI.transfer(0); byte_1=SPI.transfer(0); byte_2=SPI.transfer(0); digitalWrite(ADC_CS_Pin, HIGH); // в случае следующей запси: sensorValue = byte_2|(byte_1<<8)|((long)byte_0<<16); //имеем на мониторе вместо sensorValue просто УЖОС: DATA: FFFFF991 27 F9 91 //(Здесь, правда, сразу хочется спросить, а почему работает: byte_2|(byte_1<<8); //хотя логика подсказывает следующее: byte_2|((int)byte_1<<8); //???) //а при такой: sensorValue = byte_2+(byte_1<<8)+((long)byte_0<<16); //получаем на мониторе искажённый sensorValue: DATA: 26FCC0 27 FC C0 // И шо теперь? } // *** void loop() { ADC_read(); Serial.println("DATA: "); Serial.println(sensorValue,HEX); Serial.println(byte_0,HEX); Serial.println(byte_1,HEX); Serial.println(byte_2,HEX); // *** }
Замените в строке сборки (long) на (unsigned long).
Дык всё верно у вас происходит: long имеет максимальное положительное значение в 65535, потом - заворот кишок в отрицательные значения, при переполнении. Хотите сдвигом манипулировать большими значениями в положительной области (а тип byte в качестве носителей отдельных байт сборного значения какбэ намекает, что имеет место быть беззнаковый тип) - юзайте unsigned long.
А ишшо проще - юзать массив и копировать:
Порядок следования байт в массиве byteArray - подберите по вкусу ;)
long имеет максимальное положительное значение в 65535
ты не прав, Аркаша.
А ишшо проще - юзать массив и копировать:
Порядок следования байт в массиве byteArray - подберите по вкусу ;)
А можно просто использовать union.
Если через сдвиг хочется, то правильно будет сдвигать результурующую переменную, а не заниматься приведением типов.
Уже показали наиправильнейший вариант с юнионом, все остальные варианты просто меркнут на его фоне :)
Дык всё верно у вас происходит: long имеет максимальное положительное значение в 65535, потом - заворот кишок в отрицательные значения, при переполнении. Хотите сдвигом манипулировать большими значениями в положительной области (а тип byte в качестве носителей отдельных байт сборного значения какбэ намекает, что имеет место быть беззнаковый тип) - юзайте unsigned long.
Дык, сам об етом думал, да вот в чём загвоздка:
"Unsigned long используется для хранения положительных целых чисел в диапазоне от 0 до 4,294,967,295 (2^32 - 1) и занимает 32 бита (4 байта) в памяти". [http://arduino.ru/Reference/UnsignedLong]
Спасибо.
А можно просто использовать union.
Этто мы не проходили... Где можно погрызть union?
Спасибо.
Если через сдвиг хочется, то правильно будет сдвигать результурующую переменную, а не заниматься приведением типов.
Замечательно! Только мне нужно было для проверки преобразования выводить и побайтно.
Спасибо.
Замените в строке сборки (long) на (unsigned long).
Не помогло :((( Что за хрень? И не понятно, почему результат с побитным ИЛИ не совпадает со сложением, хотя, вроде, это одно и то же в данном случае.
Спасибо.
в union собираются переменные, которые занимают в памяти одно и то же место.
тоись, ты можешь присвоить SPIData.Value число типа long и потом обратиться к каждому его байту отдельно через SPIValue.ValueBytes[0..3]
а можешь наоборот, пхать в ValueBytes байты последовательно и считать потом весь long, чо там получилось.
Подробнее RTFM->MSDN().
в union собираются переменные, которые занимают в памяти одно и то же место.
скажите, а нет у вас такого юнион, что бы новые данные просто сдвигали старые - нужно хранить два значения переменной: новое и пердыдущее.
как раз у мня есть, сам писал. TStack называеца.
Замените в строке сборки (long) на (unsigned long).
Не помогло :((( Что за хрень? И не понятно, почему результат с побитным ИЛИ не совпадает со сложением, хотя, вроде, это одно и то же в данном случае.
Спасибо.
PS. Но наиболее прямой метод, естественно, с union. Просто его писать чуть дольше.
andriano, большущее спасибо! Вот где собака порылась: раньше работал с двухбайтными конструкциями, и byte_2+(byte_1<<8) вполне прокатывало.
Правда, как это работает я так и не понял. Переполнения разрядов не вижу, отрицательных значений на входе не задаю, а несовпадение логического и арифметического сложений вообще ведёт в ступор.
Итак, вот что я увидел:
в union собираются переменные, которые занимают в памяти одно и то же место.
тоись, ты можешь присвоить SPIData.Value число типа long и потом обратиться к каждому его байту отдельно через SPIValue.ValueBytes[0..3]
а можешь наоборот, пхать в ValueBytes байты последовательно и считать потом весь long, чо там получилось.
Подробнее RTFM->MSDN().
DetSimen, спасибо за мастер-класс!
С Union выглядит примерно так (тока я не проверял может с порядком байт напутал, убегаю, вы проверьте)
long имеет максимальное положительное значение в 65535
ты не прав, Аркаша.
Йопт, бывает, Сеня, бывает - осень, склероз :) Но щас отбрехаюсь, как обычно: зависит от системы, и от размерности типов под ней ;)
xDriver, не совсем получается.
Во-первых, в операторе №07:
byte
ValueBytes[4];изменить на
byte
ValueBytes[3];
Во-вторых, порядок байтов: у меня MSBFIRST, а в этом скетче выводится (
SPIData.Value)
LSBFIRST. Я пока не придумал, как исправить.Спасибо.
andriano, большущее спасибо! Вот где собака порылась: раньше работал с двухбайтными конструкциями, и byte_2+(byte_1<<8) вполне прокатывало.
Правда, как это работает я так и не понял. Переполнения разрядов не вижу, отрицательных значений на входе не задаю,
Компилятор на этот счет имеет другое мнение и воспринимает 0xF9 и 0x91 как отрицательные числа.
а несовпадение логического и арифметического сложений вообще ведёт в ступор.
Напрасно. Именно этот факт - одна из двух подсказок, что компилятор трактует числа как отрицательные и при преобразовании типов расширяет знаковый разряд. (вторая подсказка - FF в старших разрядах результата)
andriano, спасибо! Теперь "всё в ёлочку".
Эх, надо чаще советоваться с компиллятором!
xDriver, не совсем получается.
Во-первых, в операторе №07:
byte
ValueBytes[4];изменить на
byte
ValueBytes[3];
Во-вторых, порядок байтов: у меня MSBFIRST, а в этом скетче выводится (
SPIData.Value)
LSBFIRST. Я пока не придумал, как исправить.Спасибо.
Во-первых, не утверждайте то чего не знаете, не понимаете... компилятор за вас конечно же "додумает", но я размерность элементов union стараюсь описывать одинаковыми, так понятнее.
Во-вторых, да я как и подозревапл порядок байт попутал, исправляюсь...
Во-первых, не утверждайте то чего не знаете, не понимаете... компилятор за вас конечно же "додумает", но я размерность элементов union стараюсь описывать одинаковыми, так понятнее.
Приношу извинения за некорректность в адрес мэтра.
Меня смутил результат, вот, даже исправленный:
DATA:
D9162800
28
16
D9
Налицо лишний байт (00), четвёртый по счёту. Думал, опечатка в размерности массива.
Спасибо.
Никакой ноль не лишний, просто у вас порядок следования байт неправильный. SPI как MSBFIRST настроен, а байты в массив складываете с младшего. У вас SPIData.ValueBytes[0] всегда 0х00 должен быть. А в цикле, при размещении в массив, должно быть примерно так
при этом не забываем на всякий случай "занулять" SPIData.ValueBytes[0], можно в этом же цикле прописать, можно отдельной строчкой.
Т.е. у вас при настройке MSBFIRST первый пришедший байт (старший) должен попасть в ValueBytes[1], следующий в ValueBytes[2] и последнйи младший в ValueBytes[3], а ValueBytes[0] должен быть 0х00.