Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Методическое пособие 539

.pdf
Скачиваний:
8
Добавлен:
30.04.2022
Размер:
2.16 Mб
Скачать

delay(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