unknown work 1
This commit is contained in:
parent
935911fd2c
commit
32bdb169b7
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@ -16,6 +16,9 @@
|
||||
"tuple": "cpp",
|
||||
"type_traits": "cpp",
|
||||
"vector": "cpp",
|
||||
"string": "cpp"
|
||||
"string": "cpp",
|
||||
"__string": "cpp",
|
||||
"string_view": "cpp",
|
||||
"__split_buffer": "cpp"
|
||||
}
|
||||
}
|
8
Makefile
8
Makefile
@ -645,6 +645,14 @@ OBJECTS += ./src/drivers/MPU6000.o
|
||||
OBJECTS += ./src/drivers/MS5611.o
|
||||
OBJECTS += ./src/drivers/stepper.o
|
||||
OBJECTS += ./src/drivers/servo.o
|
||||
OBJECTS += ./src/drivers/SerialFlash/M25PDevice.o
|
||||
OBJECTS += ./src/drivers/SerialFlash/M25PDeviceImpl.o
|
||||
OBJECTS += ./src/drivers/SerialFlash/MbedSPICommand.o
|
||||
OBJECTS += ./src/drivers/SerialFlash/SerialFlashDeviceCreator.o
|
||||
OBJECTS += ./src/drivers/SerialFlash/SST25Device.o
|
||||
OBJECTS += ./src/drivers/SerialFlash/SST25DeviceImpl.o
|
||||
OBJECTS += ./src/drivers/M25P16.o
|
||||
OBJECTS += ./src/drivers/SpiFlash25.o
|
||||
OBJECTS += ./src/control/ImuFusion.o
|
||||
OBJECTS += ./src/control/PID.o
|
||||
OBJECTS += ./src/math/Utilities.o
|
||||
|
Binary file not shown.
128
main.cpp
128
main.cpp
@ -14,10 +14,15 @@
|
||||
#include "src/drivers/MPU6000.h"
|
||||
#include "src/drivers/stepper.h"
|
||||
#include "src/drivers/servo.h"
|
||||
#include "src/drivers/M25P16.h"
|
||||
// Control
|
||||
#include "src/control/lpf.h"
|
||||
#include "src/control/PID.h"
|
||||
#include "src/control/ImuFusion.h"
|
||||
// Flash Memory - TODO: This component is not correctly implemented yet
|
||||
#include "src/drivers/SerialFlash/MbedSPICommand.h"
|
||||
#include "src/drivers/SerialFlash/SerialFlashDeviceCreator.h"
|
||||
#include "src/drivers/SpiFlash25.h"
|
||||
|
||||
#define WHEEL_SIZE 0.09f
|
||||
|
||||
@ -32,8 +37,14 @@ EventQueue queue;
|
||||
Serial serialMotorOutputs(PA_2, PA_3, 115200);
|
||||
|
||||
// MPU setup
|
||||
SPI spi(PA_7, PA_6, PA_5); //define the SPI (mosi, miso, sclk). Default frequency is 1Mhz
|
||||
mpu6000_spi imu(spi,PA_4); //define the mpu6000 object
|
||||
SPI spiImu(PA_7, PA_6, PA_5); //define the SPI (mosi, miso, sclk). Default frequency is 1Mhz
|
||||
mpu6000_spi imu(spiImu,PA_4); //define the mpu6000 object
|
||||
|
||||
// Flash setup
|
||||
//SPI spiFlash(PC_12, PC_11, PC_10, PA_15);
|
||||
//SPI spiFlash(PC_12, PC_11, PC_10, PB_3);
|
||||
//M25P16 memory(spiFlash, PB_3);
|
||||
//DigitalOut radioCS(PA_15);
|
||||
|
||||
PwmOut ledBlue(D4);
|
||||
DigitalOut ledOrg(D5);
|
||||
@ -48,7 +59,7 @@ InterruptIn gyroINT(PC_4);
|
||||
|
||||
Timer timer;
|
||||
|
||||
// TODO: Figure out some good values
|
||||
// Throttle PI Controller with some acceptable values
|
||||
controllerPI throttleControl(0.0025, 0.01, 5, 0); // 0.065, 0.05, 12, 40
|
||||
|
||||
// TODO: Figure out some good values
|
||||
@ -174,6 +185,74 @@ void pulseLedContext()
|
||||
}
|
||||
}
|
||||
|
||||
void testFlash()
|
||||
{
|
||||
ISPICommand* pSPICommand = new MbedSPICommand(PC_12, PC_11, PC_10, PB_3); // MOSI, MISO, SCK, CS
|
||||
ISerialFlashDevice* pDevice = SerialFlashDeviceCreator::Create(pSPICommand);
|
||||
|
||||
if (pDevice == NULL)
|
||||
{
|
||||
printf("Unsupported device");
|
||||
return;
|
||||
}
|
||||
|
||||
// std::string deviceName = pDevice->GetDeviceName();
|
||||
// printf("%s\n", deviceName);
|
||||
int capacity = pDevice->GetCapacity() / 1024;
|
||||
printf("%dK x 8\n", capacity);
|
||||
|
||||
// int *a[3];
|
||||
// *a[0] = 1;
|
||||
// *a[1] = 2;
|
||||
// *a[2] = 3;
|
||||
|
||||
// int *b[3];
|
||||
|
||||
int result;
|
||||
|
||||
// result = pDevice->Write(100, a, sizeof(a));
|
||||
|
||||
// result = pDevice->Read(100, b, sizeof(b));
|
||||
|
||||
uint8_t c(-1), d(0);
|
||||
|
||||
result = pDevice->Read(200, &c, sizeof(c));
|
||||
|
||||
wait(1);
|
||||
|
||||
c += 4;
|
||||
|
||||
result = pDevice->Write(200, &c, sizeof(c));
|
||||
|
||||
wait(1);
|
||||
|
||||
result = pDevice->Read(200, &d, sizeof(d));
|
||||
|
||||
}
|
||||
|
||||
void testFlash2()
|
||||
{
|
||||
SpiFlash25 flash(PC_12, PC_11, PC_10, PB_3);
|
||||
|
||||
char* k = flash.read_id();
|
||||
|
||||
char f = flash.read_status();
|
||||
|
||||
char a[] = "Hej och ha";
|
||||
|
||||
bool result;
|
||||
|
||||
result = flash.write(0, strlen(a), a);
|
||||
|
||||
char b[20];
|
||||
|
||||
result = flash.read(0, strlen(a), b);
|
||||
|
||||
printf("result");
|
||||
|
||||
|
||||
}
|
||||
|
||||
// main() runs in its own thread
|
||||
int main() {
|
||||
|
||||
@ -182,6 +261,49 @@ int main() {
|
||||
Thread serialThread;
|
||||
serialThread.start(callback(&readSerialContext));
|
||||
|
||||
// Init memory
|
||||
DigitalOut radioCS(PA_15);
|
||||
radioCS = 1; // Disable radio chip. May not be necessary
|
||||
//SPI spiFlash(PC_12, PC_11, PC_10);
|
||||
|
||||
testFlash2();
|
||||
|
||||
// M25P16 memory(spiFlash, PB_3);
|
||||
// bool memoryUp = memory.init();
|
||||
|
||||
//printf("Initialization of onboard memory " + memoryUp ? "succeded" : "failed");
|
||||
|
||||
//memory.chipErase();
|
||||
int start = 0;
|
||||
|
||||
int values[3];
|
||||
//memory.read(start, &values, sizeof(values));
|
||||
|
||||
|
||||
values[0]++;
|
||||
//values[1]++;
|
||||
values[2]++;
|
||||
|
||||
//memory.write(start, values, sizeof(values));
|
||||
|
||||
|
||||
// int values2[3];
|
||||
// memory.read(start, values2, sizeof(values2));
|
||||
|
||||
// memory.read(0, values2, sizeof(values2));
|
||||
|
||||
// values[0]++;
|
||||
// values[1]++;
|
||||
// values[2]++;
|
||||
|
||||
// start += sizeof(values);
|
||||
// memory.write(0, values, sizeof(values));
|
||||
|
||||
// memory.read(0, values2, sizeof(values2));
|
||||
|
||||
|
||||
|
||||
|
||||
// MPU startup at 100hz
|
||||
if(imu.init(10,BITS_DLPF_CFG_188HZ)){
|
||||
printf("\nCouldn't initialize MPU6000 via SPI!");
|
||||
|
259
src/drivers/M25P16.cpp
Normal file
259
src/drivers/M25P16.cpp
Normal file
@ -0,0 +1,259 @@
|
||||
#include "M25P16.h"
|
||||
|
||||
namespace drivers {
|
||||
|
||||
M25P16::M25P16(SPI& spi, PinName cs)
|
||||
: m_spi(spi)
|
||||
, m_cs(cs)
|
||||
, m_property()
|
||||
{
|
||||
m_property.deviceName = "M25P16";
|
||||
m_property.capacity = 16384 * 1024 / 8;
|
||||
m_property.blockProtectionMask = 0xe3;
|
||||
}
|
||||
|
||||
bool M25P16::init()
|
||||
{
|
||||
//clearBlockProtection();
|
||||
|
||||
int manufacturerId;
|
||||
int memoryType;
|
||||
int memoryCapacity;
|
||||
|
||||
readId(manufacturerId, memoryType, memoryCapacity);
|
||||
|
||||
clearBlockProtection();
|
||||
|
||||
// If we get the expected values from the bus then we are good
|
||||
return manufacturerId == 0x20
|
||||
&& memoryType == 0x20
|
||||
&& memoryCapacity == 0x15;
|
||||
}
|
||||
|
||||
void M25P16::readId(int& manufacturerId, int& memoryType, int& memoryCapacity)
|
||||
{
|
||||
m_spi.frequency(46000000);
|
||||
|
||||
unsigned char id[3];
|
||||
readSPI(0x9f, id, sizeof(id));
|
||||
manufacturerId = id[0];
|
||||
memoryType = id[1];
|
||||
memoryCapacity = id[2];
|
||||
}
|
||||
|
||||
int M25P16::read(int address, void* buffer, int length)
|
||||
{
|
||||
if (address >= getCapacity())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (address + length > getCapacity())
|
||||
{
|
||||
length = getCapacity() - address;
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char param[] = {0, 0, 0, 0xff};
|
||||
fillAddress(param, address);
|
||||
readSPI(0x0b, param, sizeof(param), buffer, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
void M25P16::fillAddress(char* pBuffer, int address)
|
||||
{
|
||||
*(pBuffer + 0) = (address & 0xff0000) >> 16;
|
||||
*(pBuffer + 1) = (address & 0x00ff00) >> 8;
|
||||
*(pBuffer + 2) = (address & 0x0000ff);
|
||||
}
|
||||
|
||||
int M25P16::write(int address, const void* buffer, int length)
|
||||
{
|
||||
if (address >= getCapacity())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (address + length > getCapacity())
|
||||
{
|
||||
length = getCapacity() - address;
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = length;
|
||||
|
||||
const int pageSize = 256;
|
||||
const int pageSizeMask = pageSize - 1;
|
||||
const int pageAddressMask = ~pageSizeMask;
|
||||
|
||||
const unsigned char* p = static_cast<const unsigned char*>(buffer);
|
||||
if ((address & pageSizeMask) != 0)
|
||||
{
|
||||
int readLen = address & pageSizeMask;
|
||||
int copyLen = pageSize - readLen;
|
||||
if (copyLen > length)
|
||||
{
|
||||
copyLen = length;
|
||||
}
|
||||
char buf[pageSize];
|
||||
int writeAddress = address & pageAddressMask;
|
||||
read(writeAddress, buf, readLen);
|
||||
memcpy(&buf[address & pageSizeMask], buffer, copyLen);
|
||||
pageProgram(writeAddress, buf);
|
||||
p += readLen;
|
||||
address += pageSize - readLen;
|
||||
length -= copyLen;
|
||||
}
|
||||
while (length >= pageSize)
|
||||
{
|
||||
pageProgram(address, p);
|
||||
address += pageSize;
|
||||
p += pageSize;
|
||||
length -= pageSize;
|
||||
}
|
||||
if (length != 0)
|
||||
{
|
||||
char buf[pageSize];
|
||||
memcpy(buf, p, length);
|
||||
memset(&buf[length], 0xff, pageSize - length);
|
||||
pageProgram(address, buf);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void M25P16::select()
|
||||
{
|
||||
m_cs = 0;
|
||||
}
|
||||
|
||||
void M25P16::deselect()
|
||||
{
|
||||
m_cs = 1;
|
||||
}
|
||||
|
||||
unsigned int M25P16::whoami()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int M25P16::getCapacity() const
|
||||
{
|
||||
return m_property.capacity;
|
||||
}
|
||||
|
||||
std::string M25P16::getDeviceName() const
|
||||
{
|
||||
return m_property.deviceName;
|
||||
}
|
||||
|
||||
int M25P16::readStatusRegister(void)
|
||||
{
|
||||
unsigned char status;
|
||||
readSPI(0x05, &status, 1);
|
||||
return status;
|
||||
}
|
||||
|
||||
void M25P16::writeStatusRegister(int value)
|
||||
{
|
||||
char vb = static_cast<char>(value);
|
||||
writeSPI(0x01, &vb, 1);
|
||||
}
|
||||
|
||||
void M25P16::writeEnable()
|
||||
{
|
||||
writeSPI(0x06);
|
||||
}
|
||||
|
||||
void M25P16::writeDisable()
|
||||
{
|
||||
writeSPI(0x04);
|
||||
}
|
||||
|
||||
void M25P16::clearBlockProtection()
|
||||
{
|
||||
writeEnable();
|
||||
|
||||
int status = readStatusRegister();
|
||||
status &= m_property.blockProtectionMask;
|
||||
writeStatusRegister(status);
|
||||
}
|
||||
|
||||
void M25P16::writeSPI(int command)
|
||||
{
|
||||
m_cs = 0;
|
||||
m_spi.write(command);
|
||||
m_cs = 1;
|
||||
}
|
||||
|
||||
void M25P16::writeSPI(int command, const void* pParam, int length)
|
||||
{
|
||||
m_cs = 0;
|
||||
m_spi.write(command);
|
||||
|
||||
const char*p = static_cast<const char*>(pParam);
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
m_spi.write(*p++);
|
||||
}
|
||||
m_cs = 1;
|
||||
}
|
||||
|
||||
void M25P16::readSPI(int command, void* pRead, int readLength)
|
||||
{
|
||||
m_cs = 0;
|
||||
m_spi.write(command);
|
||||
|
||||
char* pReadByte = static_cast<char*>(pRead);
|
||||
for (int i = 0; i < readLength; ++i)
|
||||
{
|
||||
*pReadByte++ = m_spi.write(0xff);
|
||||
}
|
||||
m_cs = 1;
|
||||
}
|
||||
|
||||
void M25P16::readSPI(int command, const void* pParam, int paramLength, void* pReaded, int readLength)
|
||||
{
|
||||
m_cs = 0;
|
||||
m_spi.write(command);
|
||||
|
||||
const char* pParamByte = static_cast<const char*>(pParam);
|
||||
for (int i = 0; i < paramLength; ++i)
|
||||
{
|
||||
m_spi.write(*pParamByte++);
|
||||
}
|
||||
char* pReadedByte = static_cast<char*>(pReaded);
|
||||
for (int i = 0; i < readLength; ++i)
|
||||
{
|
||||
*pReadedByte++ = m_spi.write(0xff);
|
||||
}
|
||||
m_cs = 1;
|
||||
}
|
||||
|
||||
void M25P16::chipErase()
|
||||
{
|
||||
writeEnable();
|
||||
|
||||
writeSPI(0xc7);
|
||||
|
||||
while (readStatusRegister() & 0x01);
|
||||
|
||||
clearBlockProtection();
|
||||
}
|
||||
|
||||
void M25P16::pageProgram(int address, const void* buffer)
|
||||
{
|
||||
writeEnable();
|
||||
|
||||
char param[256 + 3];
|
||||
fillAddress(param, address);
|
||||
memcpy(param + 3, buffer, 256);
|
||||
writeSPI(0x02, param, sizeof(param));
|
||||
|
||||
while (readStatusRegister() & 0x01);
|
||||
}
|
||||
|
||||
} // namespace drivers
|
74
src/drivers/M25P16.h
Normal file
74
src/drivers/M25P16.h
Normal file
@ -0,0 +1,74 @@
|
||||
#include "mbed.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace drivers {
|
||||
|
||||
class M25P16 {
|
||||
|
||||
public:
|
||||
|
||||
M25P16(SPI& spi, PinName cs);
|
||||
|
||||
bool init();
|
||||
|
||||
int read(int address, void* buffer, int length);
|
||||
|
||||
int write(int address, const void* buffer, int length);
|
||||
|
||||
void chipErase();
|
||||
|
||||
unsigned int whoami();
|
||||
|
||||
private:
|
||||
|
||||
SPI& m_spi;
|
||||
DigitalOut m_cs;
|
||||
|
||||
struct DeviceProperty
|
||||
{
|
||||
std::string deviceName;
|
||||
int capacity;
|
||||
int blockProtectionMask;
|
||||
} m_property;
|
||||
|
||||
// Returns the size of the memory
|
||||
int getCapacity() const;
|
||||
|
||||
void readId(int& manufacturerId, int& memoryType, int& memoryCapacity);
|
||||
|
||||
std::string getDeviceName() const;
|
||||
|
||||
void fillAddress(char* pBuffer, int address);
|
||||
|
||||
int readStatusRegister();
|
||||
|
||||
void writeStatusRegister(int value);
|
||||
|
||||
void writeEnable();
|
||||
|
||||
void writeDisable();
|
||||
|
||||
void clearBlockProtection();
|
||||
|
||||
void select();
|
||||
|
||||
void deselect();
|
||||
|
||||
void writeSPI(int command);
|
||||
|
||||
void writeSPI(int command, const void* pParam, int length);
|
||||
|
||||
void readSPI(int command, void* pRead, int readLength);
|
||||
|
||||
void readSPI(int command, const void* pParam, int paramLength, void* pReaded, int readLength);
|
||||
|
||||
void pageProgram(int address, const void* buffer);
|
||||
|
||||
};
|
||||
|
||||
#define MYBIT 0x00
|
||||
|
||||
} // namespace drivers
|
26
src/drivers/SPICommand/main.cpp
Normal file
26
src/drivers/SPICommand/main.cpp
Normal file
@ -0,0 +1,26 @@
|
||||
#include "mbed.h"
|
||||
#include "MbedSPICommand.h"
|
||||
#include "SerialFlashDeviceCreator.h"
|
||||
|
||||
DigitalOut myled(LED1);
|
||||
|
||||
int main()
|
||||
{
|
||||
ISPICommand* pSPICommand = new MbedSPICommand(p11, p12, p13, p14); // MOSI, MISO, SCK, CS
|
||||
|
||||
ISerialFlashDevice* pDevice = SerialFlashDeviceCreator::Create(pSPICommand);
|
||||
if (pDevice == NULL)
|
||||
{
|
||||
printf("Unsupported device");
|
||||
return;
|
||||
}
|
||||
printf("%s\n", pDevice->GetDeviceName());
|
||||
printf("%dK x 8\n", pDevice->GetCapacity() / 1024);
|
||||
|
||||
while(1) {
|
||||
myled = 1;
|
||||
wait(0.2);
|
||||
myled = 0;
|
||||
wait(0.2);
|
||||
}
|
||||
}
|
1
src/drivers/SPICommand/mbed.bld
Normal file
1
src/drivers/SPICommand/mbed.bld
Normal file
@ -0,0 +1 @@
|
||||
http://mbed.org/users/mbed_official/code/mbed/builds/6473597d706e
|
14
src/drivers/SerialFlash/ISPICommand.h
Normal file
14
src/drivers/SerialFlash/ISPICommand.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
class ISPICommand
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~ISPICommand() {}
|
||||
virtual void SetMaxFrequency(int frequency) = 0;
|
||||
|
||||
virtual void Write(int command) = 0;
|
||||
virtual void Write(int command, const void* pParam, int length) = 0;
|
||||
virtual void Read(int command, void* pReaded, int readLength) = 0;
|
||||
virtual void Read(int command, const void* pParam, int paramLength, void* pReaded, int readLength) = 0;
|
||||
};
|
14
src/drivers/SerialFlash/ISerialFlashDevice.h
Normal file
14
src/drivers/SerialFlash/ISerialFlashDevice.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class ISerialFlashDevice
|
||||
{
|
||||
public:
|
||||
virtual ~ISerialFlashDevice(void) { }
|
||||
virtual std::string GetDeviceName(void) const = 0;
|
||||
virtual int GetCapacity(void) const = 0;
|
||||
virtual void ChipErase(void) = 0;
|
||||
virtual int Read(int address, void* buffer, int length) = 0;
|
||||
virtual int Write(int address, const void* buffer, int length) = 0;
|
||||
};
|
50
src/drivers/SerialFlash/M25PDevice.cpp
Normal file
50
src/drivers/SerialFlash/M25PDevice.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "mbed.h"
|
||||
#include "M25PDeviceImpl.h"
|
||||
#include "M25PDevice.h"
|
||||
|
||||
bool M25PDevice::IsSupported(ISPICommand* pSPICommand)
|
||||
{
|
||||
return M25PDeviceImpl::IsSupported(pSPICommand);
|
||||
}
|
||||
|
||||
M25PDevice* M25PDevice::Create(ISPICommand* pSPICommand)
|
||||
{
|
||||
M25PDeviceImpl* pImpl = M25PDeviceImpl::Create(pSPICommand);
|
||||
if (pImpl == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return new M25PDevice(pImpl);
|
||||
}
|
||||
|
||||
|
||||
M25PDevice::M25PDevice(M25PDeviceImpl* pImpl)
|
||||
: _pImpl(pImpl)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
string M25PDevice::GetDeviceName(void) const
|
||||
{
|
||||
return _pImpl->GetDeviceName();
|
||||
}
|
||||
|
||||
int M25PDevice::GetCapacity(void) const
|
||||
{
|
||||
return _pImpl->GetCapacity();
|
||||
}
|
||||
|
||||
void M25PDevice::ChipErase(void)
|
||||
{
|
||||
_pImpl->BulkErase();
|
||||
}
|
||||
|
||||
int M25PDevice::Read(int address, void* buffer, int length)
|
||||
{
|
||||
return _pImpl->Read(address, buffer, length);
|
||||
}
|
||||
|
||||
int M25PDevice::Write(int address, const void* buffer, int length)
|
||||
{
|
||||
return _pImpl->Write(address, buffer, length);
|
||||
}
|
25
src/drivers/SerialFlash/M25PDevice.h
Normal file
25
src/drivers/SerialFlash/M25PDevice.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "ISerialFlashDevice.h"
|
||||
|
||||
class ISPICommand;
|
||||
class M25PDeviceImpl;
|
||||
|
||||
class M25PDevice : public ISerialFlashDevice
|
||||
{
|
||||
public:
|
||||
static M25PDevice* Create(ISPICommand* pSPICommand);
|
||||
static bool IsSupported(ISPICommand* pSPICommand);
|
||||
|
||||
virtual std::string GetDeviceName(void) const;
|
||||
virtual int GetCapacity(void) const;
|
||||
virtual void ChipErase(void);
|
||||
virtual int Read(int address, void* buffer, int length);
|
||||
virtual int Write(int address, const void* buffer, int length);
|
||||
|
||||
private:
|
||||
std::auto_ptr<M25PDeviceImpl> _pImpl;
|
||||
M25PDevice(M25PDeviceImpl* pImpl);
|
||||
};
|
235
src/drivers/SerialFlash/M25PDeviceImpl.cpp
Normal file
235
src/drivers/SerialFlash/M25PDeviceImpl.cpp
Normal file
@ -0,0 +1,235 @@
|
||||
#include "ISPICommand.h"
|
||||
#include <string.h>
|
||||
#include "M25PDeviceImpl.h"
|
||||
|
||||
const M25PDeviceImpl::DeviceProperty* M25PDeviceImpl::findMatchDevice(ISPICommand* pSPICommand)
|
||||
{
|
||||
int manufacturerId;
|
||||
int memoryType;
|
||||
int memoryCapacity;
|
||||
readId(pSPICommand, manufacturerId, memoryType, memoryCapacity);
|
||||
|
||||
struct SupportedDevice
|
||||
{
|
||||
int manufacturerId;
|
||||
int memoryType;
|
||||
int memoryCapacity;
|
||||
DeviceProperty property;
|
||||
} static const supportedDevices[] =
|
||||
{
|
||||
//M25P16 16MBit (2MB)
|
||||
{ 0x20, 0x20, 0x15,
|
||||
{
|
||||
"M25P16",
|
||||
16384 * 1024 / 8,
|
||||
0xe3,
|
||||
},
|
||||
},
|
||||
//M25P80 8MBit (1MB)
|
||||
{ 0x20, 0x20, 0x14,
|
||||
{
|
||||
"M25P80",
|
||||
8192 * 1024 / 8,
|
||||
0xe3,
|
||||
},
|
||||
},
|
||||
};
|
||||
int count = sizeof(supportedDevices) / sizeof(supportedDevices[0]);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
const SupportedDevice& device = supportedDevices[i];
|
||||
if (device.manufacturerId == manufacturerId && device.memoryType == memoryType && device.memoryCapacity == memoryCapacity)
|
||||
{
|
||||
return &device.property;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool M25PDeviceImpl::IsSupported(ISPICommand* pSPICommand)
|
||||
{
|
||||
return findMatchDevice(pSPICommand) != NULL;
|
||||
}
|
||||
|
||||
M25PDeviceImpl* M25PDeviceImpl::Create(ISPICommand* pSPICommand)
|
||||
{
|
||||
const DeviceProperty* property = findMatchDevice(pSPICommand);
|
||||
if (property == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return new M25PDeviceImpl(pSPICommand, *property);
|
||||
}
|
||||
|
||||
M25PDeviceImpl::M25PDeviceImpl(ISPICommand* pSPICommand, const DeviceProperty& property)
|
||||
: _pSPICommand(pSPICommand)
|
||||
, _property(property)
|
||||
{
|
||||
clearBlockProtection();
|
||||
}
|
||||
|
||||
M25PDeviceImpl::~M25PDeviceImpl(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void M25PDeviceImpl::readId(ISPICommand* pSPICommand, int& manufacturerId, int& memoryType, int& memoryCapacity)
|
||||
{
|
||||
const static int DefaultOperationFrequency = 20000000;
|
||||
pSPICommand->SetMaxFrequency(DefaultOperationFrequency);
|
||||
|
||||
unsigned char id[3];
|
||||
pSPICommand->Read(0x9f, id, sizeof(id));
|
||||
manufacturerId = id[0];
|
||||
memoryType = id[1];
|
||||
memoryCapacity = id[2];
|
||||
}
|
||||
|
||||
int M25PDeviceImpl::readStatusRegister(void)
|
||||
{
|
||||
unsigned char status;
|
||||
_pSPICommand->Read(0x05, &status, 1);
|
||||
return status;
|
||||
}
|
||||
|
||||
void M25PDeviceImpl::writeStatusRegister(int value)
|
||||
{
|
||||
char vb = static_cast<char>(value);
|
||||
_pSPICommand->Write(0x01, &vb, 1);
|
||||
}
|
||||
|
||||
void M25PDeviceImpl::writeEnable()
|
||||
{
|
||||
_pSPICommand->Write(0x06);
|
||||
}
|
||||
|
||||
void M25PDeviceImpl::writeDisable()
|
||||
{
|
||||
_pSPICommand->Write(0x04);
|
||||
}
|
||||
|
||||
void M25PDeviceImpl::clearBlockProtection(void)
|
||||
{
|
||||
writeEnable();
|
||||
|
||||
int status = readStatusRegister();
|
||||
status &= _property.blockProtectionMask;
|
||||
writeStatusRegister(status);
|
||||
}
|
||||
|
||||
std::string M25PDeviceImpl::GetDeviceName(void) const
|
||||
{
|
||||
return _property.deviceName;
|
||||
}
|
||||
|
||||
int M25PDeviceImpl::GetCapacity(void) const
|
||||
{
|
||||
return _property.capacity;
|
||||
}
|
||||
|
||||
int M25PDeviceImpl::Read(int address, void* buffer, int length)
|
||||
{
|
||||
if (address >= GetCapacity())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (address + length > GetCapacity())
|
||||
{
|
||||
length = GetCapacity() - address;
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
char param[] = { 0, 0, 0, 0xff };
|
||||
fillAddress(param, address);
|
||||
_pSPICommand->Read(0x0b, param, sizeof(param), buffer, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
void M25PDeviceImpl::fillAddress(char* pBuffer, int address)
|
||||
{
|
||||
*(pBuffer + 0) = (address & 0xff0000) >> 16;
|
||||
*(pBuffer + 1) = (address & 0x00ff00) >> 8;
|
||||
*(pBuffer + 2) = (address & 0x0000ff);
|
||||
}
|
||||
|
||||
int M25PDeviceImpl::Write(int address, const void* buffer, int length)
|
||||
{
|
||||
if (address >= GetCapacity())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (address + length > GetCapacity())
|
||||
{
|
||||
length = GetCapacity() - address;
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int result = length;
|
||||
|
||||
const int pageSize = 256;
|
||||
const int pageSizeMask = pageSize - 1;
|
||||
const int pageAddressMask = ~pageSizeMask;
|
||||
|
||||
const unsigned char* p = static_cast<const unsigned char*>(buffer);
|
||||
if ((address & pageSizeMask) != 0)
|
||||
{
|
||||
int readLen = address & pageSizeMask;
|
||||
int copyLen = pageSize - readLen;
|
||||
if (copyLen > length)
|
||||
{
|
||||
copyLen = length;
|
||||
}
|
||||
char buf[pageSize];
|
||||
int writeAddress = address & pageAddressMask;
|
||||
Read(writeAddress, buf, readLen);
|
||||
memcpy(&buf[address & pageSizeMask], buffer, copyLen);
|
||||
pageProgram(writeAddress, buf);
|
||||
p += readLen;
|
||||
address += pageSize - readLen;
|
||||
length -= copyLen;
|
||||
}
|
||||
while (length >= pageSize)
|
||||
{
|
||||
pageProgram(address, p);
|
||||
address += pageSize;
|
||||
p += pageSize;
|
||||
length -= pageSize;
|
||||
}
|
||||
if (length != 0)
|
||||
{
|
||||
char buf[pageSize];
|
||||
memcpy(buf, p, length);
|
||||
memset(&buf[length], 0xff, pageSize - length);
|
||||
pageProgram(address, buf);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void M25PDeviceImpl::BulkErase()
|
||||
{
|
||||
writeEnable();
|
||||
|
||||
_pSPICommand->Write(0xc7);
|
||||
|
||||
while (readStatusRegister() & 0x01);
|
||||
|
||||
clearBlockProtection();
|
||||
}
|
||||
|
||||
void M25PDeviceImpl::pageProgram(int address, const void* buffer)
|
||||
{
|
||||
writeEnable();
|
||||
|
||||
char param[256 + 3];
|
||||
fillAddress(param, address);
|
||||
memcpy(param + 3, buffer, 256);
|
||||
_pSPICommand->Write(0x02, param, sizeof(param));
|
||||
|
||||
while (readStatusRegister() & 0x01);
|
||||
}
|
41
src/drivers/SerialFlash/M25PDeviceImpl.h
Normal file
41
src/drivers/SerialFlash/M25PDeviceImpl.h
Normal file
@ -0,0 +1,41 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class ISPICommand;
|
||||
|
||||
class M25PDeviceImpl
|
||||
{
|
||||
public:
|
||||
static bool IsSupported(ISPICommand* pSPICommand);
|
||||
static M25PDeviceImpl* Create(ISPICommand* pSPICommand);
|
||||
~M25PDeviceImpl(void);
|
||||
|
||||
std::string GetDeviceName() const;
|
||||
int GetCapacity() const;
|
||||
int Read(int address, void* buffer, int length);
|
||||
int Write(int address, const void* buffer, int length);
|
||||
void BulkErase(void);
|
||||
|
||||
private:
|
||||
ISPICommand* _pSPICommand;
|
||||
int _operationFrequency;
|
||||
struct DeviceProperty
|
||||
{
|
||||
std::string deviceName;
|
||||
int capacity;
|
||||
int blockProtectionMask;
|
||||
} const& _property;
|
||||
static const DeviceProperty* findMatchDevice(ISPICommand* pSPI);
|
||||
|
||||
M25PDeviceImpl(ISPICommand* pSPICommand, const DeviceProperty& property);
|
||||
static void readId(ISPICommand* pSPICommand, int& manufacturerId, int& memoryType, int& memoryCapacity);
|
||||
static void fillAddress(char* pBuffer, int address);
|
||||
|
||||
int readStatusRegister(void);
|
||||
void clearBlockProtection(void);
|
||||
void writeStatusRegister(int value);
|
||||
void writeEnable(void);
|
||||
void writeDisable(void);
|
||||
void pageProgram(int address, const void* buffer);
|
||||
};
|
71
src/drivers/SerialFlash/MbedSPICommand.cpp
Normal file
71
src/drivers/SerialFlash/MbedSPICommand.cpp
Normal file
@ -0,0 +1,71 @@
|
||||
#include "MbedSPICommand.h"
|
||||
|
||||
MbedSPICommand::MbedSPICommand(PinName mosi, PinName miso, PinName sck, PinName cs)
|
||||
: _spi(mosi, miso, sck)
|
||||
, _cs(cs)
|
||||
{
|
||||
//CS inactive
|
||||
_cs = 1;
|
||||
}
|
||||
|
||||
void MbedSPICommand::SetMaxFrequency(int frequency)
|
||||
{
|
||||
static const int LPC1768MaxFrequency = 46000000;
|
||||
|
||||
if (frequency > LPC1768MaxFrequency)
|
||||
{
|
||||
frequency = LPC1768MaxFrequency;
|
||||
}
|
||||
_spi.frequency(frequency);
|
||||
}
|
||||
|
||||
void MbedSPICommand::Write(int command)
|
||||
{
|
||||
_cs = 0;
|
||||
_spi.write(command);
|
||||
_cs = 1;
|
||||
}
|
||||
|
||||
void MbedSPICommand::Write(int command, const void* pParam, int length)
|
||||
{
|
||||
_cs = 0;
|
||||
_spi.write(command);
|
||||
|
||||
const char*p = static_cast<const char*>(pParam);
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
_spi.write(*p++);
|
||||
}
|
||||
_cs = 1;
|
||||
}
|
||||
|
||||
void MbedSPICommand::Read(int command, void* pReaded, int readLength)
|
||||
{
|
||||
_cs = 0;
|
||||
_spi.write(command);
|
||||
|
||||
char* pReadedByte = static_cast<char*>(pReaded);
|
||||
for (int i = 0; i < readLength; ++i)
|
||||
{
|
||||
*pReadedByte++ = _spi.write(0xff);
|
||||
}
|
||||
_cs = 1;
|
||||
}
|
||||
|
||||
void MbedSPICommand::Read(int command, const void* pParam, int paramLength, void* pReaded, int readLength)
|
||||
{
|
||||
_cs = 0;
|
||||
_spi.write(command);
|
||||
|
||||
const char* pParamByte = static_cast<const char*>(pParam);
|
||||
for (int i = 0; i < paramLength; ++i)
|
||||
{
|
||||
_spi.write(*pParamByte++);
|
||||
}
|
||||
char* pReadedByte = static_cast<char*>(pReaded);
|
||||
for (int i = 0; i < readLength; ++i)
|
||||
{
|
||||
*pReadedByte++ = _spi.write(0xff);
|
||||
}
|
||||
_cs = 1;
|
||||
}
|
22
src/drivers/SerialFlash/MbedSPICommand.h
Normal file
22
src/drivers/SerialFlash/MbedSPICommand.h
Normal file
@ -0,0 +1,22 @@
|
||||
#pragma once
|
||||
|
||||
#include "ISPICommand.h"
|
||||
#include "mbed.h"
|
||||
|
||||
class MbedSPICommand : public ISPICommand
|
||||
{
|
||||
public:
|
||||
MbedSPICommand(PinName mosi, PinName miso, PinName sck, PinName cs);
|
||||
|
||||
virtual void SetMaxFrequency(int frequency);
|
||||
|
||||
virtual void Write(int command);
|
||||
virtual void Write(int command, const void* pParam, int length);
|
||||
virtual void Read(int command, void* pReaded, int readLength);
|
||||
virtual void Read(int command, const void* pParam, int paramLength, void* pReaded, int readLength);
|
||||
|
||||
private:
|
||||
SPI _spi;
|
||||
DigitalOut _cs;
|
||||
};
|
||||
|
50
src/drivers/SerialFlash/SST25Device.cpp
Normal file
50
src/drivers/SerialFlash/SST25Device.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
#include "mbed.h"
|
||||
#include "SST25DeviceImpl.h"
|
||||
#include "SST25Device.h"
|
||||
|
||||
bool SST25Device::IsSupported(ISPICommand* pSPICommand)
|
||||
{
|
||||
return SST25DeviceImpl::IsSupported(pSPICommand);
|
||||
}
|
||||
|
||||
SST25Device* SST25Device::Create(ISPICommand* pSPICommand)
|
||||
{
|
||||
SST25DeviceImpl* pImpl = SST25DeviceImpl::Create(pSPICommand);
|
||||
if (pImpl == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return new SST25Device(pImpl);
|
||||
}
|
||||
|
||||
|
||||
SST25Device::SST25Device(SST25DeviceImpl* pImpl)
|
||||
: _pImpl(pImpl)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
string SST25Device::GetDeviceName(void) const
|
||||
{
|
||||
return _pImpl->GetDeviceName();
|
||||
}
|
||||
|
||||
int SST25Device::GetCapacity(void) const
|
||||
{
|
||||
return _pImpl->GetCapacity();
|
||||
}
|
||||
|
||||
void SST25Device::ChipErase(void)
|
||||
{
|
||||
_pImpl->ChipErase();
|
||||
}
|
||||
|
||||
int SST25Device::Read(int address, void* buffer, int length)
|
||||
{
|
||||
return _pImpl->Read(address, buffer, length);
|
||||
}
|
||||
|
||||
int SST25Device::Write(int address, const void* buffer, int length)
|
||||
{
|
||||
return _pImpl->Write(address, buffer, length);
|
||||
}
|
25
src/drivers/SerialFlash/SST25Device.h
Normal file
25
src/drivers/SerialFlash/SST25Device.h
Normal file
@ -0,0 +1,25 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include "ISerialFlashDevice.h"
|
||||
|
||||
class ISPICommand;
|
||||
class SST25DeviceImpl;
|
||||
|
||||
class SST25Device : public ISerialFlashDevice
|
||||
{
|
||||
public:
|
||||
static SST25Device* Create(ISPICommand* pSPICommand);
|
||||
static bool IsSupported(ISPICommand* pSPICommand);
|
||||
|
||||
virtual std::string GetDeviceName(void) const;
|
||||
virtual int GetCapacity(void) const;
|
||||
virtual void ChipErase(void);
|
||||
virtual int Read(int address, void* buffer, int length);
|
||||
virtual int Write(int address, const void* buffer, int length);
|
||||
|
||||
private:
|
||||
std::auto_ptr<SST25DeviceImpl> _pImpl;
|
||||
SST25Device(SST25DeviceImpl* pImpl);
|
||||
};
|
444
src/drivers/SerialFlash/SST25DeviceImpl.cpp
Normal file
444
src/drivers/SerialFlash/SST25DeviceImpl.cpp
Normal file
@ -0,0 +1,444 @@
|
||||
#include "ISPICommand.h"
|
||||
#include <string.h>
|
||||
#include "SST25DeviceImpl.h"
|
||||
|
||||
const SST25DeviceImpl::DeviceProperty* SST25DeviceImpl::findMatchDevice(ISPICommand* pSPICommand)
|
||||
{
|
||||
int manufacturersId;
|
||||
int deviceId;
|
||||
readId(pSPICommand, manufacturersId, deviceId);
|
||||
|
||||
struct SupportedDevice
|
||||
{
|
||||
int manufacturersId;
|
||||
int deviceId;
|
||||
DeviceProperty property;
|
||||
} static const supportedDevices[] =
|
||||
{
|
||||
//SST25xF512A 512KBit (64KByte)
|
||||
{ 0xbf, 0x48,
|
||||
{
|
||||
"SST25xF512A",
|
||||
512 * 1024 / 8,
|
||||
0xf3,
|
||||
1,
|
||||
&SST25DeviceImpl::writeAAI,
|
||||
33000000
|
||||
},
|
||||
},
|
||||
//SST25xF010A 1MBit (128KByte)
|
||||
{ 0xbf, 0x49,
|
||||
{
|
||||
"SST25xF010A",
|
||||
1024 * 1024 / 8,
|
||||
0xf3,
|
||||
1,
|
||||
&SST25DeviceImpl::writeAAI,
|
||||
33000000
|
||||
},
|
||||
},
|
||||
//SST25xF020A 2MBit (256KByte)
|
||||
{ 0xbf, 0x43,
|
||||
{
|
||||
"SST25xF020A",
|
||||
2048 * 1024 / 8,
|
||||
0xf3,
|
||||
1,
|
||||
&SST25DeviceImpl::writeAAI,
|
||||
33000000
|
||||
},
|
||||
},
|
||||
//SST25xF040B 4MBit (512KByte)
|
||||
{ 0xbf, 0x8d,
|
||||
{
|
||||
"SST25xF040B",
|
||||
4096 * 1024 / 8,
|
||||
0xe3,
|
||||
1,
|
||||
&SST25DeviceImpl::writeAAIWord,
|
||||
50000000
|
||||
},
|
||||
},
|
||||
//SST25xF080B 8MBit (1MByte)
|
||||
{ 0xbf, 0x8e,
|
||||
{
|
||||
"SST25xF080B",
|
||||
8192 * 1024 / 8,
|
||||
0xe3,
|
||||
1,
|
||||
&SST25DeviceImpl::writeAAIWord,
|
||||
50000000
|
||||
},
|
||||
},
|
||||
//SST25xF016B 16MBit (2MByte)
|
||||
{ 0xbf, 0x41,
|
||||
{
|
||||
"SST25xF016B",
|
||||
16384 * 1024 / 8,
|
||||
0xe3,
|
||||
1,
|
||||
&SST25DeviceImpl::writeAAIWord,
|
||||
50000000
|
||||
},
|
||||
},
|
||||
//SST25xF032B 32MBit (4MByte)
|
||||
{ 0xbf, 0x4a,
|
||||
{
|
||||
"SST25xF032B",
|
||||
32768 * 1024 / 8,
|
||||
0xc3,
|
||||
1,
|
||||
&SST25DeviceImpl::writeAAIWord,
|
||||
50000000
|
||||
},
|
||||
},
|
||||
//SST25xF064C 64MBit (8MByte)
|
||||
{ 0xbf, 0x4b,
|
||||
{
|
||||
"SST25xF064C",
|
||||
65536 * 1024 / 8,
|
||||
0xc3,
|
||||
256,
|
||||
&SST25DeviceImpl::writePage,
|
||||
50000000
|
||||
},
|
||||
}
|
||||
};
|
||||
int count = sizeof(supportedDevices) / sizeof(supportedDevices[0]);
|
||||
for (int i = 0; i < count; ++i)
|
||||
{
|
||||
const SupportedDevice& device = supportedDevices[i];
|
||||
if (device.manufacturersId == manufacturersId && device.deviceId == deviceId)
|
||||
{
|
||||
return &device.property;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool SST25DeviceImpl::IsSupported(ISPICommand* pSPICommand)
|
||||
{
|
||||
return findMatchDevice(pSPICommand) != NULL;
|
||||
}
|
||||
|
||||
SST25DeviceImpl* SST25DeviceImpl::Create(ISPICommand* pSPICommand)
|
||||
{
|
||||
const DeviceProperty* property = findMatchDevice(pSPICommand);
|
||||
if (property == NULL)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return new SST25DeviceImpl(pSPICommand, *property);
|
||||
}
|
||||
|
||||
SST25DeviceImpl::SST25DeviceImpl(ISPICommand* pSPICommand, const DeviceProperty& property)
|
||||
: _pSPICommand(pSPICommand)
|
||||
, _property(property)
|
||||
{
|
||||
_pSPICommand->SetMaxFrequency(_property.operationClkHz);
|
||||
}
|
||||
|
||||
SST25DeviceImpl::~SST25DeviceImpl(void)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::readId(ISPICommand* pSPICommand, int& manufacturersId, int& deviceId)
|
||||
{
|
||||
const static int DefaultOperationFrequency = 20000000;
|
||||
pSPICommand->SetMaxFrequency(DefaultOperationFrequency);
|
||||
|
||||
unsigned char read[2];
|
||||
char param[3];
|
||||
fillAddress(param, 0x000000);
|
||||
pSPICommand->Read(0x90, param, sizeof(param), read, sizeof(read));
|
||||
manufacturersId = read[0];
|
||||
deviceId = read[1];
|
||||
}
|
||||
|
||||
int SST25DeviceImpl::readStatusRegister(void)
|
||||
{
|
||||
unsigned char read;
|
||||
_pSPICommand->Read(0x05, &read, 1);
|
||||
return read;
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::writeStatusRegister(int value)
|
||||
{
|
||||
_pSPICommand->Write(0x50);
|
||||
|
||||
char vb = static_cast<char>(value);
|
||||
_pSPICommand->Write(0x01, &vb, sizeof(vb));
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::writeEnable()
|
||||
{
|
||||
int status = readStatusRegister();
|
||||
status &= _property.blockProtectionMask;
|
||||
writeStatusRegister(status);
|
||||
_pSPICommand->Write(0x06);
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::writeDisable()
|
||||
{
|
||||
_pSPICommand->Write(0x04);
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::waitForReady()
|
||||
{
|
||||
while (readStatusRegister() & 0x01)
|
||||
;
|
||||
}
|
||||
|
||||
std::string SST25DeviceImpl::GetDeviceName(void) const
|
||||
{
|
||||
return _property.deviceName;
|
||||
}
|
||||
|
||||
int SST25DeviceImpl::GetCapacity(void) const
|
||||
{
|
||||
return _property.capacity;
|
||||
}
|
||||
|
||||
int SST25DeviceImpl::Read(int address, void* buffer, int length)
|
||||
{
|
||||
if (address >= GetCapacity())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (address + length > GetCapacity())
|
||||
{
|
||||
length = GetCapacity() - address;
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
_pSPICommand->SetMaxFrequency(_property.operationClkHz);
|
||||
|
||||
char param[] = { 0, 0, 0, 0xff };
|
||||
fillAddress(param, address);
|
||||
_pSPICommand->Read(0x0b, param, sizeof(param), buffer, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
int SST25DeviceImpl::Write(int address, const void* buffer, int length)
|
||||
{
|
||||
if (address >= GetCapacity())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (address + length > GetCapacity())
|
||||
{
|
||||
length = GetCapacity() - address;
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
(this->*_property.pfnWriter)(address, buffer, length);
|
||||
return length;
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::ChipErase()
|
||||
{
|
||||
_pSPICommand->SetMaxFrequency(_property.operationClkHz);
|
||||
|
||||
writeEnable();
|
||||
_pSPICommand->Write(0x60);
|
||||
waitForReady();
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::byteProgram(int address, int value)
|
||||
{
|
||||
_pSPICommand->SetMaxFrequency(_property.operationClkHz);
|
||||
|
||||
writeEnable();
|
||||
|
||||
char param[] = { 0, 0, 0, value };
|
||||
fillAddress(param, address);
|
||||
_pSPICommand->Write(0x02, param, sizeof(param));
|
||||
|
||||
waitForReady();
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::beginAAIProgram(int address, int data)
|
||||
{
|
||||
_pSPICommand->SetMaxFrequency(_property.operationClkHz);
|
||||
|
||||
writeEnable();
|
||||
|
||||
char param[] = { 0, 0, 0, data };
|
||||
fillAddress(param, address);
|
||||
_pSPICommand->Write(0xaf, param, sizeof(param));
|
||||
|
||||
waitForReady();
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::nextAAIProgram(int data)
|
||||
{
|
||||
char param = static_cast<char>(data);
|
||||
_pSPICommand->Write(0xaf, ¶m, 1);
|
||||
|
||||
waitForReady();
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::endAAIProgram(void)
|
||||
{
|
||||
_pSPICommand->Write(0x04);
|
||||
waitForReady();
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::beginAAIWordProgram(int address, int data)
|
||||
{
|
||||
_pSPICommand->SetMaxFrequency(_property.operationClkHz);
|
||||
|
||||
writeEnable();
|
||||
char param[] = { 0, 0, 0, (data & 0xff00) >> 8, data & 0xff };
|
||||
fillAddress(param, address);
|
||||
_pSPICommand->Write(0xad, param, sizeof(param));
|
||||
waitForReady();
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::nextAAIWordProgram(int data)
|
||||
{
|
||||
char param[] = { (data & 0xff00) >> 8, data & 0xff };
|
||||
_pSPICommand->Write(0xad, param, sizeof(param));
|
||||
waitForReady();
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::fillAddress(char* pBuffer, int address)
|
||||
{
|
||||
*(pBuffer + 0) = (address & 0xff0000) >> 16;
|
||||
*(pBuffer + 1) = (address & 0x00ff00) >> 8;
|
||||
*(pBuffer + 2) = (address & 0x0000ff);
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::pageProgram(int address, const void* buffer)
|
||||
{
|
||||
_pSPICommand->SetMaxFrequency(_property.operationClkHz);
|
||||
|
||||
writeEnable();
|
||||
|
||||
char param[256 + 3];
|
||||
fillAddress(param, address);
|
||||
memcpy(param + 3, buffer, 256);
|
||||
_pSPICommand->Write(0x02, param, sizeof(param));
|
||||
|
||||
waitForReady();
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::writePage(int address, const void* buffer, int length)
|
||||
{
|
||||
int pageSize = _property.pageSize;
|
||||
int pageSizeMask = pageSize - 1;
|
||||
int pageAddressMask = ~pageSizeMask;
|
||||
|
||||
if (length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
const unsigned char* p = static_cast<const unsigned char*>(buffer);
|
||||
if ((address & pageSizeMask) != 0)
|
||||
{
|
||||
int readLen = address & pageSizeMask;
|
||||
int copyLen = pageSize - readLen;
|
||||
if (copyLen > length)
|
||||
{
|
||||
copyLen = length;
|
||||
}
|
||||
char buf[pageSize];
|
||||
int writeAddress = address & pageAddressMask;
|
||||
Read(writeAddress, buf, readLen);
|
||||
memcpy(&buf[address & pageSizeMask], buffer, copyLen);
|
||||
pageProgram(writeAddress, buf);
|
||||
p += readLen;
|
||||
address += pageSize - readLen;
|
||||
length -= copyLen;
|
||||
}
|
||||
while (length >= pageSize)
|
||||
{
|
||||
pageProgram(address, p);
|
||||
address += pageSize;
|
||||
p += pageSize;
|
||||
length -= pageSize;
|
||||
}
|
||||
if (length != 0)
|
||||
{
|
||||
char buf[pageSize];
|
||||
memcpy(buf, p, length);
|
||||
memset(&buf[length], 0xff, pageSize - length);
|
||||
pageProgram(address, buf);
|
||||
}
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::writeBytes(int address, const void* buffer, int length)
|
||||
{
|
||||
const unsigned char* p = static_cast<const unsigned char*>(buffer);
|
||||
while (length-- >= 0)
|
||||
{
|
||||
byteProgram(address++, *p++);
|
||||
}
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::writeAAI(int address, const void* buffer, int length)
|
||||
{
|
||||
if (length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned char* p = static_cast<const unsigned char*>(buffer);
|
||||
if (length < 2)
|
||||
{
|
||||
byteProgram(address, *p);
|
||||
return;
|
||||
}
|
||||
|
||||
beginAAIProgram(address, *p++);
|
||||
while (--length != 0)
|
||||
{
|
||||
nextAAIProgram(*p++);
|
||||
}
|
||||
endAAIProgram();
|
||||
}
|
||||
|
||||
void SST25DeviceImpl::writeAAIWord(int address, const void* buffer, int length)
|
||||
{
|
||||
if (length <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const unsigned char* p = static_cast<const unsigned char*>(buffer);
|
||||
if ((address & 0x1) != 0)
|
||||
{
|
||||
byteProgram(address++, *p++);
|
||||
length--;
|
||||
}
|
||||
|
||||
if (length < 4)
|
||||
{
|
||||
writeBytes(address, p, length);
|
||||
return;
|
||||
}
|
||||
|
||||
beginAAIWordProgram(address, (*p << 8) | *(p + 1));
|
||||
address += length & ~0x1;
|
||||
p += 2;
|
||||
length -= 2;
|
||||
|
||||
do
|
||||
{
|
||||
nextAAIWordProgram((*p << 8) | *(p + 1));
|
||||
p += 2;
|
||||
length -= 2;
|
||||
} while (length >= 2);
|
||||
endAAIProgram();
|
||||
|
||||
if (length != 0)
|
||||
{
|
||||
byteProgram(address, *p);
|
||||
}
|
||||
}
|
57
src/drivers/SerialFlash/SST25DeviceImpl.h
Normal file
57
src/drivers/SerialFlash/SST25DeviceImpl.h
Normal file
@ -0,0 +1,57 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
class ISPICommand;
|
||||
|
||||
class SST25DeviceImpl
|
||||
{
|
||||
public:
|
||||
static bool IsSupported(ISPICommand* pSPICommand);
|
||||
static SST25DeviceImpl* Create(ISPICommand* pSPICommand);
|
||||
~SST25DeviceImpl(void);
|
||||
|
||||
std::string GetDeviceName() const;
|
||||
int GetCapacity() const;
|
||||
int Read(int address, void* buffer, int length);
|
||||
int Write(int address, const void* buffer, int length);
|
||||
void ChipErase(void);
|
||||
|
||||
private:
|
||||
ISPICommand* const _pSPICommand;
|
||||
int _operationFrequency;
|
||||
struct DeviceProperty
|
||||
{
|
||||
std::string deviceName;
|
||||
int capacity;
|
||||
int blockProtectionMask;
|
||||
int pageSize;
|
||||
void (SST25DeviceImpl::*pfnWriter)(int address, const void* buffer, int length);
|
||||
|
||||
int operationClkHz;
|
||||
} const& _property;
|
||||
static const DeviceProperty* findMatchDevice(ISPICommand* pSPICommand);
|
||||
|
||||
SST25DeviceImpl(ISPICommand* pSPICommand, const DeviceProperty& property);
|
||||
static void readId(ISPICommand* pSPICommand, int& manufacturersId, int& deviceId);
|
||||
static void fillAddress(char* pBuffer, int address);
|
||||
|
||||
int readStatusRegister(void);
|
||||
void writeStatusRegister(int value);
|
||||
void writeEnable(void);
|
||||
void writeDisable(void);
|
||||
void waitForReady(void);
|
||||
|
||||
void byteProgram(int address, int value);
|
||||
void pageProgram(int address, const void* buffer);
|
||||
void beginAAIProgram(int address, int data);
|
||||
void nextAAIProgram(int data);
|
||||
void endAAIProgram(void);
|
||||
void beginAAIWordProgram(int address, int data);
|
||||
void nextAAIWordProgram(int data);
|
||||
|
||||
void writeBytes(int address, const void* buffer, int length);
|
||||
void writeAAI(int address, const void* buffer, int length);
|
||||
void writeAAIWord(int address, const void* buffer, int length);
|
||||
void writePage(int address, const void* buffer, int length);
|
||||
};
|
17
src/drivers/SerialFlash/SerialFlashDeviceCreator.cpp
Normal file
17
src/drivers/SerialFlash/SerialFlashDeviceCreator.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "ISerialFlashDevice.h"
|
||||
#include "SST25Device.h"
|
||||
#include "M25PDevice.h"
|
||||
#include "SerialFlashDeviceCreator.h"
|
||||
|
||||
ISerialFlashDevice* SerialFlashDeviceCreator::Create(ISPICommand* pSPICommand)
|
||||
{
|
||||
if (M25PDevice::IsSupported(pSPICommand))
|
||||
{
|
||||
return M25PDevice::Create(pSPICommand);
|
||||
}
|
||||
if (SST25Device::IsSupported(pSPICommand))
|
||||
{
|
||||
return SST25Device::Create(pSPICommand);
|
||||
}
|
||||
return NULL;
|
||||
}
|
11
src/drivers/SerialFlash/SerialFlashDeviceCreator.h
Normal file
11
src/drivers/SerialFlash/SerialFlashDeviceCreator.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "ISerialFlashDevice.h"
|
||||
|
||||
class ISPICommand;
|
||||
|
||||
class SerialFlashDeviceCreator
|
||||
{
|
||||
public:
|
||||
static ISerialFlashDevice* Create(ISPICommand* pSPICommand);
|
||||
};
|
189
src/drivers/SpiFlash25.cpp
Normal file
189
src/drivers/SpiFlash25.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
/* Library for SPI flash 25* devices.
|
||||
* Copyright (c) 2014 Multi-Tech Systems
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "SpiFlash25.h"
|
||||
|
||||
SpiFlash25::SpiFlash25(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName W, PinName HOLD, int page_size)
|
||||
: _spi(mosi, miso, sclk),
|
||||
_cs(cs),
|
||||
_page_size(page_size)
|
||||
{
|
||||
|
||||
wakeup();
|
||||
|
||||
_cs.write(1);
|
||||
_spi.format(8, 3);
|
||||
_spi.frequency(75000000);
|
||||
|
||||
if (W != NC) {
|
||||
_w = new DigitalOut(W);
|
||||
_w->write(1);
|
||||
}
|
||||
if (HOLD != NC) {
|
||||
_hold = new DigitalOut(HOLD);
|
||||
_hold->write(1);
|
||||
}
|
||||
|
||||
_cs.write(0);
|
||||
_spi.write(READ_IDENTIFICATION);
|
||||
_id[ID_MANUFACTURER] = _spi.write(0x00);
|
||||
_id[ID_MEM_TYPE] = _spi.write(0x00);
|
||||
_id[ID_MEM_SIZE] = _spi.write(0x00);
|
||||
_cs.write(1);
|
||||
|
||||
_mem_size = 1 << _id[ID_MEM_SIZE];
|
||||
}
|
||||
|
||||
void SpiFlash25::format(int bits, int mode) {
|
||||
_spi.format(bits, mode);
|
||||
}
|
||||
|
||||
void SpiFlash25::frequency(int hz) {
|
||||
_spi.frequency(hz);
|
||||
}
|
||||
|
||||
bool SpiFlash25::read(int addr, int len, char* data) {
|
||||
if (addr + len > _mem_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
enable_write();
|
||||
|
||||
_cs.write(0);
|
||||
_spi.write(READ_DATA);
|
||||
_spi.write(high_byte(addr));
|
||||
_spi.write(mid_byte(addr));
|
||||
_spi.write(low_byte(addr));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
data[i] = _spi.write(0x00);
|
||||
}
|
||||
|
||||
_cs.write(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SpiFlash25::write(int addr, int len, const char* data) {
|
||||
if (addr + len > _mem_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int written = 0;
|
||||
int write_size = 0;
|
||||
|
||||
while (written < len) {
|
||||
write_size = _page_size - ((addr + written) % _page_size);
|
||||
if (written + write_size > len) {
|
||||
write_size = len - written;
|
||||
}
|
||||
|
||||
if (! write_page(addr + written, write_size, data + written)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
written += write_size;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
char* SpiFlash25::read_id() {
|
||||
return _id;
|
||||
}
|
||||
|
||||
char SpiFlash25::read_status() {
|
||||
char status;
|
||||
|
||||
_cs.write(0);
|
||||
_spi.write(READ_STATUS);
|
||||
status = _spi.write(0x00);
|
||||
_cs.write(1);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void SpiFlash25::clear_sector(int addr) {
|
||||
enable_write();
|
||||
|
||||
_cs.write(0);
|
||||
_spi.write(SECTOR_ERASE);
|
||||
_spi.write(high_byte(addr));
|
||||
_spi.write(mid_byte(addr));
|
||||
_spi.write(low_byte(addr));
|
||||
_cs.write(1);
|
||||
|
||||
wait_for_write();
|
||||
}
|
||||
|
||||
void SpiFlash25::clear_mem() {
|
||||
enable_write();
|
||||
|
||||
_cs.write(0);
|
||||
_spi.write(BULK_ERASE);
|
||||
_cs.write(1);
|
||||
|
||||
wait_for_write();
|
||||
}
|
||||
|
||||
bool SpiFlash25::write_page(int addr, int len, const char* data) {
|
||||
enable_write();
|
||||
|
||||
_cs.write(0);
|
||||
_spi.write(PAGE_PROGRAM);
|
||||
_spi.write(high_byte(addr));
|
||||
_spi.write(mid_byte(addr));
|
||||
_spi.write(low_byte(addr));
|
||||
|
||||
for (int i = 0; i < len; i++) {
|
||||
_spi.write(data[i]);
|
||||
}
|
||||
|
||||
_cs.write(1);
|
||||
wait_for_write();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SpiFlash25::enable_write() {
|
||||
_cs.write(0);
|
||||
_spi.write(WRITE_ENABLE);
|
||||
_cs.write(1);
|
||||
}
|
||||
|
||||
void SpiFlash25::wait_for_write() {
|
||||
while (read_status() & STATUS_WIP) {
|
||||
wait_us(10);
|
||||
}
|
||||
}
|
||||
|
||||
void SpiFlash25::deep_power_down() {
|
||||
_cs.write(0);
|
||||
_spi.write(DEEP_POWER_DOWN);
|
||||
_cs.write(1);
|
||||
}
|
||||
|
||||
void SpiFlash25::wakeup() {
|
||||
_cs.write(0);
|
||||
_spi.write(DEEP_POWER_DOWN_RELEASE);
|
||||
_cs.write(1);
|
||||
}
|
107
src/drivers/SpiFlash25.h
Normal file
107
src/drivers/SpiFlash25.h
Normal file
@ -0,0 +1,107 @@
|
||||
/* Library for SPI flash 25* devices.
|
||||
* Copyright (c) 2014 Multi-Tech Systems
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#ifndef SPIFLASH25_H
|
||||
#define SPIFLASH25_H
|
||||
|
||||
#include "mbed.h"
|
||||
|
||||
class SpiFlash25 {
|
||||
public:
|
||||
SpiFlash25(PinName mosi, PinName miso, PinName sclk, PinName cs, PinName W = NC, PinName HOLD = NC, int page_size = 256);
|
||||
|
||||
/* Set the page size (default 256) */
|
||||
void set_page_size(int size);
|
||||
|
||||
/* Set up the internal SPI object */
|
||||
void format(int bits, int mode);
|
||||
void frequency(int hz);
|
||||
|
||||
/* Reads and writes can be across page boundaries */
|
||||
bool read(int addr, int len, char* data);
|
||||
bool write(int addr, int len, const char* data);
|
||||
|
||||
/* Read ID and status registers */
|
||||
char* read_id();
|
||||
char read_status();
|
||||
|
||||
/* Erase methods */
|
||||
void clear_sector(int addr);
|
||||
void clear_mem();
|
||||
|
||||
void deep_power_down();
|
||||
void wakeup();
|
||||
|
||||
private:
|
||||
enum {
|
||||
WRITE_ENABLE = 0x06,
|
||||
WRITE_DISABLE = 0x04,
|
||||
READ_IDENTIFICATION = 0x9F,
|
||||
READ_STATUS = 0x05,
|
||||
WRITE_STATUS = 0x01,
|
||||
READ_DATA = 0x03,
|
||||
READ_DATA_FAST = 0x0B,
|
||||
PAGE_PROGRAM = 0x02,
|
||||
SECTOR_ERASE = 0xD8,
|
||||
BULK_ERASE = 0xC7,
|
||||
DEEP_POWER_DOWN = 0xB9,
|
||||
DEEP_POWER_DOWN_RELEASE = 0xAB,
|
||||
};
|
||||
|
||||
enum {
|
||||
STATUS_SRWD = 0x80, // 0b 1000 0000
|
||||
STATUS_BP2 = 0x10, // 0b 0001 0000
|
||||
STATUS_BP1 = 0x08, // 0b 0000 1000
|
||||
STATUS_BP0 = 0x04, // 0b 0000 0100
|
||||
STATUS_WEL = 0x02, // 0b 0000 0010
|
||||
STATUS_WIP = 0x01, // 0b 0000 0001
|
||||
};
|
||||
|
||||
enum {
|
||||
ID_MANUFACTURER = 0,
|
||||
ID_MEM_TYPE = 1,
|
||||
ID_MEM_SIZE = 2,
|
||||
};
|
||||
|
||||
bool write_page(int addr, int len, const char* data);
|
||||
void enable_write();
|
||||
void wait_for_write();
|
||||
|
||||
static inline char high_byte(int addr) {
|
||||
return ((addr & 0xff0000) >> 16);
|
||||
}
|
||||
static inline char mid_byte(int addr) {
|
||||
return ((addr & 0xff00) >> 8);
|
||||
}
|
||||
static inline char low_byte(int addr) {
|
||||
return (addr & 0xff);
|
||||
}
|
||||
|
||||
SPI _spi;
|
||||
DigitalOut _cs;
|
||||
DigitalOut* _w;
|
||||
DigitalOut* _hold;
|
||||
int _mem_size;
|
||||
int _page_size;
|
||||
char _id[3];
|
||||
};
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user