Удаленное управление. I2C. Гироскоп.

  • Автор
И снова здравствуйте.

Раз уже сайт совсем умер, то напишу, пожалуй, ещё...А ну, как оживет.

По случаю достал гироскоп на чипе L3G4200D, которым можно управлять как по I2C, так и по SPI. Даташит приводить на него, думаю, не стоит, потому как это легко гуглится. При помощи интерфейса i2c буду получить от него какие-нибудь сведения. Например значения по x, y и z. Что ж еще нужно от гироскопа?
Сначала некоторые константы:
адрес по-умолчанию = 0x69 (можно обнаружить прямо на малине при помощи, например i2cdetect)
5 контрольных регистров 0x20..0x24
Регистры значений X, Y, Z соответственно (0х28, 0х29), (0х2A, 0х2B), (0х2C, 0х2D). Значения занимают каждый по 2 байта в таком виде (Low, High).
Lua:

device_address = 0x69
ctrl_regs = { 0x20, 0x21, 0x22, 0x23, 0x24 }

R_xL = 0x28
R_xH = 0x29

R_yL = 0x2A
R_yH = 0x2B

R_zL = 0x2C
R_zH = 0x2D

Чтоб было интереснее сделаем метатаблицу lua для работы с данным чипом. Чтоб работать с гироскопом, как с отдельным объектом.

i2c = fr.client.smbus -- псевдоним для клиента smbus

-- пустая таблица без ничего.
L3G4200D = { }

-- метод new, который вернут новый экземпляр таблицы
L3G4200D.new = function( )
inst = { }
for k, v in pairs(L3G4200D) do
inst[k] = v
end
-- откроем девайс и сохраним его в нашей таблице.
inst.i = assert(i2c.open( 1, device_address ))
-- это метатаблица
setmetatable( inst, L3G4200D )
return inst
end

теперь можно сделать что-то типа

local r = L3G4200D.new( )

Код создаст экземпляр таблицы, откроет девайс и если все нормально, с ним можно работать.
для того, чтоб настроить на работу устройство, добавлю функцию setup

-- настройка состоит в том, чтоб записать нужные регистры нужными значениями.
L3G4200D.setup = function( self, scale )
-- настроим таблицу. write_bytes ожидает таблицу, в которой указано [регистр]=значение
local data = {
[ctrl_regs[1]] = 15, -- включим
[ctrl_regs[2]] = 0, -- нормальный режим. По-умолчанию
[ctrl_regs[3]] = 8,
[ctrl_regs[4]] = scale_values[scale], -- установка scale
[ctrl_regs[5]] = 0 -- по-умолчанию
}
-- запишем.
assert(self.i:write_bytes(data))
end

Если не сработал assert, то гироскоп будет включен и готов к чтению данных. Функция для чтения тоже довольно проста

-- табица для чтения. read_bytes ожидает в таблице номера регистров для чтения.
read_xyz_data = { R_xL, R_xH, R_yL, R_yH, R_zL, R_zH }
L3G4200D.read_xyz = function( self )
-- читаем
local res = assert(self.i:read_bytes( read_xyz_data ))
-- возвращаем таблицу с уже поправленными значениями { x = ..,y = .., z = .. }
return { x = res[R_xH] << 8 | res[R_xL],
y = res[R_yH] << 8 | res[R_yL],
z = res[R_zH] << 8 | res[R_zL] }
end

Теперь есть полный инструмент для получения данных с гироскопа. Далее я приведу скрипт, который раз в 100 миллисекунд вычитывает данные и выбрасывает их на консоль.

i2c = fr.client.smbus
eq = fr.client.event_queue -- для работы с очередью событий клиента
eqt = eq.timer -- таймеры клиента

device_address = 0x69
ctrl_regs = { 0x20, 0x21, 0x22, 0x23, 0x24 }
scale_values = { [250]=0, [500]=16, [2000]=48 }

R_xL = 0x28
R_xH = 0x29

R_yL = 0x2A
R_yH = 0x2B

R_zL = 0x2C
R_zH = 0x2D

read_xyz_data = { R_xL, R_xH, R_yL, R_yH, R_zL, R_zH }

L3G4200D = { }

L3G4200D.new = function( )
inst = { }
for k, v in pairs(L3G4200D) do
inst[k] = v
end
inst.i = assert(i2c.open( 1, device_address ))
setmetatable( inst, L3G4200D )
return inst
end

L3G4200D.setup = function( self, scale )
-- setup table for write
local data = {
[ctrl_regs[1]] = 15,
[ctrl_regs[2]] = 0,
[ctrl_regs[3]] = 8,
[ctrl_regs[4]] = scale_values[scale],
[ctrl_regs[5]] = 0
}
-- write registry
assert(self.i:write_bytes(data))
end

L3G4200D.read_xyz = function( self )
local res = assert(self.i:read_bytes( read_xyz_data ))
return { x = res[R_xH] << 8 | res[R_xL],
y = res[R_yH] << 8 | res[R_yL],
z = res[R_zH] << 8 | res[R_zL] }
end

-- функция таймера, которая вычитает данные и выведет их на консоль
function show_values( err, dev ) -- первый параметр - ошибка таймера. при нормальной работе err=nil
if nil == err then
local values = dev:read_xyz( ) -- вычитаем
print( "X:", values.x,
"Y:", values.y,
"Z:", values.z ) -- ... и выведем
eqt.post( show_values, {milli=100}, dev ) -- поставим новое задание в таймер на 100 milliсекунд
else -- если ошибка
fr.exit(err) -- завершимся. fr.exit( ) остановит очередь
end
end

function main( )
fr.run( ) -- запустим очередь событий
local r = L3G4200D.new( ) -- откроем устройство
r:setup(2000) -- настроем устройство
show_values( nil, r ) -- запустим вывод.
end

теперь можно проверить: 192.168.1.3:12345 - адрес и порт агента на малине. Запущу и подвигаю железку немного.

$ ./lua_client -s 192.168.1.3:12345 -e L3G4200D.lua
X: 129 Y: 500 Z: 65489
X: 50 Y: 65468 Z: 65508
X: 26 Y: 4 Z: 14
...............
X: 34869 Y: 37125 Z: 21348
X: 57781 Y: 65259 Z: 3636
X: 32752 Y: 32752 Z: 32832
...............
X: 76 Y: 78 Z: 23
X: 73 Y: 7 Z: 11
X: 78 Y: 62 Z: 65519
X: 48 Y: 65495 Z: 45
X: 14 Y: 4 Z: 6
^C

На этом все, спасибо за внимание.
PS: скрипт можно взять тут
PPS: Добавил в пример получение значения температурного регистра: 0x26

Комментарии:

Для написания комментариев необходимо зарегистрироваться