ATmega328 + SPI闪存
前言:这个问题是关于我正在与大学教授合作的一个项目。这不是为了成绩,但我在这位教授中的声誉确实很重要。因此,虽然我在这个项目上的成功对我来说很重要,但我并不认为向 Stack Overflow 寻求帮助是不公平的。
也就是说,这是我的项目的高级概述。我有一个 ATmega328 微控制器。我有一个 Microchip SST 64 Mbit 闪存卡。 ATmega 有 SPI 的硬件实现。闪存具有 SPI 的硬件实现。
我的目标是在 SPI 主模式下使用 ATmega 从闪存芯片读取数据并向闪存芯片写入数据。内存以多重覆盖结构组织,这很适合擦除,但就我的目的而言,它基本上只有 32,768 页,每页 256 字节。
要写入数据,基本思想是发送一个指令字节,然后发送起始地址,然后发送数据。要读取数据,基本思想是发送一个指令字节,然后发送起始地址,然后发送一个虚拟字节,然后它开始向我发送数据。
以下是数据表:
微控制器:http:// www.atmel.com/dyn/resources/prod_documents/doc8271.pdf
Flash:http: //www.sst.com/dotAsset/40498.pdf
代码:
#include <SPI.h>
#include <Peggy2.h>
#define SS_PIN 16
Peggy2 frame1;
byte toDisp = 0;
byte checker = 0;
void setup()
{
frame1.HardwareInit();
pinMode(SS_PIN,OUTPUT); //set pin16 to output, SS pin
SPI.setClockDivider(SPI_CLOCK_DIV2); //set the SPI clock to f/2, fastest possible
SPI.begin(); //SPI lib function which sets ddr for SCK and MOSI pin
//MISO is auto input
//see SPI.cpp for more info
}
void loop()
{
if(!checker){
enableProgramming();
programData();
toDisp = receiveByte(0);
checker = 1;
frame1.WriteRow(0,toDisp);
}
frame1.RefreshAll(2);
}
byte receiveByte(unsigned long startAddress)
{
//Begin High Speed Read Instruction
//See p. 10 of SST data sheet
digitalWrite(SS_PIN,LOW);
SPI.transfer(0x0B); //high speed read instruction
SPI.transfer(0x00); //next 3 transfers are address bits A32 - A0
SPI.transfer(0x00); //So this will read the first byte on the chip
SPI.transfer(0x00); //last address bits
SPI.transfer(0xFF); //dummy byte is required to start sending data back to uP
SPI.transfer(0xFF); //I'm hoping that if I transfer a bullshit byte, the flash
//chip will transfer it's data to me in the same time
digitalWrite(SS_PIN,HIGH);
//End High Speed Read Instruction
return SPDR;
}
//will perform the read instruction starting from
//startAddress and will receive numOfBytes bytes in
//succession
void receiveBytes(int numOfBytes, unsigned long startAddress)
{
digitalWrite(SS_PIN,LOW);
SPI.transfer(0x0B);//high speed read instruction
}
//will perform:
// 1) Chip Erase
// and loop through:
// 1) Page Program
// 2) increment Page
//until the data has finished **note this can loop and over write beginning of memory
void programData(){
//Begin ChipErase Instruction
//See p. 17 of SST data sheet
digitalWrite(SS_PIN,LOW);
SPI.transfer(0x60);//chip erase instruction
digitalWrite(SS_PIN,HIGH);
delay(50);//spec'd time for CE to finish
//don't bother polling because time to program is irrelevant
//End ChipErase Instruction
//Begin WREN Instruction
//See p. 18 of SST data sheet
digitalWrite(SS_PIN,LOW);
SPI.transfer(0x06);//write enable instruction
digitalWrite(SS_PIN,HIGH);
//End WREN Instruction
digitalWrite(SS_PIN,LOW);
SPI.transfer(0x02); //page program instruction
SPI.transfer(0x00); //first 8 address bits
SPI.transfer(0x00); //2nd 8 address bits
SPI.transfer(0x00); //3rd 8 address bits
SPI.transfer(0xAA); //10101010 is the byte I should be writing
digitalWrite(SS_PIN,HIGH);
delayMicroseconds(3000); //wait 3 ms for page program
/*
//Begin Page-Program Instruction
//see p. 13 of SST data sheet
byte firstAddress = 0;
byte secondAddress = 0;
//this loop will write to every byte in the chips memory
//32,768 pages of 256 bytes = 8,388,608 bytes
for(unsigned int i = 0; i < 32,768; ++i) //long variable is number of pages
{
digitalWrite(SS_PIN,LOW);
++secondAddress; //cycles from 0 to 255, counts pages
firstAddress = i>>8; // floor(i/256)
SPI.transfer(0x02);//Page-Program instruction byte
SPI.transfer(firstAddress); //increments every 256 pages i.e. at page 256 this should be 1
SPI.transfer(secondAddress); //increments every 256 bytes, i.e every page
SPI.transfer(0x00); //beginning of a page boundary
for(int j = 0; j < 256; ++j) //number of bytes per page
{
SPI.transfer(2program[(256*i) + j]);//data byte transfer
}
digitalWrite(SS_PIN,HIGH);
delayMicroseconds(2500); //2500us (2.5ms) delay for each page-program instruction to execute
}
//End Page-Program Instruction
*/
}
//Will prepare the chip for writing by performing:
// 1) arm the status register
// 2) Write Enable instruction
//Only needs to be performed once!
void enableProgramming(){
//Begin EWSR & WRSR Instructions
//See p. 20 of SST data sheet for more info
digitalWrite(SS_PIN,LOW); //lower the SS pin
SPI.transfer(0x50); //enable write status register instruction
digitalWrite(SS_PIN,HIGH); //raise the SS pin
delay(10);
digitalWrite(SS_PIN,LOW); //lower the SS pin
SPI.transfer(0x01); //write the status register instruction
SPI.transfer(0x00);//value to write to register
//xx0000xx will remove all block protection
digitalWrite(SS_PIN,HIGH);
//End EWSR & WRSR Instructions
//Begin WREN Instruction
//See p. 18 of SST data sheet
digitalWrite(SS_PIN,LOW);
SPI.transfer(0x06);//write enable instruction
digitalWrite(SS_PIN,HIGH);
//End WREN Instruction
}
所以这应该是一个测试程序,它将 1 个字节编程到闪存上,然后读回并显示我拥有的 LED 阵列上的字节。如果您对 LED 阵列感兴趣,可以在这里找到:http://evilmadscience.com/tinykitlist/157
我相信我的读取功能有效,因为我第一次运行此功能时,所有 8 个 LED 都亮了。这对我来说表明它在闪存处于全 1 的出厂状态时读取了闪存。现在显然我把一些东西搞砸了,因为点亮的字节与我试图编程的字节根本不对应。
我还应该注意,我正在使用 Arduino 的默认 SPI 库,并且帧缓冲区功能可以正常工作。当我执行frame1.WriteRow(toDisp)
时,它工作正常并且已经过广泛测试。
如果有人有时间或耐心找出我做错了什么,那就太棒了。
编辑:帮助调试:
LED 由同样使用 SPI 接口的驱动芯片驱动。我没有写那部分代码。在示波器上我可以看到 SCK 线是由那部分代码驱动的。然而,我在 MOSI 引脚上也有一个探针,如果我不点亮任何灯,它似乎永远不会变高。对我来说,这意味着我没有正确发送信息。又名...也许我的 SPI.transfer() 需要启用功能或其他功能?
Preface: This question is about a project I am working on with a professor at my university. It is NOT for a grade, but my reputation with this professor does matter. So while my success on this project is important to me, I do not see it as unfair to seek help from Stack Overflow.
That said, here is a high level overview of my project. I have an ATmega328 microcontroller. I have a Microchip SST 64 Mbit flash memory card. The ATmega has a hardware implementation of SPI. The flash memory has a hardware implementation of SPI.
My goal is to read data from and write data to the flash chip using the ATmega in SPI master mode. The memory is organized in a multiple overlay structure which is nice for erasing but for my purposes, it's basically just 32,768 pages of 256 bytes each.
To write data the basic idea is I send an instruction byte, then the starting address, then the data. To read data the basic idea is that I send an instruction byte, then the starting address, then a dummy byte, and then it starts sending me data.
Here are the data sheets:
Microcontroller: http://www.atmel.com/dyn/resources/prod_documents/doc8271.pdf
Flash: http://www.sst.com/dotAsset/40498.pdf
Code:
#include <SPI.h>
#include <Peggy2.h>
#define SS_PIN 16
Peggy2 frame1;
byte toDisp = 0;
byte checker = 0;
void setup()
{
frame1.HardwareInit();
pinMode(SS_PIN,OUTPUT); //set pin16 to output, SS pin
SPI.setClockDivider(SPI_CLOCK_DIV2); //set the SPI clock to f/2, fastest possible
SPI.begin(); //SPI lib function which sets ddr for SCK and MOSI pin
//MISO is auto input
//see SPI.cpp for more info
}
void loop()
{
if(!checker){
enableProgramming();
programData();
toDisp = receiveByte(0);
checker = 1;
frame1.WriteRow(0,toDisp);
}
frame1.RefreshAll(2);
}
byte receiveByte(unsigned long startAddress)
{
//Begin High Speed Read Instruction
//See p. 10 of SST data sheet
digitalWrite(SS_PIN,LOW);
SPI.transfer(0x0B); //high speed read instruction
SPI.transfer(0x00); //next 3 transfers are address bits A32 - A0
SPI.transfer(0x00); //So this will read the first byte on the chip
SPI.transfer(0x00); //last address bits
SPI.transfer(0xFF); //dummy byte is required to start sending data back to uP
SPI.transfer(0xFF); //I'm hoping that if I transfer a bullshit byte, the flash
//chip will transfer it's data to me in the same time
digitalWrite(SS_PIN,HIGH);
//End High Speed Read Instruction
return SPDR;
}
//will perform the read instruction starting from
//startAddress and will receive numOfBytes bytes in
//succession
void receiveBytes(int numOfBytes, unsigned long startAddress)
{
digitalWrite(SS_PIN,LOW);
SPI.transfer(0x0B);//high speed read instruction
}
//will perform:
// 1) Chip Erase
// and loop through:
// 1) Page Program
// 2) increment Page
//until the data has finished **note this can loop and over write beginning of memory
void programData(){
//Begin ChipErase Instruction
//See p. 17 of SST data sheet
digitalWrite(SS_PIN,LOW);
SPI.transfer(0x60);//chip erase instruction
digitalWrite(SS_PIN,HIGH);
delay(50);//spec'd time for CE to finish
//don't bother polling because time to program is irrelevant
//End ChipErase Instruction
//Begin WREN Instruction
//See p. 18 of SST data sheet
digitalWrite(SS_PIN,LOW);
SPI.transfer(0x06);//write enable instruction
digitalWrite(SS_PIN,HIGH);
//End WREN Instruction
digitalWrite(SS_PIN,LOW);
SPI.transfer(0x02); //page program instruction
SPI.transfer(0x00); //first 8 address bits
SPI.transfer(0x00); //2nd 8 address bits
SPI.transfer(0x00); //3rd 8 address bits
SPI.transfer(0xAA); //10101010 is the byte I should be writing
digitalWrite(SS_PIN,HIGH);
delayMicroseconds(3000); //wait 3 ms for page program
/*
//Begin Page-Program Instruction
//see p. 13 of SST data sheet
byte firstAddress = 0;
byte secondAddress = 0;
//this loop will write to every byte in the chips memory
//32,768 pages of 256 bytes = 8,388,608 bytes
for(unsigned int i = 0; i < 32,768; ++i) //long variable is number of pages
{
digitalWrite(SS_PIN,LOW);
++secondAddress; //cycles from 0 to 255, counts pages
firstAddress = i>>8; // floor(i/256)
SPI.transfer(0x02);//Page-Program instruction byte
SPI.transfer(firstAddress); //increments every 256 pages i.e. at page 256 this should be 1
SPI.transfer(secondAddress); //increments every 256 bytes, i.e every page
SPI.transfer(0x00); //beginning of a page boundary
for(int j = 0; j < 256; ++j) //number of bytes per page
{
SPI.transfer(2program[(256*i) + j]);//data byte transfer
}
digitalWrite(SS_PIN,HIGH);
delayMicroseconds(2500); //2500us (2.5ms) delay for each page-program instruction to execute
}
//End Page-Program Instruction
*/
}
//Will prepare the chip for writing by performing:
// 1) arm the status register
// 2) Write Enable instruction
//Only needs to be performed once!
void enableProgramming(){
//Begin EWSR & WRSR Instructions
//See p. 20 of SST data sheet for more info
digitalWrite(SS_PIN,LOW); //lower the SS pin
SPI.transfer(0x50); //enable write status register instruction
digitalWrite(SS_PIN,HIGH); //raise the SS pin
delay(10);
digitalWrite(SS_PIN,LOW); //lower the SS pin
SPI.transfer(0x01); //write the status register instruction
SPI.transfer(0x00);//value to write to register
//xx0000xx will remove all block protection
digitalWrite(SS_PIN,HIGH);
//End EWSR & WRSR Instructions
//Begin WREN Instruction
//See p. 18 of SST data sheet
digitalWrite(SS_PIN,LOW);
SPI.transfer(0x06);//write enable instruction
digitalWrite(SS_PIN,HIGH);
//End WREN Instruction
}
So this is supposed to be a test program which programs 1 byte onto the flash and then reads it back and displays that byte on an LED array I have. If you're interested in the LED array, it can be found here: http://evilmadscience.com/tinykitlist/157
I believe my read function works because the first time I ran this, all 8 LEDs lit up. That would indicate to me that it read the flash memory when it was in it's factory state of all 1s. Now apparently I've screwed something up with the writing because the byte that lights up does not correspond at all with the byte that I'm attempting to program.
I should also note I'm using the default SPI library for Arduinos and the frame buffer functions work. When I do frame1.WriteRow(toDisp)
, that is working correctly and has been tested extensively.
If anyone has the time or patience to figure out what I'm doing wrong that would be extremely awesome.
EDIT: To help debugging:
The LEDs are being driven by driver chips which use the SPI interface as well. I did not write that part of the code. On an oscilloscope I can see the SCK line being driven by that part of the code. However, I also have a probe on the MOSI pin and if I don't light up any lights, it never appears to go high. To me that means I'm not sending information correctly. AKA... perhaps my SPI.transfer()
needs an enable function or something?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
对于仍然好奇的人来说,问题在于存储芯片对缓慢的上升时间极其敏感。安装施密特触发器后,一切正常。
To anyone still curious the problem was that the memory chip was extremely sensitive to slow rise times. After putting in a schmitt trigger, everything worked perfectly.