Методическое пособие 539
.pdfdelay(1); CS = 0;
Cmd = READ;
Addr = ADDR; shift_out8(Cmd); shift_out16(Addr); byteRead = shift_in8(); delay(20);
CS = 1;
return byteRead;
}
bit readStatus(void)
{
bit stat;
unsigned char ctmp; CS = 1;
CS = 0;
Cmd = RDSR; shift_out8(Cmd); ctmp = shift_in8(); CS = 1;
stat = ctmp & 0x1; return stat;
}
void writeByte(unsigned char c1)
{
CS = 1; delay(2); CS = 0;
Cmd = WREN; shift_out8(Cmd); CS = 1; delay(2);
CS = 0;
Cmd = WRITE;
81
Addr = ADDR; shift_out8(Cmd); shift_out16(Addr); shift_out8(c1);
CS = 1;
}
void main(void)
{
unsigned char c2 = ‘9’; bit Busy;
P1 |= 0x8; SCON = 0x50; TH1 = 0xFD; TMOD |= 0x20; TR1 = 1;
TI = 1; writeByte(c2); delay(1000);
while ((Busy = readStatus()) == 1); c2 = readByte();
printf(“\nByte received: %c\n”, c2); while(1);
}
При работе с EEPROM нужно очень тщательно просчитывать временные зависимости, особенно при чтении данных из микросхемы памяти, и задавать необходимые задержки между сигналами. В любом случае, перед использованием такой памяти следует изучить все временные зависимости данного чипа и рассчитать требуемые временные задержки.
Анализ исходного текста начнем с переменных и констант, определенных в нашей программе. Группа директив
82
#define WREN 0x6 #define WRITE 0x2 #define READ 0x3 #define RDSR 0x5 #define ADDR 0x2
определяет константы, соответствующие кодам команд EEPROM, которые нам уже знакомы. Константа ADDR выбрана произвольно – она определяет адрес, по которому будет записан байт данных и из которого он позже будет считан.
Директивы sbit определяют биты порта P1, к которым присоединяются соответствующие выводы микросхемы памяти. Соединения соответствуют принципиальной схеме, показанной на рис. 3.8.
Переменная byteRead будет содержать прочитанный из EEPROM байт, в переменную Cmd помещаются коды выполняемых команд, переменная Addr содержит выбранный нами адрес памяти, а переменная cnt является счетчиком записываемых и читаемых битов. Программа содержит несколько функций. В их число входят shift_out8, shift_out16, shift_in8, delay, readByte, readStatus, writeByte, initWR. Функции readByte и writeByte соответственно считывают и записывают байт данных в/из памяти. Функция readStatus выполняет считывание содержимого регистра состояния, а функция initWR устанавливает регистр-защелку перед записью байта данных в память.
Функции shift_out8, shift_out16 и shift_in8 выполняют отдельные операции в циклах чтения/записи. Так, например, функция shift_out8 записывает в EEPROM побитово байт по линии SI, функция shift_out16 делает то же самое для 16разрядного слова (при установке адреса в циклах чтения/записи), а функция shift_in8 принимает байт из памяти по линии SO. Функция delay служит для формирования временных задержек при операциях чтения/записи.
83
Для выполнения циклических сдвигов используются специальные встроенные функции Keil C _crol_ (циклический сдвиг байта на определенное число позиций) и _irol_ (циклический сдвиг 16$разрядного слова на определенное число позиций). Эти функции определены в файле заголовка intrins.h.
В листинге, представленном далее, функции shift_out8, shift_out16, shift_in8, delay написаны на ассемблере, а основная программа на Keil C51. В этом случае перейти от Keil к другому компилятору, например, к тому же SDCC или IAR, не составит труда.
Исходный текст программы записи/чтения байта в/из EEPROM, в котором использованы функции, написанные на языке ассемблера:
#include <stdio.h> #include <REG52.H> #define WREN 0x6 #define WRITE 0x2 #define READ 0x3 #define RDSR 0x5 #define ADDR 0x2
extern void delay(unsigned int i1); extern void shift_out8(unsigned char c1); extern void shift_out16(unsigned int i1); extern unsigned char shift_in8(void); sbit CS = P1^0;
unsigned char byteRead; unsigned char Cmd; unsigned int Addr; unsigned int cnt;
unsigned char readByte(void)
{
CS = 1; delay(1);
84
CS = 0;
Cmd = READ;
Addr = ADDR; shift_out8(Cmd); shift_out16(Addr); byteRead = shift_in8(); delay(20);
CS = 1;
return byteRead;
}
bit readStatus(void)
{
bit stat;
unsigned char ctmp; CS = 1;
CS = 0;
Cmd = RDSR; shift_out8(Cmd); ctmp = shift_in8(); CS = 1;
stat = ctmp & 0x1; return stat;
}
void writeByte(unsigned char c1)
{
CS = 1; delay(2); CS = 0;
Cmd = WREN; shift_out8(Cmd); CS = 1; delay(2);
CS = 0;
Cmd = WRITE;
Addr = ADDR;
85
shift_out8(Cmd); shift_out16(Addr); shift_out8(c1);
CS = 1;
}
void main(void)
{
unsigned char c2 = ‘7’; bit Busy;
P1 |= 0x8; SCON = 0x50; TH1 = 0xFD; TMOD |= 0x20; TR1 = 1;
TI = 1; do {
printf(“Enter character to write to EEPROM:”); c2 = getchar ();
if (c2 == 0x1B) break; writeByte(c2); delay(1000);
while ((Busy = readStatus()) == 1); c2 = readByte();
printf(“\nByte RECEIVED: %c\n“, c2); }while (1);
}
Как видно из исходного текста программы, мы несколько модифицировали программный код. После компиляции и запуска программа запрашивает ввод символа с консоли удаленного терминала, работающего с последовательным портом.
Функции, объявленные с директивой extern, содержатся в отдельном файле с расширением .asm. Вот содержимое этого файла:
86
NAME PROCS
PUBLIC _shift_out8, _shift_out16, shift_in8, _delay SCK EQU P1.1
SI EQU P1.2
SO EQU P1.3
PROG SEGMENT CODE USING 0
RSEG PROG ;——————————————
_shift_out8: MOV A, R7 MOV R4, #8 next_out8: CLR SCK RLC A MOV SI, C SETB SCK
DJNZ R4, next_out8 RET
;—————————————————
_shift_out16: MOV A, R6 MOV R4, #8
next_high_out16: CLR SCK
RLC A MOV SI, C SETB SCK
DJNZ R4, next_high_out16 MOV A, R7
MOV R4, #8 next_low_out16: CLR SCK
RLC A MOV SI, C
87
SETB SCK
DJNZ R4, next_low_out16 RET
;————————————————————
shift_in8: CLR A MOV R4, #8 next_in8: CLR SCK
MOV R6, #250
MOV R7, #150
CALL _delay
MOV C, SO RLC A SETB SCK MOV R6, #5 MOV R7, #5 CALL _delay
DJNZ R4, next_in8 MOV R7, A
RET ;———————————————————
_delay:
DJNZ R6, inner_loop RET
inner_loop: PUSH 7h again: NOP
DJNZ R7, again POP 7h
JMP _delay END
88
Обратите внимание на то, что функция _shift_out8 принимает однобайтовый параметр в регистре R7, а функции _shift_out16 и _delay принимают двухбайтовые параметры в регистрах R6 и R7, причем R6 содержит старший байт, а R7 – младший. Кроме того, в Keil C51 для функций, принимающих параметры, требуется ставить символ подчеркивания в начале имени. При этом имена функций, объявленные в основной программе на C, пишутся без подчеркивания. Функция shift_in8 не принимает никаких параметров от вызывающей программы, поэтому символ подчеркивания в начале имени здесь не нужен. Эта функция возвращает прочитанный байт основной программе в регистре R7. Все функции, вызываемые из другой программы, должны быть объявлены как доступные из внешних модулей, для чего служит директива PUBLIC в начале листинга программы.
Хотелось бы обратить внимание читателей на реализацию временной задержки в функции _delay. Поскольку используется 16-разрядное значение, то задержка организована в виде двух циклов с командами DJNZ. Здесь же используются команды PUSH и POP. Поскольку ассемблер Keil A51 (как и подавляющее большинство других ассемблеров) не использует команды наподобие
PUSH R7
POP R7
то, учитывая, что в программе используется банк 0 регистров общего назначения, имеющих адреса с 0x0 до 0x7, можно применить команды
PUSH 7h
POP 7h
Использование протоколов SPI и I2C хоть и решает в целом ряде случаев вопросы разработки интерфейсов, но, тем не менее, не всегда может быть применимо.
89
ГЛАВА 4. ИНТЕРФЕЙС JTAG
Каждый МК имеет встроенный интерфейс JTAG и логику поддержки граничного сканирования, предназначенные для производственных испытаний и внутрисистемного тестирования, выполнения операций чтения и записи Flashпамяти, а также для проведения «неразрушающей» внутрисхемной отладки. Интерфейс JTAG полностью соответствует спецификации IEEE 1149.1. Эта спецификация содержит подробную информацию об интерфейсе тестирования и архитектуре граничного сканирования.
Для работы с интерфейсом JTAG используются четыре специальных вывода МК: TCK, TMS, TDI и TDO.
Используя 16-разрядный регистр команд интерфейса JTAG (IR), можно подавать любую из восьми команд, показанных на рис. 4.1. Имеется три регистра данных (DR), связанных с работой интерфейса граничного сканирования, и четыре регистра данных, связанных с выполнением операций чтения/записи Flash-памяти МК.
Рис. 4.1. Регистр команд интерфейса JTAG
90