мини-библиотека записи и чтения памяти FRAM FM24
- Войдите на сайт для отправки комментариев
Заказал мне тут один клиент библиотеку чтения и записи чипов памяти FRAM. Но с условием - не использовать Wire.h, чтобы, значит, для компактности.
Написал. А теперь решил выложить сюда, на ваш суд. Клиент не против.
Основные особенности:
- маленькая (около 400 байт кода), для сравнения тоже самое через Wire получается более 2килобайт
- без внутреннего буфера, что означает возможность записи блока данных произвольной длины в один прием, а не порциями по 32 байта, как через Wire
- без прерываний
- возможно изменение частоты шины TWI.
Ошибки отслеживаются в полном обьеме , однако диагностика их минимальна - все функции при ошибке возвращают false. при успехе true.
Библиотека написана только для мк атмега328 и аналогичных. Используются регистры, поэтому на других контроллерах работать не будет. Чипы FRAM поддерживаются от FM24cl02 до FM24cl16, для чипов более 16 мбит алгоритм определения адреса ячейки уже другой.
Код (файл FM24_Mini.h)
/* mini library for reading/writing data from/to FRAM FM24
(c) board707 2022
ver 0.01
*/
#ifndef TWI_FREQ
#define TWI_FREQ 100000L // 100 KHz (max 400 KHz)
#endif
#define USE_INTERNAL_PULLUP 1
// TWI Actions
#define TWI_START 0
#define TWI_RESTART 1
#define TWI_STOP 2
#define TWI_TRANSMIT 3
#define TWI_RECEIVE_ACK 4
#define TWI_RECEIVE_NACK 5
// TWI status codes
#define TW_MT_SLA_ACK 0x18
#define TW_MT_DATA_ACK 0x28
#define TW_MR_SLA_ACK 0x40
#define TW_MR_DATA_ACK 0x50
#define TW_MR_DATA_NACK 0x58
void twi_start(uint32_t twi_freq = TWI_FREQ);
// Twi init
void twi_start(uint32_t twi_freq )
{
#if(USE_INTERNAL_PULLUP)
// activate internal pullups for twi.
DDRC &= ~ (( 1 << 4 ) | ( 1 << 5 )) ;
PORTC |= ( 1 << 4 ) | ( 1 << 5 );
#endif
// set TWI Freq
TWSR &= ~(_BV(TWPS0) | _BV(TWPS1)); // clear prescaler bits
TWBR = ((F_CPU / twi_freq) - 16) / 2;
}
// perform TWI action and wait for TWCR change
// return status code
uint8_t twi(uint8_t action)
{
switch (action)
{
case TWI_START:
case TWI_RESTART:
TWCR = _BV(TWSTA) | _BV(TWEN) | _BV(TWINT);// Если нужно прерывание | _BV(TWIE);
break;
case TWI_STOP:
TWCR = _BV(TWSTO) | _BV(TWEN) | _BV(TWINT);// | _BV(TWIE);
break;
case TWI_TRANSMIT:
TWCR = _BV(TWEN) | _BV(TWINT);// | _BV(TWIE);
break;
case TWI_RECEIVE_ACK:
TWCR = _BV(TWEN) | _BV(TWINT) | _BV(TWEA);//| _BV(TWIE);
break;
case TWI_RECEIVE_NACK:
TWCR = _BV(TWEN) | _BV(TWINT);// | _BV(TWIE);
break;
}
if (action != TWI_STOP) while (!(TWCR & _BV(TWINT)));
return (TWSR & 0xF8);
}
// transmit address or data byte via TWI
// return 1 with success, 0 - error
bool transmit_byte_I2C(uint8_t data, uint8_t OK_code)
{
TWDR = data;
return (OK_code == twi(TWI_TRANSMIT));
}
// read data from TWI
// return 1 with success, 0 - error
bool receive_byte_I2C(uint8_t* ptr, bool ack)
{ bool res_code = false;
if (ack) res_code = (TW_MR_DATA_ACK == twi(TWI_RECEIVE_ACK));
else res_code = (TW_MR_DATA_NACK == twi(TWI_RECEIVE_NACK));
if (res_code) *ptr = TWDR;
return res_code;
}
// write data bytes to EEPROM
// disk - 0x50-0x57, startAddress - 0x00 - 0xFF, &data - data ptr, len - (sizeof(data)
// return 1 with success, 0 - error
byte FM24C_write(byte disk, byte startAddress, void *data, unsigned int len) {
byte* ptr = (byte*) data;
twi(TWI_START);
bool twi_OK = false;
twi_OK = transmit_byte_I2C(disk << 1, TW_MT_SLA_ACK);
if (twi_OK) twi_OK = transmit_byte_I2C(startAddress, TW_MT_DATA_ACK);
while (twi_OK && len) {
twi_OK = transmit_byte_I2C(*ptr, TW_MT_DATA_ACK );
ptr++;
len--;
}
twi(TWI_STOP);
return twi_OK;
}
// read data bytes from EEPROM
// disk - 0x50-0x57, startAddress - 0x00 - 0xFF, &data - data ptr, len - (sizeof(data)
// return - number of bytes readed or 0 if error
byte FM24C_read(byte disk, byte startAddress, void *data, unsigned int len) {
byte rdata = 0;
byte *p;
twi(TWI_START);
bool twi_OK = false;
twi_OK = transmit_byte_I2C(disk << 1, TW_MT_SLA_ACK);
if (twi_OK) twi_OK = transmit_byte_I2C(startAddress, TW_MT_DATA_ACK);
if (twi_OK) {
twi(TWI_RESTART);
twi_OK = transmit_byte_I2C(((disk << 1) | 1), TW_MR_SLA_ACK);
for (rdata = 0, p = (byte*)data; (twi_OK && rdata < len); rdata++, p++) {
twi_OK = receive_byte_I2C(p, rdata < (len - 1));
}
}
twi(TWI_STOP);
return (rdata);
}
пример использования
#define disk1 0x50 //страницы FM24C
#include "FM24_Mini.h"
void setup() {
// setup TWI bus
twi_start(); // use default TWI freq = 100 KHz
// twi_start(400000L); // alternative: setup TWI freq 400 KHz
bool result = false;
Serial.begin(9600);
// пишем данные
Serial.print("writing...");
// Writing ulong (uint32_t)
unsigned long aa = 12345678;
// write data to disk1 (0x50), addr 254, from variable &aa, size of data 4 bytes (sizeof(uint32_t))
result = FM24C_write(disk1, 254, &aa, sizeof(aa));
if (result) Serial.println("write OK!");
else Serial.println("write error!");
// Writing int (uint16_t)
// int a = -12345;
// result = FM24C_write(disk1, 0, &a, sizeof(a));
// читаем данные
Serial.print("reading...");
uint32_t b;
// read data from disk1 (0x50), addr 254, to variable &b, size of data 4 bytes (sizeof(uint32_t))
result = FM24C_read(disk1, 254, &b, sizeof(b));
if (result) {Serial.println("read OK!");
Serial.print("Value read: ");
Serial.println(b);
}
else Serial.println("read error!");
}
void loop() {
// put your main code here, to run repeatedly:
}
Поправка - поддерживаются чипы Фрам FM24 от 2х до 16 Кбит, а не Мбит, как сказано в первом сообщении - это опечатка.
Дисклеймер - код не писался с нуля, использованы обрывки кода разных авторов.