Тому, что на блоге не было давно статей с детальным обзором датчиков SmartBricks, есть одно объяснение - предыдущие обзоры рассматривали аналоговые датчики, которые по поведению ничем ни отличались от уже существующих LEGO датчиков, поэтому и при их программировании в тестах применялись те же самые средства, что и для обычных датчиков. |
Следует отметить, что для среды программирования NXT-G, компания SmartBricks предлагает для загрузки уже готовые блоки, а также и примеры их использования. Но по причине неудобства использования этой среды в целом для тестирования - это больше касается возможностей взаимодействия с компьютером, нежели самого процесса программирования и производительности, было решено не использовать NXT-G для подготовки обзоров.
Прежде, чем перейти описанию того как работать с цифровыми датчиками в языках NXC и Python, нужно сначала обратить внимание на, то как устроен интерфейс общения NXT блока с датчиком через протокол I2C. В целом, неплохая вводная есть известной книге "Extreme NXT: Extending the LEGO MINDSTORMS NXT to the Next Level" (*) и есть надежда, что это описание перекочует в "перевод" на русский язык, который делается энтузиастами из Оренбурга. Сейчас же, в двух словах можно сказать, что цифровой датчик по сути представляет из себя отдельное вычислительное устройство, которое может отдавать данные - обработанные результаты измерений, причем обработка измерений может контролироваться снаружи - на устройство могут подаваться входные данные, задающие правила вычислений, или переменные, участвующие в этих вычислениях. Иными словами, NXT блок может передавать в датчик данные по I2C шине и считывать из датчика данные.
Чтобы не спутать такое вычислительное устройство с другим, помимо порта подключения к NXT блоку, у него есть свой идентификатор, называемый также адрес. Адрес будет одним и тем же для двух одинаковых датчиков.
Чтение и запись данных происходит через регистры - специальные области памяти, поэтому каждый регистр имеет свой адрес. Разные устройства-датчики могут иметь одни и те же адреса для работы c регистрами. Они не похожи на обычные регистры процессоров хотя бы по той причине, что можно за одну операцию записи или чтения можно обратиться сразу к нескольким регистрам, указав первый из них, а количество данных считываемых из устройства - длиной в несколько регистров-ячеек.
Например, есть два регистра с адресами 0x42 и 0x44 (адреса по привычке обозначаются шестнадцатеричными числами) и каждый из них адресует по два байта информации. В итоге, оба регистра можно считать за одну операцию, указав адрес 0x42 и количество необходимых данных в 4 байта.
Поскольку датчику и шине данных (она последовательная - побитовая) требуется определенное время на предоставление информации, то языки программирования могут предоставлять механизмы опроса шины - готовы ли новые данные. Имеет смысл использовать эти механизмы для обеспечения целостности данных.
Теперь стоит перейти к конкретике.
На текущий момент компания SmartBricks выпускает три цифровых датчика:
Датчик | I2C адрес | |
---|---|---|
Датчик магнитного поля | ||
Приемник сигналов ДУ | ||
Датчик линии |
Каждый из них поддерживает разное количество регистров для чтения данных и передачи управляющей информации.
Язык программирования NXC предоставляет множество функций для работы с I2C устройствами. Наиболее часто используемые из них:
I2CBytes Выполняет транзакции чтения/записи. | В качестве входного параметра, помимо порта датчика, принимает массив данных, где должен быть указан адрес устройства, адрес регистра для записи или чтения данных, данные для записи (если нужно). Другим параметров является количество данных, сколько ожидается в буфере для чтения. |
ReadI2CRegister Чтение байта данных из конкретного регистра. | Входные данные: адрес устройства, адрес регистра. |
WriteI2CRegister Запись байта данных в конкретный регистр | Входные данные: адрес устройства, адрес регистра. |
I2CCheckStatus Проверка состояния шины данных для определенного порта |
Как результат, для простейшей работы с датчиком линии можно составить следующую библиотеку функций:
#define I2CAddr_SBLine 0x54
#define regSBLineMode 0x40
#define regSBLineIndicator 0x41
#define regSBLineSteering 0x42
#define regSBLineSteeringPID 0x43
#define regSBLineBinary 0x44
sub SetSensorSBLine(const byte port) {
SetSensorLowspeed(port);
}
sub SetSensorSBLineIndicatorValue(const byte port, byte value) {
while (I2CCheckStatus(port) == STAT_COMM_PENDING);
WriteI2CRegister(port, I2CAddr_SBLine, regSBLineMode, 1);
while (I2CCheckStatus(port) == STAT_COMM_PENDING);
WriteI2CRegister(port, I2CAddr_SBLine, regSBLineIndicator, value);
}
char SensorSBLineValue(const byte port) {
char value;
while (I2CCheckStatus(port) == STAT_COMM_PENDING);
WriteI2CRegister(port, I2CAddr_SBLine, regSBLineMode, 0);
while (I2CCheckStatus(port) == STAT_COMM_PENDING);
ReadI2CRegister(port, I2CAddr_SBLine, regSBLineSteering, value);
return value;
}
byte SensorSBLineValueBin(const byte port) {
byte value;
while (I2CCheckStatus(port) == STAT_COMM_PENDING);
WriteI2CRegister(port, I2CAddr_SBLine, regSBLineMode, 0);
while (I2CCheckStatus(port) == STAT_COMM_PENDING);
ReadI2CRegister(port, I2CAddr_SBLine, regSBLineBinary, value);
return value;
}
Пример, ниже поможет получить представление, как работать с этими функциями:
task main() {
SetSensorSBLine(S1);
byte val;
while(! ButtonPressed(BTNCENTER)) {
val = SensorSBLineValueBin(S1);
for(int i=0; i<8; i++) {
NumOut(i*6, LCD_LINE1, (val >> i) & 0x01);
}
Wait(500);
}
Wait(1000);
while(! ButtonPressed(BTNCENTER)) {
ClearScreen();
NumOut(0, LCD_LINE1, SensorSBLineValue(S1));
Wait(500);
}
Wait(1000);
byte i = 1;
while(! ButtonPressed(BTNCENTER)) {
SetSensorSBLineIndicatorValue(S1, i);
i = i == 128 ? 1 : i * 2;
Wait(500);
}
}
Поскольку все модули языка Python по сути - открытый исходный код, то в них без труда можно найти то, как сейчас реализована не только работа с ультразвуковым датчиком расстояния, который единственный цифровой из стандартных датчиков LEGO, но и с датчиками компаний HiTechnic и Mindsensors. Используя эти примеры, можно без особого труда подготовить модуль для работы с датчиками SmartBricks:
from .common import * from .digital import BaseDigitalSensor class Line(BaseDigitalSensor): """SMARTBRICKS Line sensor.""" I2C_ADDRESS = BaseDigitalSensor.I2C_ADDRESS.copy() I2C_ADDRESS.update({ 'Command': (0x40, 'B'), 'Indicator': (0x41, 'B'), 'Steering': (0x42, 'b'), 'SteeringPID': (0x43, 'b'), 'Binary': (0x44, 'B'), 'S1': (0x50, 'B'), 'S2': (0x51, 'B'), 'S3': (0x52, 'B'), 'S4': (0x53, 'B'), 'S5': (0x54, 'B'), 'S6': (0x55, 'B'), 'S7': (0x56, 'B'), 'S8': (0x57, 'B'), 'all_sensors': (0x50, '8B'), }) I2C_DEV = 0x54 class Commands: NORMAL = 0x00 INDICATOR = 0x01 CALIBRATE_BLACK = 0x02 CALIBRATE_WHITE = 0x03 CALIBRATE = 0x04 RESET_CALIBRATION = 0x05 RAW = 0x06 PID_CONFIG = 0x07 class CommandsMods: COMPENSATION_ON = 0x80 PROFILE_INSIDE = 0x40 INVERSE = 0x20 def __init__(self, brick, port, check_compatible=False): super(Line, self).__init__(brick, port, check_compatible) def set_mode(self, mode): self.write_value('Command', (mode, )) def set_indicator(self, value): self.set_mode(self.Commands.INDICATOR) self.write_value('Indicator', (value & 0xff, )) def get_binary_value(self): self.set_mode(self.Commands.NORMAL) return self.read_value('Binary')[0] def get_scaled_value(self): self.set_mode(self.Commands.NORMAL) return self.read_value('Steering')[0] get_sample = get_scaled_value Line.add_compatible_sensor(None, 'SMBRCKS', 'LINE')
Полученный модуль нужно положить в каталог, куда установлен модуль nxt-python -
Python27\Lib\site-packages\nxt\sensor
(для Windows). В этом же каталоге находится файл __init__.py
, в котором нужно добавить строки, для более удобного доступа к датчикам:import smartbricks SBLine = smartbricks.Line
Теперь, из Python-скриптов с датчиком SmartBricks, в данном случае с датчиком линии, можно будет работать следующим образом:
from nxt.locator import find_one_brick from nxt.sensor import * from time import sleep br=find_one_brick(debug=True) sbline = SBLine(br, PORT_1) for i in (1,2,4,8,16,32,64,128): print sbline.set_indicator(i) sleep(0.5) i = 0 while(i<10): print bin(sbline.get_binary_value()) i = i+1 sleep(1) i = 0 while(i<10): print sbline.get_sample() i = i+1 sleep(1)
Комментариев нет:
Отправить комментарий