DataFlash - Problema com gravações sequenciais

Enviado:
20 Nov 2012 15:08
por baltazar
Senhores,
Estou trabalhando pela primeira vez com uma memória flash e estou apanhando para conseguir gravar uma grande quantidade de pequenos pacotes.
A memória em questão é uma DataFlash SPI da Atmel modelo AT45DB321D.
O produto em questão é um coletor de dados que armazena pequenos registros a cada intervalo de tempo e isso estava funcionando bem até que resolvi implementar uma rotina para preencher a memória, chamando várias vezes a minha rotina original de armazenamento do registro, coisa bem simples.
Acontece que os dados gravados "automaticamente" apresentam lixo em alguns pontos, quando não "pulam" registros ou "ignoram" alguns.
O mais intrigante é que a cada vez que executo a rotina eu tenho um resultado diferente.
Já tentei colocar um delay entre os registros e o resultado foi o mesmo...
Alguém já teve alguma experiência parecida e pode me dar uma luz? Tenho certeza de que deve ser uma coisa simples, mas já estou ficando sem ideias aqui...
Abraços e obrigado!

Enviado:
20 Nov 2012 15:16
por cfreund
Veja se ajuda:
Lembre de ajustar o PAGESIZE. Se não me engano, são 528 bytes por padrão. Veja no datasheet.
- Código: Selecionar todos
#ifndef DATAFLASH_H
#define DATAFLASH_H
#include <stdint.h>
#include <avr/io.h>
#include <avr/power.h>
#define PORT_SPI PORTB
#define DDR_SPI DDRB
#define DD_SCK PB5
#define DD_MISO PB4
#define DD_MOSI PB3
#define DD_SS PB2
#define SPI_NOP 0xFF
#define SPIFLASH_CONFIG_CS DDRB |= (1 << PB2)
#define SPIFLASH_ASSERT_CS PORTB &= ~(1 << PB2)
#define SPIFLASH_RELEASE_CS PORTB |= (1 << PB2)
#define CMD_DEVICEID_READ 0x9F
#define CMD_DEEP_POWER_DOWN 0xB9
#define CMD_RESUME_POWER_DOWN 0xAB
#define CMD_MAIN_MEMORY_PAGE_READ 0xD2
#define CMD_STATUS_REGISTER_READ 0xD7
#define CMD_BUFFER1_WRITE 0x84
#define CMD_BUF1_TO_MAIN_PAGE_PROGRAM 0x83
#define CMD_MAIN_MEM_PAGE_TO_BUF1 0x53
#define DATAFLASH_PAGESIZE 528
void dflash_Init(void);
uint16_t dflash_GetDeviceID(void);
uint8_t dflash_GetStatus(void);
void dflash_PowerDown(uint8_t down);
void dflash_Read(uint32_t addr, uint16_t nbytes, uint8_t *data);
void dflash_Write(uint32_t addr, uint16_t nbytes, uint8_t *data);
#endif
- Código: Selecionar todos
#include "spi.h"
#include "dataflash.h"
void dflash_Init(void){
SPIFLASH_RELEASE_CS;
SPIFLASH_CONFIG_CS;
}
uint16_t dflash_GetDeviceID(void){
uint16_t devid;
SPIFLASH_ASSERT_CS;
spiSendByte(CMD_DEVICEID_READ);
devid = spiRecvByte();
devid = (devid << 8) | spiRecvByte();
SPIFLASH_RELEASE_CS;
return devid;
}
uint8_t dflash_GetStatus(void){
uint8_t status;
SPIFLASH_ASSERT_CS;
spiSendByte(CMD_STATUS_REGISTER_READ);
status = spiRecvByte();
SPIFLASH_RELEASE_CS;
return status;
}
void dflash_PowerDown(uint8_t down){
SPIFLASH_ASSERT_CS;
if(down) spiSendByte(CMD_DEEP_POWER_DOWN); else spiSendByte(CMD_RESUME_POWER_DOWN);
SPIFLASH_RELEASE_CS;
}
void dflash_Read(uint32_t addr, uint16_t nbytes, uint8_t *data){
while(!(dflash_GetStatus() & (1 << 7)));
if(addr) addr = ((addr / DATAFLASH_PAGESIZE) << 10) | (addr % DATAFLASH_PAGESIZE); else addr %= DATAFLASH_PAGESIZE;
SPIFLASH_ASSERT_CS;
spiSendByte(CMD_MAIN_MEMORY_PAGE_READ);
spiSendByte(addr >> 16);
spiSendByte(addr >> 8);
spiSendByte(addr);
spiSendByte(SPI_NOP);
spiSendByte(SPI_NOP);
spiSendByte(SPI_NOP);
spiSendByte(SPI_NOP);
while(nbytes--) *data++ = spiRecvByte();
SPIFLASH_RELEASE_CS;
}
void dflash_Write(uint32_t addr, uint16_t nbytes, uint8_t *data){
uint16_t page;
if(addr) page = addr / DATAFLASH_PAGESIZE; else page = 0;
while(!(dflash_GetStatus() & (1 << 7)));
SPIFLASH_ASSERT_CS;
spiSendByte(CMD_MAIN_MEM_PAGE_TO_BUF1);
spiSendByte(page >> 6);
spiSendByte(page << 2);
spiSendByte(0);
SPIFLASH_RELEASE_CS;
while(!(dflash_GetStatus() & (1 << 7)));
SPIFLASH_ASSERT_CS;
spiSendByte(CMD_BUFFER1_WRITE);
spiSendByte((addr % DATAFLASH_PAGESIZE) >> 16);
spiSendByte((addr % DATAFLASH_PAGESIZE) >> 8);
spiSendByte((addr % DATAFLASH_PAGESIZE));
while(nbytes--) spiSendByte(*data++);
SPIFLASH_RELEASE_CS;
spiSendByte(SPI_NOP);
SPIFLASH_ASSERT_CS;
spiSendByte(CMD_BUF1_TO_MAIN_PAGE_PROGRAM);
spiSendByte(page >> 6);
spiSendByte(page << 2);
spiSendByte(0);
SPIFLASH_RELEASE_CS;
}

Enviado:
20 Nov 2012 18:16
por Red Neck Guy
Olha eu nunca utilizei essa dataflash em questão, mas itens que tu deves observar:
1) Monitorar o flag que indica que a operação foi finalizada
2) Sempre que for escrever em uma página, transferir o conteúdo dela para o buffer ram interno ou copiar para um buffer no microcontrolador caso o a DF não possua, como nos modeloes SST25xx.
3) Nessa tua dataflash em questão existem comandos do tipo, copiar página para o buffer, aí tu alterar o buffer, manda apagar a página e programar o buffer lá. O que acontece é que a próxima escrita só pode ser feita depois de "um tempo" ou depois que o flag de busy estiver liberado.
4) Tu tem certeza que está comunicando com a memória? Tentou ler a assinatura dela?
5) Uma prática que eu adoto é ler o que escrevi na DF para ter garantia que os dados estão lá.
6) Sem conhecer o teu hardware e o teu código é só o que dá pra te ajudar.

Enviado:
21 Nov 2012 15:17
por RobL
É isso que colocaram aí.
Só para lembrar : Estas memos gravam somente páginas, ou seja, se formos gravar um byte, em certa posição, de uma página, toda a página será gravada. Por exemplo, um byte no meio da página, que começa no endereço zero, será gravado 0xFF em toda a página e será diferente somente no seu byte.
Portanto, sua rotina, após preencher a primeira página, tem que começar exatamente, no endereço da segunda página.
O ideal é sempre estar com a ram, cheia com uma página e transferir para a flash, pois gravar um byte ou uma página leva o mesmo tempo (ordem de grandeza unidades de milisegundos).
Se sua rotina não preenche uma página, e vai enchendo-a aos poucos, uma página, deve tomar cuidado, pois neste caso, os dados já gravados, terão que estar na mesma posição na ram(serão regravados) e os novos na sequência (pois estas memos só gravam uma página sempre).
Se ainda não preencher a página e sua rotina gravar uma página, acima do último dado válido na ram, estará um lixo, de sua ram, até completar uma página.
Se sua rotina errar o endereço inicial e final de qualquer página, dados já gravados serão alterados.
Nota: mesmo quando se grava um byte, gravação randômica, neste caso, também será gravado uma página, mas em todos os dados desta página, será feita uma operação lógica, com efeito similar a uma leitura dos dados desta página e regravando, desta forma, mantendo os demais dados já gravados, nesta página, sem alteração a não ser o seu dado endereçado que será alterado.

Enviado:
21 Nov 2012 16:36
por tcpipchip
Por falar nesta memoria...voces conhecem um PROGRAMADOR barato para esta memoria ?
Tentei equinox e xeltec e é uma forturna...quero por o U-BOOT nelas....minha placa linux teste nao tem JTAG


Enviado:
04 Dez 2012 15:21
por baltazar
Resolvido!
O problema, pra variar, estava nas constantes do CCS.
Quem por acaso tiver o mesmo problema, basta incluir os seguintes defines:
#define SPI_MODE_0_0 0x4000
#define SPI_MODE_0_1 0x0000
#define SPI_MODE_1_0 0x0010
#define SPI_MODE_1_1 0x4010
Os originais do CCS não configuram direito o barramento SPI.

Enviado:
04 Dez 2012 16:28
por andre_luis
tcpipchip escreveu:Por falar nesta memoria...voces conhecem um PROGRAMADOR barato para esta memoria ?
Tentei equinox e xeltec e é uma forturna...quero por o U-BOOT nelas....minha placa linux teste nao tem JTAG

A muito tempo atrás, fiz um gravador pela porta paralela, para uma memória EEPROM de 3,3v via SPI. Foi relativamente tranquilo. O hardware do gravador, prativamente era o conector, onde a placa ficava soldada.
+++