Не удается вызвать функцию при обратном вызове для onRequest()
Я НУБ! Попытка использовать модуль ZS-042 RTC для получения текущего времени и отправки его обратно в RPi по I2C. Я могу вызвать функцию и перевести ее в последовательный режим, но когда я пытаюсь вызвать функцию для обновления глобальных переменных из функции обратного вызова для onRequest (), происходит сбой Ardiuno (Micro).
#include <Wire.h>
// LED on pin 13
const int TEMP_PIN1 = A0;
const int RELAY_PIN1 = 4;
const int SAMPLE_TIME = 100;
const byte RPI_I2C_ADDRESS = 0x08;
const int DS3231_I2C_ADDRESS = 0x68;
byte number = 0;
byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
/*
* RTC Setup
*/
// Convert normal decimal numbers to binary coded decimal
byte decToBcd(byte val)
{
return( (val/10*16) + (val%10) );
}
// Convert binary coded decimal to normal decimal numbers
byte bcdToDec(byte val)
{
return( (val/16*10) + (val%16) );
}
void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte
dayOfMonth, byte month, byte year)
{
// sets time and date data to DS3231
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // set next input to start at the seconds register
Wire.write(decToBcd(second)); // set seconds
Wire.write(decToBcd(minute)); // set minutes
Wire.write(decToBcd(hour)); // set hours
Wire.write(decToBcd(dayOfWeek)); // set day of week (0=Sunday, 6=Saturday)
Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31)
Wire.write(decToBcd(month)); // set month
Wire.write(decToBcd(year)); // set year (0 to 99)
Wire.endTransmission();
}
/*
void readDS3231time(byte *second,
byte *minute,
byte *hour,
byte *dayOfWeek,
byte *dayOfMonth,
byte *month,
byte *year)
{
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // set DS3231 register pointer to 00h
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
// request seven bytes of data from DS3231 starting from register 00h
*second = bcdToDec(Wire.read() & 0x7f);
*minute = bcdToDec(Wire.read());
*hour = bcdToDec(Wire.read() & 0x3f);
*dayOfWeek = bcdToDec(Wire.read());
*dayOfMonth = bcdToDec(Wire.read());
*month = bcdToDec(Wire.read());
*year = bcdToDec(Wire.read());
}
*/
void readDS3231time()
{
Wire.beginTransmission(DS3231_I2C_ADDRESS);
Wire.write(0); // set DS3231 register pointer to 00h
Wire.endTransmission();
Wire.requestFrom(DS3231_I2C_ADDRESS, 7);
// request seven bytes of data from DS3231 starting from register 00h
second = bcdToDec(Wire.read() & 0x7f);
minute = bcdToDec(Wire.read());
hour = bcdToDec(Wire.read() & 0x3f);
dayOfWeek = bcdToDec(Wire.read());
dayOfMonth = bcdToDec(Wire.read());
month = bcdToDec(Wire.read());
year = bcdToDec(Wire.read());
}
void displayTime()
{
//byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
// retrieve data from DS3231
//readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
readDS3231time();
// send it to the serial monitor
Serial.print(hour, DEC);
// convert the byte variable to a decimal number when displayed
Serial.print(":");
if (minute<10)
{
Serial.print("0");
}
Serial.print(minute, DEC);
Serial.print(":");
if (second<10)
{
Serial.print("0");
}
Serial.print(second, DEC);
Serial.print(" ");
Serial.print(dayOfMonth, DEC);
Serial.print("/");
Serial.print(month, DEC);
Serial.print("/");
Serial.print(year, DEC);
Serial.print(" Day of week: ");
switch(dayOfWeek){
case 0:
Serial.println("Sunday");
break;
case 1:
Serial.println("Monday");
break;
case 2:
Serial.println("Tuesday");
break;
case 3:
Serial.println("Wednesday");
break;
case 4:
Serial.println("Thursday");
break;
case 5:
Serial.println("Friday");
break;
case 6:
Serial.println("Saturday");
break;
}
}
void flash_data(int loops)
{
for (int i = 0; i < loops; i++)
{
digitalWrite(LED_BUILTIN, HIGH);
Serial.println("LED Pin HIGH: "+String(digitalRead(LED_BUILTIN)));
delay(50);
digitalWrite(LED_BUILTIN, LOW);
Serial.println("LED Pin LOW: "+String(digitalRead(LED_BUILTIN)));
delay(50);
}
}
// callback for received data
void receiveData(int byteCount)
{
Serial.println("Receiving Data... ");
if ( Wire.available() ) {
number = Wire.read();
}
Serial.println("Data Received: "+String(number));
}
void sendData() {
Serial.println("Sending Data on Request");
//byte second, minute, hour, dayOfWeek, dayOfMonth, month, year;
// retrieve data from DS3231
//readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year);
readDS3231time();
//char buf1[20];
//sprintf(buf1, "%02d:%02d:%02d %02d/%02d/%02d", hour, minute, second, dayOfMonth, month, year);
//Serial.print(buf1);
Wire.write("Hello");
}
void setup() {
Serial.begin(57600);
do {
delay(500);
} while (!Serial);
delay(500);
Serial.println("Initializing Wire I2C on "+RPI_I2C_ADDRESS);
// Join I@c bus as slave with RPI_I2C_ADDRESS 8
Wire.begin(RPI_I2C_ADDRESS);
Serial.println("Setting call back for Wire.onReceive");
Wire.onReceive(receiveData);
Wire.onRequest(sendData);
//Setup pin 13 as output and turn LED off
Serial.println("Setting LED Pin up for Output");
pinMode(LED_BUILTIN, OUTPUT);
pinMode(RELAY_PIN1, OUTPUT);
pinMode(TEMP_PIN1, INPUT);
Serial.println("Turning LED off");
digitalWrite(LED_BUILTIN, LOW);
}
float getVoltage(int pin)
{
// This function has one input parameter, the analog pin number
// to read. You might notice that this function does not have
// "void" in front of it; this is because it returns a floating-
// point value, which is the true voltage on that pin (0 to 5V).
return (analogRead(pin) * 0.004882814);
// This equation converts the 0 to 1023 value that analogRead()
// returns, into a 0.0 to 5.0 value that is the true voltage
// being read at that pin.
}
/*
* MAIN LOOP
*/
void loop() {
if ( number > 0 ) {
//Serial.println("Flash Data: "+String(number));
flash_data(number);
float voltage, avgVoltage, degreesC, degreesF;
if (number == 1) {
digitalWrite(RELAY_PIN1, HIGH);
} else if (number == 2) {
digitalWrite(RELAY_PIN1, LOW);
} else if ( number == 3 ) {
voltage = getVoltage(TEMP_PIN1);
//gets and prints the raw data from the lm35
Serial.print("Voltage: ");
Serial.print (voltage);
Serial.println(" ");
// Now we'll convert the voltage to degrees Celsius.
// This formula comes from the temperature sensor datasheet:
degreesC = (voltage - 0.48) * 100.0;
Serial.print("CELSIUS: ");
Serial.print(degreesC);
Serial.println("*C ");
//converts celsius into fahrenheit
degreesF = degreesC * (9.0/5.0) + 32.0;
Serial.print("FAHRENHEIT: ");
Serial.print(degreesF);
Serial.println("*F");
} else if ( number == 4 ) {
displayTime();
}
number = 0;
} else {
byte pinValue = bitRead(LED_BUILTIN, 3);
if ( digitalRead(LED_BUILTIN) == HIGH ) {
digitalWrite(LED_BUILTIN, LOW);
}
}
delay(SAMPLE_TIME);
}
Любые рекомендации будут оценены по достоинству! Спасибо
@Brian, 👍0
Обсуждение2 ответа
Функции обратного вызова onRequest и onReceive вызываются изнутри ISR. Таким образом, вы не можете использовать там какой-либо код, который полагается на прерывания для работы. Например, последовательная запись или печать будут работать только до тех пор, пока буфер не заполнен (поскольку фактическая связь обрабатывается прерываниями). Если буфер заполнен, последовательный код либо заблокирует, либо потеряет данные (в зависимости от версии ядра Arduino). Отсутствие блокировки в ISR-это относительно новое изменение). Такие вещи также происходят с другими функциями, управляемыми прерываниями.
Давайте рассмотрим логику ваших кодов, в частности, в функции onRequest callcack. Как написано выше, это вызывается из ISR. Мастер связался с библиотекой проводов, чтобы отправить ей данные. При этом обратном вызове библиотека спрашивает вас, что ответить мастеру. В течение этого времени текущая передача (чтение ведущего устройства, отправка ведомого устройства) уже активна, но ведомое устройство приостанавливает транзакцию до тех пор, пока данные не будут готовы, то есть до тех пор, пока вы не завершите обратный вызов onRequest. Теперь внутри обратного вызова вы пытаетесь инициировать другую транзакцию на той же шине I2C, теперь в качестве ведущего. Та же шина I2C, на которой уже происходит транзакция. Это не может сработать. Бывший подчиненный не может взять на себя управление шиной, так как первый ведущий еще не освободил ее (как вы пытались сделать это в середине транзакции. Таким образом, вы не только используете функции, управляемые прерываниями, в ISR, вы также пытаетесь перенастроить аппаратное обеспечение, от которого вы только что получили прерывание (что является плохой идеей, если вы действительно не знаете, что делаете). Библиотека настроила аппаратное обеспечение как подчиненное, и в середине транзакции вы пытаетесь настроить его как ведущее.
Я не знаю, зачем ты вообще пытаешься это сделать. Поскольку у вас уже есть RTC на той же шине, что и Pi, почему бы не сделать Pi единственным мастером и не позволить ему напрямую опросить RTC? Зачем вам нужен Arduino между ними?
Если вам действительно нужна настройка multi master по какой-либо причине (хотя я не могу придумать ни одной), вы должны выполнять транзакции независимо друг от друга. Arduino может регулярно опрашивать RTC, когда шина свободна, и буферизировать результаты. Затем он может отправить эти результаты Pi, когда он запросит Arduino сделать это.
Примечание: В настоящее время я не в курсе того, насколько хороша поддержка нескольких мастеров в библиотеке проводов в наши дни. Он включал возможность для нескольких мастеров с давних пор, но когда я проверял в последний раз, арбитраж шины не был реализован (это означает, что если 2 мастера попытаются отправить одновременно, шина заглохнет). Если это все еще так, вам не следует использовать multi master с библиотекой проводов.
Спасибо, Крисл! Простите всех. Через несколько мгновений после публикации я понял, что у меня конфликт между двумя устройствами I2C в том, как я пытаюсь это сделать, что, по моему мнению, соответствует сообщению Крисла. ДА! Я должен иметь возможность отправить запрос к RTC непосредственно от RPi по его адресу, если они оба находятся на одной шине I2C. Я обязательно опубликую это после проверки, но я почти уверен, что это не так. Тотальное перемещение костной головы., @masterofnull
Спасибо, крисл! Извините всех. Я понял через несколько мгновений после публикации, что я конфликтую между 2 устройствами I2C в том, как я пытаюсь это сделать, что, по-моему, согласуется с сообщением Крисла. ДА! Я должен быть в состоянии сделать запрос в RTC непосредственно из RPi по его адресу, учитывая, что они оба находятся на одной шине I2C. Я обязательно опубликую это, как только проверю, но я почти уверен, что это неправильно. Полное Движение Костной Головы.
- I2C - программная отправка данных
- Подключение нескольких MPU 6050 к Micro/Lenardo
- Отправка и получение различных типов данных через I2C в Arduino
- Как работают функции вне цикла void?
- Как отображать переменные на 0,96-дюймовом OLED-дисплее с библиотекой u8glib?
- Как отправить строку на мастер с помощью i2c
- Как выбрать альтернативные контакты I2C на ESP32?
- Arduino Micro против Pro Micro
Не могли бы вы показать схему вашей схемы? Вы действительно хотите, чтобы ваш Arduino был как ведущим, так и ведомым на шине I2C? Почему бы не подключить Pi через Serial?, @Edgar Bonet