Задача
Записать байт во внешнюю память и прочитать его обратно.
Микроконтроллер: ATmega8, кварц 4 МГц. Среда: Atmel Studio / avra (или avra / Atmel Studio). Как настроить среду и прошить контроллер — в статье программирование AVR.
Что понадобится
- ATmega8
- кварц 8 МГц + 2×22 пФ
- микросхема 24C02
- два резистора 4,7 кОм (подтяжки шины)
- 8 светодиодов для индикации
Схема подключения
| Вывод | Подключение |
|---|---|
| PC4 / SDA | линия данных, подтяжка 4,7 кОм на +5 В |
| PC5 / SCL | линия тактирования, подтяжка 4,7 кОм |
| A0…A2 микросхемы | на GND — базовый адрес 0xA0 |
| WP | на GND, иначе запись заблокирована |
Алгоритм
- Задать скорость шины через регистр TWBR.
- Сформировать условие START и передать адрес микросхемы с признаком записи.
- Отправить адрес ячейки и данные, завершить обмен условием STOP.
- Для чтения повторить старт с признаком чтения и принять байт без подтверждения.
Листинг на ассемблере
Код проверен ассемблером и собирается без ошибок. Скачать: a34.asm · a34.hex (готовая прошивка).
;=======================================================
; Проект 34. Внешняя EEPROM 24C02 по шине I2C (TWI)
; ATmega8, 8 МГц. SDA — PC4, SCL — PC5, подтяжки 4,7 кОм
; Записываем байт в ячейку 0x10 и читаем его обратно
;=======================================================
.include "m8def.inc"
.def tmp = r16
.def data = r17
.equ EE_W = 0xA0 ; адрес 24C02 для записи
.equ EE_R = 0xA1 ; адрес для чтения
.cseg
.org 0x0000
rjmp RESET
RESET:
ldi tmp, low(RAMEND)
out SPL, tmp
ldi tmp, high(RAMEND)
out SPH, tmp
ldi tmp, 0xFF
out DDRB, tmp ; индикация на светодиодах
ldi tmp, 32 ; TWBR: ~100 кГц при 8 МГц
out TWBR, tmp
;--- запись значения 0x5A в ячейку 0x10 ---
rcall TwiStart
ldi data, EE_W
rcall TwiWrite
ldi data, 0x10 ; адрес ячейки
rcall TwiWrite
ldi data, 0x5A ; данные
rcall TwiWrite
rcall TwiStop
rcall Delay10ms ; пауза на цикл записи EEPROM
;--- чтение той же ячейки ---
rcall TwiStart
ldi data, EE_W
rcall TwiWrite
ldi data, 0x10
rcall TwiWrite
rcall TwiStart ; повторный старт
ldi data, EE_R
rcall TwiWrite
rcall TwiReadNack
rcall TwiStop
out PORTB, data ; показываем прочитанное
MAIN:
rjmp MAIN
TwiStart:
ldi tmp, (1<<TWINT)|(1<<TWSTA)|(1<<TWEN)
out TWCR, tmp
TS_W:
in tmp, TWCR
sbrs tmp, TWINT ; ждём завершения операции
rjmp TS_W
ret
TwiWrite:
out TWDR, data
ldi tmp, (1<<TWINT)|(1<<TWEN)
out TWCR, tmp
TW_W:
in tmp, TWCR
sbrs tmp, TWINT
rjmp TW_W
ret
TwiReadNack:
ldi tmp, (1<<TWINT)|(1<<TWEN) ; без ACK — последний байт
out TWCR, tmp
TR_W:
in tmp, TWCR
sbrs tmp, TWINT
rjmp TR_W
in data, TWDR
ret
TwiStop:
ldi tmp, (1<<TWINT)|(1<<TWSTO)|(1<<TWEN)
out TWCR, tmp
ret
Delay10ms:
ldi r18, 130
E1: ldi r19, 255
E2: dec r19
brne E2
dec r18
brne E1
ret
Пояснения
- Каждая операция TWI завершается установкой флага TWINT — ждать его обязательно, иначе следующая команда собьёт обмен.
- Повторный старт нужен, чтобы сменить направление обмена, не отпуская шину: иначе другой ведущий успел бы вклиниться.
- После записи микросхеме нужно несколько миллисекунд на внутренний цикл — обращение раньше времени вернёт ошибку.
Что дальше
Разобравшись с этим проектом, переходите к следующему в разделе проекты на PIC. Справочник по архитектуре и периферии контроллеров — на странице микроконтроллеры AVR.