Работа с основной библиотекой Wire
Я изучаю драйвер кода библиотеки Wire utility/twi.h
, чтобы узнать, как работают буферизация twi и управление ISR.
На самом деле я скопировал заголовочные/исходные файлы из пути Arduino F:\Program Files\Arduino\hardware\arduino\avr\libraries\Wire\src\utility
и поместил их в пользовательские библиотеки Arduino и изменил .c
на .cpp
, и все заработало.
На самом деле я пробовал разные библиотеки i2c для avr, и эта библиотека кода, по моему мнению, одна из лучших.
Мой тестовый код предназначен для инициализации HMC5883L и считывания этих настроек конфигурации из первых SFR HMC5883L.
Я использую Serial.prints внутри/вне ISR для отладки своего кода. Он работает почти нормально. Я не уверен, что это может вызвать какие-либо проблемы.
Но поскольку у меня нет полного понимания того, как работает twi, я сейчас пытаюсь понять, как отображается контент, поступающий в ISR.
Я думаю, что инициализация работает нормально, но процедура чтения может быть не такой, как должна быть.
Вот мой код: код приложения в Arduino IDE и код в библиотеке.
Код Arduino IDE:
#include "twi.h"
/////////////////////////////////////////////////// //////////////////////////
// HMC5883L
#define HMC5883L 0x1E
#define CONFIG_REG_A 0x00 //Чтение/Запись
#define CONFIG_REG_B 0x01 //Чтение/Запись
#define MODE_REGISTER 0x02 //Чтение/Запись
#define DATA_OUTPUT_X_MSB_REGISTER 0x03 //Чтение
#define DATA_OUTPUT_X_LSB_REGISTER 0x04 //Чтение
#define DATA_OUTPUT_Z_MSB_REGISTER 0x05 //Чтение
#define DATA_OUTPUT_Z_LSB_REGISTER 0x06 //Чтение
#define DATA_OUTPUT_Y_MSB_REGISTER 0x07 //Чтение
#define DATA_OUTPUT_Y_LSB_REGISTER 0x08 //Чтение
#define STATUS_REGISTER 0x09 //Чтение
#define IDENTIFICATION_REGISTER_A 0x10 //Чтение
#define IDENTIFICATION_REGISTER_B 0x11 //Чтение
#define IDENTIFICATION_REGISTER_C 0x12 //Чтение
#define DECLINATION_ANGLE 3.46
void hmc5883l_init(void);
void init_read(void);
void hmc5883l_print_serial(void);
void setup() {
Serial.begin(9600);
twi_init();
hmc5883l_init();
init_read();
}
void loop() {
hmc5883l_print_serial();
}
void hmc5883l_init(void){
uint8_t err,HMC5883L_ini[] = {CONFIG_REG_A,0x78,0x20,0x00};//
err = twi_writeTo(HMC5883L,HMC5883L_ini,4,1,1);
if(err != 0){Serial.print("error of init is");Serial.println(err);}
Serial.println();
}
void init_read(void){
uint8_t err,len,dat[3];
err = twi_writeTo(HMC5883L,0x00,1,0,1);
if(err != 0){Serial.print("error of init is");Serial.println(err);}
Serial.println();
len = twi_readFrom(HMC5883L,dat,3,1);
Serial.print("length of read op: ");
Serial.println(len);
Serial.println();
Serial.print("config settings: ");
Serial.print(dat[0],HEX);Serial.print("\t");
Serial.print(dat[1],HEX);Serial.print("\t");
Serial.println(dat[2],HEX);
}
void hmc5883l_print_serial(void){
int16_t x,z,y;
uint8_t data[6],len,err, dst_reg[] = {0x03};
float heading,heading_in_degrees,declination_angle_YANBU;
err = twi_writeTo(HMC5883L,dst_reg,1,0,1);
if(err != 0){Serial.print("error of init is");Serial.println(err);}
Serial.println();
len = twi_readFrom(HMC5883L,data,6,1);
Serial.print("len is: ");Serial.println(len);
x = data[0] << 8 | data[1];
z = data[2] << 8 | data[3];
y = data[4] << 8 | data[5];
heading = atan2(y,x);
declination_angle_YANBU = ((3.0 + (52.0 / 60.0)) / (180 / M_PI)); // в городе Янбу это
heading += declination_angle_YANBU;
if (heading < 0){heading += 2 * PI;}
if (heading > 2 * PI){heading -= 2 * PI;}
heading_in_degrees = heading * 180 / M_PI;
Serial.println(heading_in_degrees);
//Serial.print("ось x\t");Serial.print("ось z\t");Serial.println("ось y");
Serial.print(x);Serial.print("\t");
Serial.print(z);Serial.print("\t");
Serial.println(y);
}
А это основная библиотека:
В этой части я поместил Serial.prints в корпуса ISR, чтобы отобразить, что происходит с начала буферов tx/rx. Кроме того, я вырезал ведомую половину кода, потому что в этой задаче в этом нет необходимости, поскольку я имею дело только с HMC5883L как с ведомым устройством, а я как с главным.
#include <math.h>
#include <stdlib.h>
#include <inttypes.h>
#include <avr/io.h>
#include <avr/interrupt.h>
#include <compat/twi.h>
#include "Arduino.h" // для digitalWrite
#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif
#include "pins_arduino.h"
#include "twi.h"
static volatile uint8_t twi_state;
static volatile uint8_t twi_slarw;
static volatile uint8_t twi_sendStop; // должна ли сделка завершиться стопом
static volatile uint8_t twi_inRepStart; // в середине повторного запуска
static void (*twi_onSlaveTransmit)(void);
static void (*twi_onSlaveReceive)(uint8_t*, int);
static uint8_t twi_masterBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_masterBufferIndex;
static volatile uint8_t twi_masterBufferLength;
static uint8_t twi_txBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_txBufferIndex;
static volatile uint8_t twi_txBufferLength;
static uint8_t twi_rxBuffer[TWI_BUFFER_LENGTH];
static volatile uint8_t twi_rxBufferIndex;
static volatile uint8_t twi_error;
void twi_init(void)
{
// инициализируем состояние
twi_state = TWI_READY;
twi_sendStop = true; // значение по умолчанию
twi_inRepStart = false;
// активируем внутренние подтягивания для twi.
digitalWrite(SDA, 1);
digitalWrite(SCL, 1);
// инициализируем двойной прескалер и скорость передачи данных
cbi(TWSR, TWPS0);
cbi(TWSR, TWPS1);
TWBR = ((F_CPU / TWI_FREQ) - 16) / 2;
// включаем модуль twi, подтверждения и прерывание twi
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA);
}
void twi_disable(void)
{
// отключаем модуль twi, подтверждения и прерывание twi
TWCR &= ~(_BV(TWEN) | _BV(TWIE) | _BV(TWEA));
// деактивируем внутренние подтягивания для twi.
digitalWrite(SDA, 0);
digitalWrite(SCL, 0);
}
uint8_t twi_readFrom(uint8_t address, uint8_t* data, uint8_t length, uint8_t sendStop)
{
uint8_t i;
// гарантируем, что данные поместятся в буфер
if(TWI_BUFFER_LENGTH < length){
return 0;
}
// ждем, пока twi будет готов, становимся главным получателем
while(TWI_READY != twi_state){
continue;
}
twi_state = TWI_MRX;
twi_sendStop = sendStop;
// сброс состояния ошибки (0xFF.. ошибок не произошло)
twi_error = 0xFF;
// инициализируем переменные итерации буфера
twi_masterBufferIndex = 0;
twi_masterBufferLength = length-1; // Это не интуитивно понятно, читайте дальше...
twi_slarw = TW_READ;
twi_slarw |= address << 1;
if (true == twi_inRepStart) {
twi_inRepStart = false; // помните, мы имеем дело с ASYNC ISR
do {
TWDR = twi_slarw;
} while(TWCR & _BV(TWWC));
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // включаем INT, но не START
}
else
// отправляем условие начала
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTA);
// ждем завершения операции чтения
while(TWI_MRX == twi_state){
continue;
}
if (twi_masterBufferIndex < length)
length = twi_masterBufferIndex;
// копируем буфер twi в данные
for(i = 0; i < length; ++i){
data[i] = twi_masterBuffer[i];
}
return length;
}
uint8_t twi_writeTo(uint8_t address, uint8_t* data, uint8_t length, uint8_t wait, uint8_t sendStop)
{
uint8_t i;
// гарантируем, что данные поместятся в буфер
if(TWI_BUFFER_LENGTH < length){
return 1;
}
// ждем, пока twi будет готов, становимся главным передатчиком
while(TWI_READY != twi_state){
continue;
}
twi_state = TWI_MTX;
twi_sendStop = sendStop;
// сброс состояния ошибки (0xFF.. ошибок не произошло)
twi_error = 0xFF;
// инициализируем переменные итерации буфера
twi_masterBufferIndex = 0;
twi_masterBufferLength = length;
// копируем данные в twi-буфер
for(i = 0; i < length; ++i){
twi_masterBuffer[i] = data[i];
}
// построить sla+w, адрес ведомого устройства + бит w
twi_slarw = TW_WRITE;
twi_slarw |= address << 1;
// если у нас повторный старт, то мы уже отправили START
// в ISR. Не делай этого снова.
//
if (true == twi_inRepStart) {
// если мы находимся в состоянии повторного старта, то мы уже отправили старт,
// (@@@ мы надеемся), а конечный автомат TWI просто ждет адресного байта.
// Нам нужно выйти из состояния повторного запуска, прежде чем мы разрешим прерывания,
// поскольку ISR является ASYNC, и мы можем запутаться, если нажмем на ISR перед очисткой
// вверх. Кроме того, не разрешайте прерывание START. Может быть один ожидающий рассмотрения из
// повторный запуск, который мы отправили сами, и это действительно запутало бы ситуацию.
twi_inRepStart = false; // помните, мы имеем дело с ASYNC ISR
do {
TWDR = twi_slarw;
} while(TWCR & _BV(TWWC));
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE); // включаем INT, но не START
}
else
// отправляем условие запуска
TWCR = _BV(TWINT) | _BV(TWEA) | _BV(TWEN) | _BV(TWIE) | _BV(TWSTA); // включаем INT
// ждем завершения операции записи
while(wait && (TWI_MTX == twi_state)){
continue;
}
if (twi_error == 0xFF)
return 0; // успех
else if (twi_error == TW_MT_SLA_NACK)
return 2; // ошибка: адрес отправлен, Nack получен
else if (twi_error == TW_MT_DATA_NACK)
return 3; // ошибка: данные отправлены, но нет получено
else
return 4; // другая ошибка twi
}
void twi_reply(uint8_t ack)
{
if(ack){
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT) | _BV(TWEA);
}else{
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWINT);
}
}
void twi_stop(void)
{
// отправляем стоп-условие
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT) | _BV(TWSTO);
while(TWCR & _BV(TWSTO)){
continue;
}
// обновляем два состояния
twi_state = TWI_READY;
}
/*
* Function twi_releaseBus
* Desc releases bus control
* Input none
* Output none
*/
void twi_releaseBus(void)
{
// освобождаем шину
TWCR = _BV(TWEN) | _BV(TWIE) | _BV(TWEA) | _BV(TWINT);
// обновляем два состояния
twi_state = TWI_READY;
}
ISR(TWI_vect)
{
switch(TW_STATUS){
// Все мастера
case TW_START: // отправлено условие запуска
case TW_REP_START: // отправлено повторное условие запуска
// копируем адрес устройства и бит чтения/записи в выходной регистр и подтверждаем
TWDR = twi_slarw;
Serial.print("mt add sent is: 0x");
Serial.println(twi_slarw, HEX);
twi_reply(1);
break;
// Главный передатчик
case TW_MT_SLA_ACK: // подтвержденный адрес подчиненного получателя
case TW_MT_DATA_ACK: // подтвержденные данные подчиненного получателя
// если есть данные для отправки, отправьте их, иначе остановитесь
if(twi_masterBufferIndex < twi_masterBufferLength){
// копируем данные в выходной регистр и подтверждаем
Serial.print("mt byte transmitted no: ");
Serial.println(twi_masterBufferIndex);
Serial.print("content of tx byte: 0x");
Serial.println(twi_masterBuffer[twi_masterBufferIndex], HEX);
TWDR = twi_masterBuffer[twi_masterBufferIndex++];
twi_reply(1);
}else{
if (twi_sendStop){
Serial.println("mt stop");
twi_stop();
}
else {
Serial.println("mt repst");
twi_inRepStart = true; // мы отправим START
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;
twi_state = TWI_READY;
}
}
break;
case TW_MT_SLA_NACK: // адрес отправлен, NACK получен
Serial.println("mt add sent nack");
twi_error = TW_MT_SLA_NACK;
twi_stop();
break;
case TW_MT_DATA_NACK: // данные отправлены, Nack получен
twi_error = TW_MT_DATA_NACK;
twi_stop();
break;
case TW_MT_ARB_LOST: // арбитраж шины проигран
twi_error = TW_MT_ARB_LOST;
twi_releaseBus();
break;
// Главный получатель
case TW_MR_DATA_ACK: // данные получены, подтверждение отправлено
// помещаем байт в буфер
Serial.print("mr byte received no: ");
Serial.println(twi_masterBufferIndex);
Serial.print("content of rx byte: 0x");
Serial.println(twi_masterBuffer[twi_masterBufferIndex], HEX);
twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
case TW_MR_SLA_ACK: // адрес отправлен, подтверждение получено
// подтверждаем, если ожидается больше байт, в противном случае — нет
if(twi_masterBufferIndex < twi_masterBufferLength){
twi_reply(1);
}else{
twi_reply(0);
}
break;
case TW_MR_DATA_NACK: // данные получены, Nack отправлен
// помещаем последний байт в буфер
Serial.print("mr last received byte: ");
Serial.println(twi_masterBufferIndex);
Serial.print("content of last rx byte: 0x");
Serial.println(twi_masterBuffer[twi_masterBufferIndex], HEX);
twi_masterBuffer[twi_masterBufferIndex++] = TWDR;
if (twi_sendStop)
twi_stop();
else {
twi_inRepStart = true;
TWCR = _BV(TWINT) | _BV(TWSTA)| _BV(TWEN) ;
twi_state = TWI_READY;
}
break;
case TW_MR_SLA_NACK:
twi_stop();
break;
}
Теперь проблема в следующем:
Я получаю эти показания на последовательном мониторе Arduino. Примечание. Я написал свои примечания к каждому разделу.
// инициализация
// Я думаю, что этот процесс выполняется нормально, отправляются точные байты
// но каждый раз, когда я открываю последовательный монитор, я сначала получаю мусор
// окна, как этот перевернутый вопросительный знак, который я получаю чаще всего
// Есть ли что-то вроде функции сброса для очистки?
⸮mt add sent is: 0x3C
mt byte transmitted no: 0
content of tx byte: 0x0
mt byte transmitted no: 1
content of tx byte: 0x78
mt byte transmitted no: 2
content of tx byte: 0x20
mt byte transmitted no: 3
content of tx byte: 0x0
mt stop
// записываем целевой регистр 0x00, но получаем 0xA3 ??!
m
mt byte transmitted no: 0
content of tx byte: 0xA3
mt stop
// Здесь, запрашивая 6-байтовые данные, я отправляю чтение HMC5883L, который равен 0x3D
// но 1-й байт должен быть 0x78, затем 2-й 0x20, 3-й 0x00
// но результат другой! как будто они как-то сдвинуты
// В конце я пытаюсь прочитать переданный массив функции, но
// Я получаю 0!!
mt add sent is: 0x3D
mr byte received no: 0
content of rx byte: 0xA3
mr byte received no: 1
content of rx byte: 0x78
mr last received byte: 2
content of last rx byte: 0x20
length of read op: 3
config settings: 0 0 0
@R1S8K, 👍1
Обсуждение1 ответ
Лучший ответ:
Второй параметр twi_writeTo
должен быть указателем на буфер. Теперь у вас есть
twi_writeTo(HMC5883L,0x00,1,0,1);
который отправляет чтение байта по адресу 0x00. измените его на
uint8_t b = 0x00;
twi_writeTo(HMC5883L,&b,1,0,1);
(Массивы C являются указателем на первый элемент)
Да, я сделал это. Ничего страшного. Но проблема в чтении регистров конфигурации. Я могу автоматически читать регистры данных, но не могу автоматически читать регистры конфигурации! Я должен сделать это один за другим., @R1S8K
это совсем другой вопрос. в оригинале речь шла о twi (под)библиотеке. новое касается конкретного устройства. автоматическое приращение регистра предназначено для ускорения обычного варианта использования. с настройкой не нужно торопиться, @Juraj
Да, извините, на самом деле он подключен к устройству, но, возможно, принцип общий для устройств i2c. Проблема, которую я пытаюсь понять, заключается в этой особенности общения с устройствами i2c. Я думаю, что существует два типа связи с устройствами i2c; по крайней мере, к HMC5883L. Я думаю, если я хочу разобраться с чтением регистров конфигурации, то мне придется делать это по одному, если с чтением регистров данных, то я могу сделать это автоматически., @R1S8K
это зависит от устройства, а не от функции I2C, @Juraj
А, ок, понятно, что у меня действительно не так уж много информации :) Спасибо чувак за помощь, я очень ценю это. Сначала я опубликовал весь этот длинный пост и действительно не знаю, чего пытаюсь добиться, потому что мне нужно знать очень много связанных с этим проблем. Думаю удалить тему! Что вы думаете ? Вы согласны ? а затем я снова привожу себя в порядок и рассматриваю еще одну полезную тему. Есть рекомендации?, @R1S8K
Вы имеете в виду последнее обновление поста?, @R1S8K
- устаревшее преобразование из строковой константы в 'char*'
- Отправка и получение различных типов данных через I2C в Arduino
- Как выбрать альтернативные контакты I2C на ESP32?
- как быстро loop() работает в Arduino
- Альтернативы библиотеке Wire для I2C
- как отправить аргумент объектам ESP8266WebServer в функции
- Библиотека Wire.h работает на Uno, но не компилируется для ATtiny85
- I2C связь между Arduino Uno и Nodemcu32-s (ESP32)
Это не основная библиотека, эти функции нижнего уровня являются частью библиотеки Wire., @Jot
Ааа, ок, я тогда не различал основные/нижние функции. Хорошо, а что может быть похоже на основную библиотеку? Являются ли
Arduino.h
и другие связанные библиотеки, находящиеся в папке Arduino в папке установки, основными библиотеками для Arduino?, @R1S8KНе существует «основной» библиотеки. Существует ряд библиотек Wire (официальных и других совместимых библиотек) для разных процессоров. Так уж получилось, что библиотека Wire для микроконтроллеров avr имеет два слоя., @Jot
Да, спасибо за разъяснения. Конечно, я думаю, что одна библиотека не может охватить все семейства микроконтроллеров, она может сосредоточиться на одном конкретном семействе или группе микроконтроллеров., @R1S8K