Программирование LEGO NXT роботов на языке NXC - Дополнительные возможности — различия между версиями

Материал из roboforum.ru Wiki
Перейти к: навигация, поиск
(Файловая система)
(Дисплей)
 
(не показано 15 промежуточных версий 1 участника)
Строка 2: Строка 2:
  
 
=Дополнительные возможности=
 
=Дополнительные возможности=
NXC has a number of additional commands. In this chapter we will discuss three types: the use of the timer,
+
NXC имеет несколько сервисных команд. В этой главе мы обсудим три типа таких команд: для работы с таймером, дисплеем и для использования файловой системы NXT.
commands to control the display, and the use of NXT file system.
 
  
 
==Таймеры==
 
==Таймеры==
The NXT has a timer that runs continuously. This timer ticks in increments of 1/1000 of a second. You can get
+
В модуле NXT есть таймер, который работает в постоянном режиме. Этот таймер увеличивается на единицу каждые 1/1000 секунды. Вы можете получить текущее значение таймера функцией CurrentTick(). Вот пример использования таймера, в котором робот будет двигаться случайным образом 10 секунд.
the current value of the timer with CurrentTick(). Here is an example of the use of a timer. The following
+
 
program lets the robot drive sort of random for 10 seconds.
+
task main()
task main()
+
{
{
+
  long t0, time;
long t0, time;
+
  t0 = CurrentTick();
t0 = CurrentTick();
+
  do
do
+
  {
{
+
    time = CurrentTick()-t0;
time = CurrentTick()-t0;
+
    OnFwd(OUT_AC, 75);
OnFwd(OUT_AC, 75);
+
    Wait(Random(1000));
Wait(Random(1000));
+
    OnRev(OUT_C, 75);
OnRev(OUT_C, 75);
+
    Wait(Random(1000));
Wait(Random(1000));
+
  }
}
+
  while (time<10000);
while (time<10000);
+
  Off(OUT_AC);
Off(OUT_AC);
+
}
}
+
 
You might want to compare this program with the one given in Chapter IV that did exactly the same task. The
+
Можно попробовать сравнить эту программу с приведенной в главе 4, делающей то же самое. На таймере это делается определенно проще.
one with timers is definitely simpler.
+
 
Timers are very useful as a replacement for a Wait() command. You can sleep for a particular amount of time
+
Таймеры очень полезны в качестве заменителя команд Wait(). Вы можете ожидать нужного момента времени сбросив таймер и ожидая нужного его значения. Но вы так же можете в это время реагировать на происходящие события (например на поступление информации от сенсоров), пока ждете. Следующая простая программа является примером такого использования таймеров. Она двигает робота вперед либо пока не пройдёт 10 секунд, либо пока у него не сработает бампер, упершись во что-нибудь.
by resetting a timer and then waiting till it reaches a particular value. But you can also react on other events (e.g.
+
 
from sensors) while waiting. The following simple program is an example of this. It lets the robot drive until
+
task main()
either 10 seconds are past, or the touch sensor touches something.
+
{
task main()
+
  long t3;
{
+
  SetSensor(IN_1,SENSOR_TOUCH);
long t3;
+
  t3 = CurrentTick();
SetSensor(IN_1,SENSOR_TOUCH);
+
  OnFwd(OUT_AC, 75);
t3 = CurrentTick();
+
  until ((SENSOR_1 == 1) || ((CurrentTick()-t3) > 10000));
OnFwd(OUT_AC, 75);
+
  Off(OUT_AC);
until ((SENSOR_1 == 1) || ((CurrentTick()-t3) > 10000));
+
}
Off(OUT_AC);
+
 
}
+
Не забывайте, что таймер работает с шагом 1/1000 секунды, как и команда wait.
Don't forget that timers work in ticks of 1/1000 of a second just like the wait command.
 
  
 
==Дисплей==
 
==Дисплей==
NXT brick features a black and white dot matrix display with a resolution of 100x64 pixels. There are many API
+
Модуль NXT имеет черно-белый графический дисплей с разрешением 100х64 пиксела. К нему есть множество функций API для рисования на нём текста, чисел, точек, линий, прямоугольников, кругов и даже картинок (из .ric-файлов). Следующий пример пытается покрыть все эти случаи. Пиксель (0,0) находится внизу слева экранчика.
functions to draw text strings, numbers, dots, lines, rectangles, circles, and even bitmap images (.ric files). The
 
next example tries to cover all these cases. Pixel numbered (0,0) is the bottom left one.
 
  
#define X_MAX 99
+
#define X_MAX 99
#define Y_MAX 63
+
#define Y_MAX 63
#define X_MID (X_MAX+1)/2
+
#define X_MID (X_MAX+1)/2
#define Y_MID (Y_MAX+1)/2
+
#define Y_MID (Y_MAX+1)/2
task main(){
+
int i = 1234;
+
task main(){
TextOut(15,LCD_LINE1,"Display", true);
+
  int i = 1234;
NumOut(60,LCD_LINE1, i);
+
  TextOut(15,LCD_LINE1,"Display", true);
PointOut(1,Y_MAX-1);
+
  NumOut(60,LCD_LINE1, i);
PointOut(X_MAX-1,Y_MAX-1);
+
  PointOut(1,Y_MAX-1);
PointOut(1,1);
+
  PointOut(X_MAX-1,Y_MAX-1);
PointOut(X_MAX-1,1);
+
  PointOut(1,1);
Wait(200);
+
  PointOut(X_MAX-1,1);
RectOut(5,5,90,50);
+
  Wait(200);
Wait(200);
+
  RectOut(5,5,90,50);
LineOut(5,5,95,55);
+
  Wait(200);
Wait(200);
+
  LineOut(5,5,95,55);
LineOut(5,55,95,5);
+
  Wait(200);
Wait(200);
+
  LineOut(5,55,95,5);
CircleOut(X_MID,Y_MID-2,20);
+
  Wait(200);
Wait(800);
+
  CircleOut(X_MID,Y_MID-2,20);
ClearScreen();
+
  Wait(800);
GraphicOut(30,10,"faceclosed.ric"); Wait(500);
+
  ClearScreen();
ClearScreen();
+
  GraphicOut(30,10,"faceclosed.ric"); Wait(500);
GraphicOut(30,10,"faceopen.ric");
+
  ClearScreen();
Wait(1000);
+
  GraphicOut(30,10,"faceopen.ric");
}
+
  Wait(1000);
All these functions are quite self-explanatory, but now I'll describe their parameters in detail.
+
}
ClearScreen() clears the screen;
+
 
NumOut(x, y, number) lets you specify coordinates, and number;
+
Все эти функции достаточно очевидны, но я всё-таки опишу их параметры детально:
TextOut(x, y, string) works as above, but outputs a text string
+
* ClearScreen() очищает экран;
GraphicOut(x, y, filename) shows a bitmap .ric file
+
* NumOut(x, y, number) выводит число в указанные координаты;
CircleOut(x, y, radius) outputs a circle specified by the coordinates of the center and radius;
+
* TextOut(x, y, string) тоже самое, только выводит строку
LineOut(x1, y1, x2, y2) draws a line that goes from point (x1,x2) to (x2,y2)
+
* GraphicOut(x, y, filename) выводит картинку из .ric-файла
PointOut(x, y) puts a dot on the screen
+
* CircleOut(x, y, radius) выводит окружность с заданными координатами центра и радиусом;
RectOut(x, y, width, height) draws a rectangle with the bottom left vertex in (x,y) and with the
+
* LineOut(x1, y1, x2, y2) рисует линию из (x1,x2) в (x2,y2)
dimensions specified;
+
* PointOut(x, y) ставит точку
ResetScreen() resets the screen.
+
* RectOut(x, y, width, height) рисует прямоугольник с нижним левым углом в (x,y) и указанных размеров;
 +
* ResetScreen() сбрасывает экран.
  
 
==Файловая система==
 
==Файловая система==
The NXT can write and read files, stored inside its flash memory. So you could save a datalog from sensor data
+
Модуль NXT может читать и писать файлы, имеющиеся у него в флеш-памяти. Так что вы можете создать лог-файл с датчиков или читать числа во время выполнения программы. Единственное ограничение в количестве и размере файлов - это размер флеш-памяти. Для работы с файлами используются функции API, которые позволяют создавать, переименовывать, удалять и искать файлы, а также с конкретным файлом вы можете читать и писать в него текстовые строки, числа и отдельные байты.
or read numbers during program execution. The only limit in files number and dimension is the size of the flash
+
В следующем примере мы посмотрим, как создать файл, записать в него несколько строк и переименовать этот файл.
memory. NXT API functions let you manage files (create, rename, delete, find) , let you read and write text
+
 
strings, numbers and single bytes.
+
Сначала, программа удаляет файлы с именами, которые мы хотим использовать: вообще это плохая практика (приличнее будет сначала проверить существование файла, вручную удалить его, или выбрать другое имя для нашего рабочего файла), но в нашей простой ситуации мы можем не обращать внимание на это. Дальше программа создает наш файл функцией CreateFile("Danny.txt", 512, fileHandle), указывая имя, размер и переменную, куда NXT поместит свой внутренний идентификатор открытого файла.
In the next example, we will see how to create a file, write strings into it and rename it.
+
 
 +
После чего программа готовит строки и пишет их в файл с возвратом каретки в конце функцией WriteLnString(fileHandle,string,
 +
bytesWritten), в которой все параметры должны быть переменными. И в конце, файл закрывается и переименовывается.
 +
 
 +
'''Помните:''' файл должен быть явно закрыт перед тем как начать следующую операцию, так что если вы создали файл, вы можете писать в него, если вы хотите прочитать из него - надо его закрыть и открыть на чтение командой OpenFileRead(). И перед удалением/переименованием файла он тоже должен быть закрыт.
 +
 
 +
#define OK LDR_SUCCESS
 +
 +
task main(){
 +
  byte fileHandle;
 +
  short fileSize;
 +
  short bytesWritten;
 +
  string read;
 +
  string write;
 +
  DeleteFile("Danny.txt");
 +
  DeleteFile("DannySays.txt");
 +
  CreateFile("Danny.txt", 512, fileHandle);
 +
  for(int i=2; i<=10; i++ ){
 +
    write = "NXT is cool ";
 +
    string tmp = NumToStr(i);
 +
    write = StrCat(write,tmp," times!");
 +
    WriteLnString(fileHandle,write, bytesWritten);
 +
  }
 +
  CloseFile(fileHandle);
 +
  RenameFile("Danny.txt","DannySays.txt");
 +
}
 +
 
 +
Чтобы увидеть, что получилось, зайдите через меню "BricxCC => Tools => NXT Explorer", в просмотрщик NXT-флеш-памяти,загрузите файл DannySays.txt на ПК и посмотрите, что внутри.
 +
 
 +
Посмотрите на следующий пример! Мы создадим таблицу ASCII-символов.
 +
 
 +
task main(){
 +
  byte handle;
 +
  if (CreateFile("ASCII.txt", 2048, handle) == NO_ERR) {
 +
    for (int i=0; i < 256; i++) {
 +
      string s = NumToStr(i);
 +
      int slen = StrLen(s);
 +
      WriteBytes(handle, s, slen);
 +
      WriteLn(handle, i);
 +
    }
 +
    CloseFile(handle);
 +
  }
 +
}
 +
 
 +
Правда просто? Эта программа создает файл и если при этом не произошла ошибка - пишет в него числа от 0 до 255 (конвертируя их предварительно в соответствующие символы функцией WriteBytes(handle, s, slen), которая предоставляет путь писать в файл строки без возврата каретки); после чего пишет это же число функцией WriteLn(handle, value), которая уже добавляет в конец записанной строки возврат каретки.
 +
 
 +
Результат, который вы видите при открытии файла ASCII.txt текстовым редактором (например Блокнотом из Windows), содержит в каждой строке символ и его код записанный в десятичном виде.
 +
 
 +
Остались еще две важные функции, которые вы должны знать: ReadLnString для чтения строк из файлов и ReadLn для чтения чисел.
 +
 
 +
Теперь для примера использования первой функции мы создадим программу, в которой из главной задачи будет вызвана функция CreateRandomFile, создающая файл со случайными числами внутри (записанными в строковом представлении); вы можете закомментировать эту строку и использовать любой другой текстовый файл для этого примера.
 +
 
 +
После этого главная задача откроет этот файл на чтение, и до конца файла будет читать строку из файла, вызывая ReadLnString
 +
и показывать прочитанный текст.
 +
 
 +
В функции CreateRandomFile мы генерируем заданное количество случайных чисел, преобразуем их в строки и пишем их в файл.
 +
 
 +
Функция ReadLnString в качестве аргументов имеет внутренний идентификатор открытого файла и строковую переменную, в которую после вызова функции будет прочитанная строка, а функция вернет код ошибки, который мы используем для того, чтобы понять, что достигли конца файла.
 +
 
 +
#define FILE_LINES 10
 +
 +
sub CreateRandomFile(string fname, int lines){
 +
  byte handle;
 +
  string s;
 +
  int bytesWritten;
 +
  DeleteFile(fname);
 +
  int fsize = lines*5;
 +
  //create file with random data
 +
  if(CreateFile(fname, fsize, handle) == NO_ERR) {
 +
    int n;
 +
    repeat(FILE_LINES) {
 +
      int n = Random(0xFF);
 +
      s = NumToStr(n);
 +
      WriteLnString(handle,s,bytesWritten);
 +
    }
 +
    CloseFile(handle);
 +
  }
 +
}
 +
 +
task main(){
 +
  byte handle;
 +
  int fsize;
 +
  string buf;
 +
  bool eof = false;
 +
  CreateRandomFile("rand.txt",FILE_LINES);
 +
  if(OpenFileRead("rand.txt", fsize, handle) == NO_ERR) {
 +
    TextOut(10,LCD_LINE2,"Filesize:");
 +
    NumOut(65,LCD_LINE2,fsize);
 +
    Wait(600);
 +
    until (eof == true){ // read the text file till the end
 +
      if(ReadLnString(handle,buf) != NO_ERR) eof = true;
 +
      ClearScreen();
 +
      TextOut(20,LCD_LINE3,buf);
 +
      Wait(500);
 +
    }
 +
  }
 +
  CloseFile(handle);
 +
}
 +
 
 +
В последней программе я покажу вам, как читать числа из файла. Кроме того я покажу вам пример условной компиляции. В начале кода мы определим значение, которое не будет использоваться ни как макрос, ни как псевдоним, мы просто определим INT.
 +
 
 +
Вот директива препроцессора
 +
 
 +
#ifdef INT
 +
…Code…
 +
#endif
 +
 
 +
которая сообщает компилятору, что компилировать код внутри "#ifdef" нужно только если предварительно было определено значение INT.
 +
Так что если мы в начале программы объявим INT, скомпилируется задача "main" из первой половины кода, а если же мы определим LONG вместо INT, тогда будет скомпилирована вторая версия задачи "main".
 +
 
 +
Этот метод позволил мне в одной программе показать вам, как работать одной и той же функцией ReadLn(handle,val) с обоими типами чисел - int (16 бит) и long (32 бита).
 +
 
 +
Как и предыдущая, эта команда принимает в качестве аргументов идентификатор открытого файла и числовую переменную, возвращая код ошибки.
  
First, program deletes files with the names we're going to use: it is not a good habit (we should check for file
+
Функция прочитает 2 байта из файла, если переданная переменная типа int, и 4 байта, если переменная типа long. Таким же образом вообще говоря можно читать и писать переменные типа bool.
existence, manually delete it or choose automatically another name for our work file), but there's no problem in
 
our simple case. It creates our file by CreateFile("Danny.txt", 512, fileHandle), specifying name,
 
size and a handle to the file, where NXT firmware will write a number for its own uses.
 
Then it builds strings and write to file with carriage return with WriteLnString(fileHandle,string,
 
bytesWritten), where all the parameters must be variables. Finally, the file is closed and renamed.
 
Remember: a file must be closed before beginning another operation, so if you created a file you can write to it;
 
if you want to read from it, you must close it and open it with OpenFileRead(); to delete/rename it, you
 
must close it.
 
#define OK LDR_SUCCESS
 
task main(){
 
byte fileHandle;
 
short fileSize;
 
short bytesWritten;
 
string read;
 
string write;
 
DeleteFile("Danny.txt");
 
DeleteFile("DannySays.txt");
 
CreateFile("Danny.txt", 512, fileHandle);
 
for(int i=2; i<=10; i++ ){
 
write = "NXT is cool ";
 
string tmp = NumToStr(i);
 
write = StrCat(write,tmp," times!");
 
WriteLnString(fileHandle,write, bytesWritten);
 
}
 
CloseFile(fileHandle);
 
RenameFile("Danny.txt","DannySays.txt");
 
}
 
To see the result, go to BricxCCToolsNXT Explorer, upload DannySays.txt to pc and take a look.
 
Ready for the next example! We will create a table with ASCII characters.
 
task main(){
 
byte handle;
 
if (CreateFile("ASCII.txt", 2048, handle) == NO_ERR) {
 
for (int i=0; i < 256; i++) {
 
string s = NumToStr(i);
 
int slen = StrLen(s);
 
WriteBytes(handle, s, slen);
 
WriteLn(handle, i);
 
}
 
CloseFile(handle);
 
}
 
}
 
Really simple, this program creates the file and if no error occurred, it writes a number from 0 to 255 (converting
 
it to string before) with WriteBytes(handle, s, slen), that is another way to write strings without
 
carriage return; then it writes the number as is with WriteLn(handle, value) that appends a carriage return.
 
The result, that you can see as before opening ASCII.txt with a text editor (as Windows Notepad), is so
 
explainable: the number written as string is shown in a human-readable way, while the number written as hex
 
value is interpreted and shown as an ASCII code.
 
Two important functions remain to be showed: ReadLnString to read strings from files and ReadLn to read
 
numbers.
 
  
Now for the example for the first one: the main task calls CreateRandomFile subroutine that creates a file
+
#define INT // INT or LONG
with random numbers in it (written as strings); you can comment this line and use another hand-created text file
+
#ifdef INT
for this example.
+
Then the main task opens this file for reading, reads it a line at once until the end of file, calling ReadLnString
+
task main () {
function and displays text.
+
  byte handle, time = 0;
In the CreateRandomFile subroutine we generate a predefined quantity of random numbers, convert them to
+
  int n, fsize,len, i;
string and write them to the file.
+
  int in;
The ReadLnString accepts a file handle and a string variable as arguments: after the call, the string will
+
  DeleteFile("int.txt");
contain a text line and the function will return an error code, that we can use to know if the end of file has been
+
  CreateFile("int.txt",4096,handle);
reached.
+
  for (int i = 1000; i<=10000; i+=1000){
#define FILE_LINES 10
+
    WriteLn(handle,i);
sub CreateRandomFile(string fname, int lines){
+
  }
byte handle;
+
  CloseFile(handle);
string s;
+
  OpenFileRead("int.txt",fsize,handle);
int bytesWritten;
+
  until (ReadLn(handle,in)!=NO_ERR){
DeleteFile(fname);
+
    ClearScreen();
int fsize = lines*5;
+
    NumOut(30,LCD_LINE5,in);
//create file with random data
+
    Wait(500);
if(CreateFile(fname, fsize, handle) == NO_ERR) {
+
  }
int n;
+
  CloseFile(handle);
repeat(FILE_LINES) {
+
}
int n = Random(0xFF);
+
s = NumToStr(n);
+
#endif
WriteLnString(handle,s,bytesWritten);
+
#ifdef LONG
}
+
CloseFile(handle);
+
task main () {
}
+
  byte handle, time = 0;
}
+
  int n, fsize,len, i;
task main(){
+
  long in;
byte handle;
+
  DeleteFile("long.txt");
int fsize;
+
  CreateFile("long.txt",4096,handle);
string buf;
+
  for (long i = 100000; i<=1000000; i+=50000){
bool eof = false;
+
    WriteLn(handle,i);
CreateRandomFile("rand.txt",FILE_LINES);
+
  }
if(OpenFileRead("rand.txt", fsize, handle) == NO_ERR) {
+
  CloseFile(handle);
TextOut(10,LCD_LINE2,"Filesize:");
+
  OpenFileRead("long.txt",fsize,handle);
NumOut(65,LCD_LINE2,fsize);
+
  until (ReadLn(handle,in)!=NO_ERR){
Wait(600);
+
    ClearScreen();
until (eof == true){ // read the text file till the end
+
    NumOut(30,LCD_LINE5,in);
if(ReadLnString(handle,buf) != NO_ERR) eof = true;
+
    Wait(500);
ClearScreen();
+
  }
TextOut(20,LCD_LINE3,buf);
+
  CloseFile(handle);
Wait(500);
+
}
}
+
}
+
#endif
CloseFile(handle);
 
}
 
In the last program, I'll show you how to read numbers from a file.
 
I take the occasion to give you a little sample of conditional compilation. At the beginning of the code, there is a
 
definition that is not used for a macro neither for an alias: we simply define INT.
 
Then there is a preprocessor statement
 
#ifdef INT
 
…Code…
 
#endif
 
  
that simply tells the compiler to compile the code between the two statements if INT as been previously defined.
 
So, if we define INT, the task main inside the first couplet will be compiled and if LONG is defined instead of
 
INT, the second version of main will be compiled.
 
This method allows me to show in a single program how both int (16 bit) and long (32 bit) types can be read
 
from file calling the same function ReadLn(handle,val).
 
As before, it accepts a file handle and a numeric variable as arguments, returning an error code.
 
The function will read 2 bytes from file if the passed variable is declared as int, and will read 4 bytes if the
 
variable is long. Also bool variables can be written and read the same way.
 
#define INT // INT or LONG
 
#ifdef INT
 
task main () {
 
byte handle, time = 0;
 
int n, fsize,len, i;
 
int in;
 
DeleteFile("int.txt");
 
CreateFile("int.txt",4096,handle);
 
for (int i = 1000; i<=10000; i+=1000){
 
WriteLn(handle,i);
 
}
 
CloseFile(handle);
 
OpenFileRead("int.txt",fsize,handle);
 
until (ReadLn(handle,in)!=NO_ERR){
 
ClearScreen();
 
NumOut(30,LCD_LINE5,in);
 
Wait(500);
 
}
 
CloseFile(handle);
 
}
 
#endif
 
#ifdef LONG
 
task main () {
 
byte handle, time = 0;
 
int n, fsize,len, i;
 
long in;
 
DeleteFile("long.txt");
 
CreateFile("long.txt",4096,handle);
 
for (long i = 100000; i<=1000000; i+=50000){
 
WriteLn(handle,i);
 
}
 
CloseFile(handle);
 
OpenFileRead("long.txt",fsize,handle);
 
until (ReadLn(handle,in)!=NO_ERR){
 
ClearScreen();
 
NumOut(30,LCD_LINE5,in);
 
Wait(500);
 
}
 
CloseFile(handle);
 
}
 
#endif
 
 
==Подводим итоги==
 
==Подводим итоги==
In this last chapter you met the advanced features offered by NXT: high resolution timer, dot matrix display and
+
В этой последней главе мы познакомились с дополнительными функциями модуля NXT: таймером высокого разрешения, графическим дисплеем и файловой системой.
filesystem.
 

Текущая версия на 20:17, 29 сентября 2010

Автор: Daniele Benedettelli

Перевод: © Ботов Антон aka =DeaD=, 2009

Эксклюзивно для www.roboforum.ru
копирование на другие ресурсы и публикация перевода
без разрешения его автора запрещены

Дополнительные возможности

NXC имеет несколько сервисных команд. В этой главе мы обсудим три типа таких команд: для работы с таймером, дисплеем и для использования файловой системы NXT.

Таймеры

В модуле NXT есть таймер, который работает в постоянном режиме. Этот таймер увеличивается на единицу каждые 1/1000 секунды. Вы можете получить текущее значение таймера функцией CurrentTick(). Вот пример использования таймера, в котором робот будет двигаться случайным образом 10 секунд.

task main()
{
  long t0, time;
  t0 = CurrentTick();
  do
  {
    time = CurrentTick()-t0;
    OnFwd(OUT_AC, 75);
    Wait(Random(1000));
    OnRev(OUT_C, 75);
    Wait(Random(1000));
  }
  while (time<10000);
  Off(OUT_AC);
}

Можно попробовать сравнить эту программу с приведенной в главе 4, делающей то же самое. На таймере это делается определенно проще.

Таймеры очень полезны в качестве заменителя команд Wait(). Вы можете ожидать нужного момента времени сбросив таймер и ожидая нужного его значения. Но вы так же можете в это время реагировать на происходящие события (например на поступление информации от сенсоров), пока ждете. Следующая простая программа является примером такого использования таймеров. Она двигает робота вперед либо пока не пройдёт 10 секунд, либо пока у него не сработает бампер, упершись во что-нибудь.

task main()
{
  long t3;
  SetSensor(IN_1,SENSOR_TOUCH);
  t3 = CurrentTick();
  OnFwd(OUT_AC, 75);
  until ((SENSOR_1 == 1) || ((CurrentTick()-t3) > 10000));
  Off(OUT_AC);
}

Не забывайте, что таймер работает с шагом 1/1000 секунды, как и команда wait.

Дисплей

Модуль NXT имеет черно-белый графический дисплей с разрешением 100х64 пиксела. К нему есть множество функций API для рисования на нём текста, чисел, точек, линий, прямоугольников, кругов и даже картинок (из .ric-файлов). Следующий пример пытается покрыть все эти случаи. Пиксель (0,0) находится внизу слева экранчика.

#define X_MAX 99
#define Y_MAX 63
#define X_MID (X_MAX+1)/2
#define Y_MID (Y_MAX+1)/2

task main(){
  int i = 1234;
  TextOut(15,LCD_LINE1,"Display", true);
  NumOut(60,LCD_LINE1, i);
  PointOut(1,Y_MAX-1);
  PointOut(X_MAX-1,Y_MAX-1);
  PointOut(1,1);
  PointOut(X_MAX-1,1);
  Wait(200);
  RectOut(5,5,90,50);
  Wait(200);
  LineOut(5,5,95,55);
  Wait(200);
  LineOut(5,55,95,5);
  Wait(200);
  CircleOut(X_MID,Y_MID-2,20);
  Wait(800);
  ClearScreen();
  GraphicOut(30,10,"faceclosed.ric"); Wait(500);
  ClearScreen();
  GraphicOut(30,10,"faceopen.ric");
  Wait(1000);
}

Все эти функции достаточно очевидны, но я всё-таки опишу их параметры детально:

  • ClearScreen() очищает экран;
  • NumOut(x, y, number) выводит число в указанные координаты;
  • TextOut(x, y, string) тоже самое, только выводит строку
  • GraphicOut(x, y, filename) выводит картинку из .ric-файла
  • CircleOut(x, y, radius) выводит окружность с заданными координатами центра и радиусом;
  • LineOut(x1, y1, x2, y2) рисует линию из (x1,x2) в (x2,y2)
  • PointOut(x, y) ставит точку
  • RectOut(x, y, width, height) рисует прямоугольник с нижним левым углом в (x,y) и указанных размеров;
  • ResetScreen() сбрасывает экран.

Файловая система

Модуль NXT может читать и писать файлы, имеющиеся у него в флеш-памяти. Так что вы можете создать лог-файл с датчиков или читать числа во время выполнения программы. Единственное ограничение в количестве и размере файлов - это размер флеш-памяти. Для работы с файлами используются функции API, которые позволяют создавать, переименовывать, удалять и искать файлы, а также с конкретным файлом вы можете читать и писать в него текстовые строки, числа и отдельные байты. В следующем примере мы посмотрим, как создать файл, записать в него несколько строк и переименовать этот файл.

Сначала, программа удаляет файлы с именами, которые мы хотим использовать: вообще это плохая практика (приличнее будет сначала проверить существование файла, вручную удалить его, или выбрать другое имя для нашего рабочего файла), но в нашей простой ситуации мы можем не обращать внимание на это. Дальше программа создает наш файл функцией CreateFile("Danny.txt", 512, fileHandle), указывая имя, размер и переменную, куда NXT поместит свой внутренний идентификатор открытого файла.

После чего программа готовит строки и пишет их в файл с возвратом каретки в конце функцией WriteLnString(fileHandle,string, bytesWritten), в которой все параметры должны быть переменными. И в конце, файл закрывается и переименовывается.

Помните: файл должен быть явно закрыт перед тем как начать следующую операцию, так что если вы создали файл, вы можете писать в него, если вы хотите прочитать из него - надо его закрыть и открыть на чтение командой OpenFileRead(). И перед удалением/переименованием файла он тоже должен быть закрыт.

#define OK LDR_SUCCESS

task main(){
  byte fileHandle;
  short fileSize;
  short bytesWritten;
  string read;
  string write;
  DeleteFile("Danny.txt");
  DeleteFile("DannySays.txt");
  CreateFile("Danny.txt", 512, fileHandle);
  for(int i=2; i<=10; i++ ){
    write = "NXT is cool ";
    string tmp = NumToStr(i);
    write = StrCat(write,tmp," times!");
    WriteLnString(fileHandle,write, bytesWritten);
  }
  CloseFile(fileHandle);
  RenameFile("Danny.txt","DannySays.txt");
}

Чтобы увидеть, что получилось, зайдите через меню "BricxCC => Tools => NXT Explorer", в просмотрщик NXT-флеш-памяти,загрузите файл DannySays.txt на ПК и посмотрите, что внутри.

Посмотрите на следующий пример! Мы создадим таблицу ASCII-символов.

task main(){
  byte handle;
  if (CreateFile("ASCII.txt", 2048, handle) == NO_ERR) {
    for (int i=0; i < 256; i++) {
      string s = NumToStr(i);
      int slen = StrLen(s);
      WriteBytes(handle, s, slen);
      WriteLn(handle, i);
    }
    CloseFile(handle);
  }
}

Правда просто? Эта программа создает файл и если при этом не произошла ошибка - пишет в него числа от 0 до 255 (конвертируя их предварительно в соответствующие символы функцией WriteBytes(handle, s, slen), которая предоставляет путь писать в файл строки без возврата каретки); после чего пишет это же число функцией WriteLn(handle, value), которая уже добавляет в конец записанной строки возврат каретки.

Результат, который вы видите при открытии файла ASCII.txt текстовым редактором (например Блокнотом из Windows), содержит в каждой строке символ и его код записанный в десятичном виде.

Остались еще две важные функции, которые вы должны знать: ReadLnString для чтения строк из файлов и ReadLn для чтения чисел.

Теперь для примера использования первой функции мы создадим программу, в которой из главной задачи будет вызвана функция CreateRandomFile, создающая файл со случайными числами внутри (записанными в строковом представлении); вы можете закомментировать эту строку и использовать любой другой текстовый файл для этого примера.

После этого главная задача откроет этот файл на чтение, и до конца файла будет читать строку из файла, вызывая ReadLnString и показывать прочитанный текст.

В функции CreateRandomFile мы генерируем заданное количество случайных чисел, преобразуем их в строки и пишем их в файл.

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

#define FILE_LINES 10

sub CreateRandomFile(string fname, int lines){
  byte handle;
  string s;
  int bytesWritten;
  DeleteFile(fname);
  int fsize = lines*5;
  //create file with random data
  if(CreateFile(fname, fsize, handle) == NO_ERR) {
    int n;
    repeat(FILE_LINES) {
      int n = Random(0xFF);
      s = NumToStr(n);
      WriteLnString(handle,s,bytesWritten);
    }
    CloseFile(handle);
  }
}

task main(){
  byte handle;
  int fsize;
  string buf;
  bool eof = false;
  CreateRandomFile("rand.txt",FILE_LINES);
  if(OpenFileRead("rand.txt", fsize, handle) == NO_ERR) {
    TextOut(10,LCD_LINE2,"Filesize:");
    NumOut(65,LCD_LINE2,fsize);
    Wait(600);
    until (eof == true){ // read the text file till the end
      if(ReadLnString(handle,buf) != NO_ERR) eof = true;
      ClearScreen();
      TextOut(20,LCD_LINE3,buf);
      Wait(500);
    }
  }
  CloseFile(handle);
}

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

Вот директива препроцессора

#ifdef INT
…Code…
#endif

которая сообщает компилятору, что компилировать код внутри "#ifdef" нужно только если предварительно было определено значение INT. Так что если мы в начале программы объявим INT, скомпилируется задача "main" из первой половины кода, а если же мы определим LONG вместо INT, тогда будет скомпилирована вторая версия задачи "main".

Этот метод позволил мне в одной программе показать вам, как работать одной и той же функцией ReadLn(handle,val) с обоими типами чисел - int (16 бит) и long (32 бита).

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

Функция прочитает 2 байта из файла, если переданная переменная типа int, и 4 байта, если переменная типа long. Таким же образом вообще говоря можно читать и писать переменные типа bool.

#define INT // INT or LONG
#ifdef INT

task main () {
  byte handle, time = 0;
  int n, fsize,len, i;
  int in;
  DeleteFile("int.txt");
  CreateFile("int.txt",4096,handle);
  for (int i = 1000; i<=10000; i+=1000){
    WriteLn(handle,i);
  }
  CloseFile(handle);
  OpenFileRead("int.txt",fsize,handle);
  until (ReadLn(handle,in)!=NO_ERR){
    ClearScreen();
    NumOut(30,LCD_LINE5,in);
    Wait(500);
  }
  CloseFile(handle);
}

#endif
#ifdef LONG

task main () {
  byte handle, time = 0;
  int n, fsize,len, i;
  long in;
  DeleteFile("long.txt");
  CreateFile("long.txt",4096,handle);
  for (long i = 100000; i<=1000000; i+=50000){
    WriteLn(handle,i);
  }
  CloseFile(handle);
  OpenFileRead("long.txt",fsize,handle);
  until (ReadLn(handle,in)!=NO_ERR){
    ClearScreen();
    NumOut(30,LCD_LINE5,in);
    Wait(500);
  }
  CloseFile(handle);
}

#endif

Подводим итоги

В этой последней главе мы познакомились с дополнительными функциями модуля NXT: таймером высокого разрешения, графическим дисплеем и файловой системой.