Linux, spidev: why it shouldn't be directly in devicetree? - linux-device-driver

I want to define a SPI device with usermode access, as explained for example in http://linux-sunxi.org/SPIdev
Following these examples, I added in the devicetree this :
&ecspi1 {
.... other stuff ...
mydev#0 {
compatible = "spidev";
spi-max-frequency = <5000000>;
reg = <2>; /*chipselect*/
};
};
The platform is i.MX6. ecspi1 seems to be their SPI controller.
Then I indeed get /dev/spi0.2 and /sys/class/spidev/spidev0.2
But in kernel trace there's a WARNING saying this:
spidev spi0.2: buggy DT: spidev listed directly in DT
So how else the spidev should be described? What is the right syntax?

spidev: why it shouldn't be directly in devicetree?
The Device Tree should describe the board's hardware, but
spidev does not describe/identify any hardware.
Mark Brown wrote:
Since spidev is a detail of how Linux controls a device rather than a
description of the hardware in the system we should never have a node
described as "spidev" in DT, any SPI device could be a spidev so this
is just not a useful description.
The rationale and workaround for this kernel patch is https://patchwork.kernel.org/patch/6113191/
So how else the spidev should be described? What is the right syntax?
Instead of explicit use of spidev in your Device Tree source, you instead need to identify the actual device that you're controlling, e.g.
mydev#0 {
- compatible = "spidev";
+ compatible = "my_spi_device";
spi-max-frequency = <5000000>;
Then (as Geert Uytterhoeven explains), modify drivers/spi/spidev.c in the kernel source code by adding the compatible value for your device to the spidev_dt_ids[] array
static const struct of_device_id spidev_dt_ids[] = {
{ .compatible = "rohm,dh2228fv" },
{ .compatible = "lineartechnology,ltc2488" },
{ .compatible = "ge,achc" },
{ .compatible = "semtech,sx1301" },
+ { .compatible = "my_spi_device" },
{},
}
An alternate solution, which involves a quick-n-dirty change to just the Device Tree, is suggested by this article.
Simply replace the "spidev" compatible string with a proper string that already does exist:
mydev#0 {
- compatible = "spidev";
+ compatible = "rohm,dh2228fv"; /* actually spidev for my_spi_dev */
spi-max-frequency = <5000000>;
Since "rohm,dh2228fv" is already in the spidev_dt_ids[] list, no edit to drivers/spi/spidev.c is needed.

To avoid this issue just use linux,spidev instead of spidev:
&spi0 {
mydev#0 {
compatible = "linux,spidev";
};
};

Related

creating a Vulkan Instance in version 1.3.224 doesn't work and results in an Error VK_ERROR_INCOMPATIBLE_DRIVER on Apple M1

Hello I am new to Vulkan and c++.
I am following a tutorial on Vulkan for Visual-Studio and stuck at creating a Vulkan Instance. Through a lot of googling I found out that the error -9 (VK_ERROR_INCOMPATIBLE_DRIVER) is based on the Vulkan Version 1.3, me using MacOS and how it implements the MoltenVk or must be included or something else like that.
There it got too complicated for me.
I have tried to search through the documentations and now I don't really even understand what MoltenVk is or how I even tried to include Vulkan with cmake in my project.
I may found a solution on [here] and on stackoverflow (Why does vkCreateInstance return "VK_ERROR_INCOMPATIBLE_DRIVER" on MacOS despite vulkan supported GPU and up to date driver?) but I am not able to implement/understand it. Maybe somebody can explain it or tell me how to implement it?.
The Site said I should put something like that in
std::vector<const char*>
extNames.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
extNames.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
VkInstanceCreateInfo inst_info = {};
ins_info.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
inst_info.enabledExtensionCount = static_cast<uint32_t>(extNames.size());
inst_info.ppEnabledExtensionNames = extNames.data();
I have tried it that way and with some deviations in the vk_device.cpp file without any success.
My Project basically looks like this:
Project
--.vscode
--bin
->Project.exe
--build
--CMakeFiles
--libs
--SDL
--src
--Vk_base
->vk_base.h
->vk_device.cpp
->main.cpp
->CMakeList.txt
vk_base.h:
#include <vulkan/vulkan.h>
struct VulkanContext {
VkInstance instance;
};
VulkanContext* initVulkan();
vk_device.cpp:
#include "vk_base.h"
#include <iostream>
bool initVulkanInstance(VulkanContext* context) {
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "Vulkan App";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "No Engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_VERSION_1_0;
VkInstanceCreateInfo createInfo = {VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
createInfo.pApplicationInfo = &appInfo;
createInfo.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; // MAC OS SPECIFIC
//createInfo.enabledExtensionCount = static_cast<uint32_t>(extNames.size()); // MAC OS SPECIFIC
//createInfo.ppEnabledExtensionNames = extNames.data(); // MAC OS SPECIFIC
VkResult creation_result = vkCreateInstance(&createInfo, 0, &context->instance);
std::cout << creation_result << std::endl;
return true;
}
VulkanContext* initVulkan() {
VulkanContext* context = new VulkanContext();
if (initVulkanInstance(context) != VK_SUCCESS) {
throw std::runtime_error("failed to create instance!");
}
return context;
}
main.cpp
#include <iostream>
#include <SDL.h>
#include "vk_base/vk_base.h"
bool handleMessage() {
SDL_Event event;
while (SDL_PollEvent(&event)) {
switch (event.type) {
case SDL_QUIT:
return false;
case SDL_KEYDOWN:
if (event.key.keysym.sym == SDLK_ESCAPE) {
return false;
}
break;
}
}
}
int main() {
SDL_Init(SDL_INIT_VIDEO);
SDL_Window *_window;
_window = SDL_CreateWindow("Game Engine", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 700, 500, SDL_WINDOW_RESIZABLE);
SDL_Event e;
bool quit = false;
while (!quit) {
while(SDL_PollEvent(&e) != 0){
if(e.type == SDL_QUIT){
quit = true;
}
}
}
VulkanContext* context = initVulkan();
SDL_DestroyWindow(_window);
SDL_Quit();
return 0;
}
and my Cmakefile to create the Project in the build folder with the command : cmake ../ -GXcode ( I have tried it with -GXcode and without)
CMakeLists.txt:
#project name
project(Project)
set(CMAKE_CXX_STANDARD 11)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin")
set(SOURCE_FILES src/main.cpp src/vk_base/vk_device.cpp)
#find SDL2
add_subdirectory(libs/SDL)
#find Vulkan
find_package(Vulkan REQUIRED)
#vulkan executebl
add_executable(Project ${SOURCE_FILES})
target_include_directories(Project PUBLIC libs/SDL/include)
target_link_libraries(Project PUBLIC SDL2-static)
target_link_libraries(Project PUBLIC Vulkan::Vulkan)
target_link_libraries(Project PUBLIC ${Vulkan_LIBRARIES})
Thank you for reading and sry for my bad english:)
if there are misspellings in it, it is probably not the cause of the problem and just a typo from me on this thread :p
if you are not comfortable with C++, steer away from Vulkan. One will teach you exactly the wrong lessons about the other. Vulkan is low-level API, and shows you how you should not be programming C++ in 98 % of the time.
Apple does not support Vulkan. They have their own API Metal. MoltenVK is a library that attempts to translate Vulkan function calls to Metal calls.
This API translation does not match 1:1, so the MoltenVK wrapper is not a conformant Vulkan implementation. You need to waive that it is not conformant, and that you know of and will avoid triggering the limitations:
https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#VK_KHR_portability_subset
https://github.com/KhronosGroup/MoltenVK/blob/master/Docs/MoltenVK_Runtime_UserGuide.md#known-moltenvk-limitations
You waive the Vulkan conformance by adding VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR bit to the Instance creation:
#ifdef __APPLE__
createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
#endif //__APPLE__
VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR is part of a Vulkan extension, so that extension needs to be enabled before using the bit, per standard Vulkan rules:
#ifdef __APPLE__
extNames.push_back( VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME );
#endif //__APPLE__
createInfo.enabledExtensionCount = static_cast<uint32_t>(extNames.size());
createInfo.ppEnabledExtensionNames = extNames.data();

spidev to read eeprom id

OS: Linux
I'm writing a spidev application in userspace to read EEPROM id. I have my device tree entry as following:
spi0: spi#ffda4000 {
compatible = "snps,dw-apb-ssi";
#address-cells = <1>;
#size-cells = <0>;
reg = <0xffda4000 0x100>;
interrupt-parent = <&intc>;
interrupts = <GIC_SPI 101 IRQ_TYPE_LEVEL_HIGH>;
num-cs = <2>;
cs-gpios = <&porta 7 GPIO_ACTIVE_HIGH>, <&porta 0 GPIO_ACTIVE_HIGH>;
bus-num = <0>;
tx-dma-channel = <&pdma 16>;
rx-dma-channel = <&pdma 17>;
clocks = <&spi_m_clk>;
status = "disabled";
};
and then:
&spi0 {
status = "okay";
m25p10_spi#0 {
compatible = "m25p10";
reg = <0>; /* chip select */
spi-max-frequency = <20000000>;
/* m25p,fast-read; */
enable-dma = <0>;
};
spidev#0 {
compatible = "rohm,dh2228fv";
reg = <0>; /* chip select */
spi-max-frequency = <20000000>;
enable-dma = <0>;
};
};
Idea is to have spidev at same node as m25p10 so that when user space application open handle to "/dev/spidev0.0", it is actually talking to m25p10. But I can't get linux boot up. Is there anything wrong with this approach?
This will not work. One device - one definition in DT.
Also, why do you need to have spidev device? You already have m25p10_spi, which should show up as MTD device (something like /dev/mtd0), and there should be no problems accessing it from user space.
UPDATE:
It looks like OP wants to keep MTD and read device unique ID via RDID command, which is not supported by current m25p10 driver.
I might be wrong, but for me the easiest solution would be to extend the driver to create sysfs entry with RDID data, that is read during probing.
Some valuable resources:
LDD3
Linux kernel documentation

PCM5122 DAC with Android Things

I have a Raspberry Pi 3B and Suptronics X920 Expansion Board which uses PCM5122 DAC. So I'm having trouble playing sounds through that board.
The config file is default except for the display configuration part:
kernel=u-boot-dtok.bin
framebuffer_depth=16
# Prevent the firmware from loading HAT overlays now that we handle pin muxing.
# ourselves. See:
# https://www.raspberrypi.org/documentation/configuration/device-tree.md#part3.4
dtoverlay=
dtparam=i2c_arm=on
dtparam=spi=on
dtparam=audio=on
# pwm and I2S are mutually-exclusive since they share hardware clocks.
dtoverlay=pwm-2chan-with-clk,pin=18,func=2,pin2=13,func2=4
dtoverlay=generic-i2s
start_x=1
# Tell U-boot to always use the "serial0" interface for the console, which is
# set to whichever uart (uart0 or uart1) is set to the header pins. This doesn't
# interfere with the uart selected for Bluetooth.
dtoverlay=chosen-serial0
# Enable skip-init on the UART interfaces, so U-Boot doesn't attempt to
# re-initialize them.
dtoverlay=rpi-uart-skip-init
# Add pin devices to the system for use by the runtime pin configuration driver.
dtoverlay=runtimepinconfig
dtoverlay=uart1
dtoverlay=bcm2710-rpi-3-b-spi0-pin-reorder
# Tell the I2S driver to use the cprman clock.
dtoverlay=bcm2710-rpi-3-b-i2s-use-cprman
# Uncomment to disable serial port on headers, use GPIO14 and GPIO15
# as gpios and to allow the core_freq to change at runtime.
enable_uart=1
core_freq=400
# Support official RPi display.
dtoverlay=i2c-rtc,ds3231
dtoverlay=rpi-ft5406
hdmi_force_hotplug=1
# Set framebuffer to support RGBA colors.
framebuffer_swap=0
# Waveshare display settings
max_usb_current=1
hdmi_group=2
hdmi_mode=87
hdmi_cvt 1024 600 60 6 0 0 0
hdmi_drive=1
This is the code for playing a sound file:
fun playSound(file: File) {
val audioEncoding = AudioFormat.ENCODING_PCM_16BIT
val sampleRate = 16000
val audioOutputFormat = AudioFormat.Builder()
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.setEncoding(audioEncoding)
.setSampleRate(16000)
.build()
val audioOutputBufferSize = AudioTrack.getMinBufferSize(sampleRate, audioOutputFormat.channelMask, audioEncoding)
val audioOutputDevice = findAudioDevice(AudioManager.GET_DEVICES_OUTPUTS, AudioDeviceInfo.TYPE_BUS)
val audioTrack = AudioTrack.Builder()
.setAudioFormat(audioOutputFormat)
.setBufferSizeInBytes(audioOutputBufferSize)
.setTransferMode(AudioTrack.MODE_STREAM)
.build()
audioTrack.preferredDevice = audioOutputDevice
val buffer = ByteArray(audioOutputBufferSize)
audioTrack.play()
audioTrack.setVolume(1f)
val stream = file.inputStream().buffered()
try {
while (stream.read(buffer) > 0) {
val out = audioTrack.write(buffer, 0, buffer.size, AudioTrack.WRITE_BLOCKING)
d { "audioTrack.write = $out" }
}
} catch (error: Throwable) {
e(error) { "Error playing audio $file" }
} finally {
stream.close()
}
audioTrack.stop()
audioTrack.release()
}
private fun findAudioDevice(deviceFlag: Int, deviceType: Int): AudioDeviceInfo? {
val manager = getSystemService(Context.AUDIO_SERVICE) as AudioManager
val adis = manager.getDevices(deviceFlag)
for (adi in adis) {
if (adi.type == deviceType) {
return adi
}
}
return null
}
I've tested the code with a regular Raspberry Pi audio output (which is AudioDeviceInfo.TYPE_BUILTIN_SPEAKER) and it works ok. But with AudioDeviceInfo.TYPE_BUS it just produces no sound without any errors.
I tried various config options like dtoverlay=hifiberry or dtoverlay=hifiberry-dacplus with no luck.
Please help.
It looks like you might be using some of the code for the Google Assistant sample, and you are correct to assume that TYPE_BUS is what you need to enable the audio routes to use the I2S bus instead of the built-in audio jack.
However, that is likely not the whole story. The DAC likely requires additional configuration commands and/or external triggers. Looking at a similar HAT with the same DAC, for example, there is an I2C bus connection as well for DAC setup commands. Our Assistant sample uses the VoiceHAT driver to accomplish the additional triggering required by the DAC on that peripheral.
In Raspbian, the driver you enable via dtoverlay likely takes care of both pieces. Here, your code will need to manage the setup bits manually. Look at how the VoiceHAT driver is used in the Assistant sample as an example of this.
Also, make sure you are not enabling any of the I2S pins as either GPIO or PWM, as this will disable the audio route per the documentation.
Side Note: Android Things does not support making kernel changes via config.txt, so adding drivers there is expected not to have any effect.
It's been awhile since I figured this out, so I'm posting the code that working for me so that others spend less time buried in manuals.
After I've spent a few hours reading through the manual and frowning on the board's schematic, I figured out that the PCM5122 chip needs some preconfiguration.
It turns out that this chip has a complex clocking scheme. From the datasheet:
The serial audio interface typically has 4 connections: SCK (system master clock), BCK (bit clock), LRCK (left
right word clock), and DIN (data). The device has an internal PLL that is used to take either SCK or BCK and
create the higher rate clocks required by the interpolating processor and the DAC clock. This allows the device to
operate with or without an external SCK.
So, long story short, the chip's PLL operation depends on what pin is physically wired to the Raspberry board - SCK, BCK or both:
In my case it was BCK. We need to select PLL clock source with 13th register:
With all that explained, I'll post the full driver I've used with some additional configuration. All the information you can find in the linked manual. Hope it helps.
class SuptronicsX920AudioDevice private constructor(
private val busDevice: AudioDeviceInfo,
private val i2cDevice: I2cDevice) : AudioDevice {
private var audioTrack: AudioTrack? = null
private var leftVolume = 1f
private var rightVolume = 1f
companion object {
private const val ERROR_DETECT_REG = 37
private const val ERROR_DETECT_IDCM_BIT = 3
private const val PLL_SOURCE_REG = 13
private const val PLL_SOURCE_BCK_BIT = 4
private const val AUTO_MUTE_REG = 65
private const val DIGITAL_VOLUME_LEFT_REG = 61
private const val DIGITAL_VOLUME_RIGHT_REG = 62
fun create(busDevice: AudioDeviceInfo, i2cDevice: I2cDevice): Either<IOException, SuptronicsX920AudioDevice> {
return try {
// Ignore BCK\SCK missing errors as they turn device into Power down mode
riseRegBit(i2cDevice, ERROR_DETECT_REG, ERROR_DETECT_IDCM_BIT)
// Select BCK as the source for PLL
riseRegBit(i2cDevice, PLL_SOURCE_REG, PLL_SOURCE_BCK_BIT)
// Disable auto mute for both channels
i2cDevice.writeRegByte(AUTO_MUTE_REG, 0)
// Set the maximum gain for both channels
i2cDevice.writeRegByte(DIGITAL_VOLUME_LEFT_REG, 0)
i2cDevice.writeRegByte(DIGITAL_VOLUME_RIGHT_REG, 0)
SuptronicsX920AudioDevice(busDevice, i2cDevice).right()
} catch (ioe: IOException) {
e(ioe) { "Unable to configure PCM512x for Suptronics x920" }
ioe.left()
}
}
private fun riseRegBit(i2cDevice: I2cDevice, regAddress: Int, bitAddress: Int) {
val value = i2cDevice.readRegByte(regAddress)
i2cDevice.writeRegByte(regAddress, value or (1 shl bitAddress).toByte())
}
}
override fun play(stream: InputStream, audioFormat: AudioFormat) {
val audioOutputBufferSize = AudioTrack.getMinBufferSize(
audioFormat.sampleRate,
audioFormat.channelMask,
audioFormat.encoding)
val buffer = ByteArray(audioOutputBufferSize)
audioTrack = AudioTrack.Builder()
.setAudioFormat(audioFormat)
.setBufferSizeInBytes(audioOutputBufferSize)
.setTransferMode(AudioTrack.MODE_STREAM)
.build()
audioTrack?.apply {
preferredDevice = busDevice
setStereoVolume(leftVolume, rightVolume)
play()
var bytes = 0
try {
while (stream.read(buffer) > 0) {
bytes += write(buffer, 0, buffer.size, AudioTrack.WRITE_BLOCKING)
}
} catch (error: Throwable) {
e(error) { "Error playing audio" }
}
d { "$bytes of audio track written" }
}
stop()
audioTrack = null
}
override fun stop() {
audioTrack?.apply {
if (state != AudioTrack.STATE_UNINITIALIZED) {
try {
pause()
flush()
release()
d { "Audio stopped" }
} catch (error: Throwable) {
e(error) { "Can't stop track properly" }
}
}
}
}
override fun setVolume(leftVolume: Float, rightVolume: Float) {
this.leftVolume = leftVolume
this.rightVolume = rightVolume
audioTrack?.apply { setStereoVolume(leftVolume, rightVolume) }
}
override fun close() {
stop()
i2cDevice.close()
}
}

opensc-pkcs11 not getting any token

I am writing a sample program using opensc-pkcs11.so in redhat linux. This is for pure software implementation of AES encryption/decryption. I am not using for any card.
My program intilizes the cryptoki successfully but giving CKR_TOKEN_NOT_PRESENT error.
code snippet is given.
CK_FUNCTION_LIST_PTR pFunctionList;
CK_C_Initialize pC_Initialize;
CK_RV rv;
rv = C_GetFunctionList(&pFunctionList);
if(rv == CKR_OK)
pC_Initialize = pFunctionList -> C_Initialize;
rv = (*pC_Initialize)(NULL_PTR);
CK_ULONG ulSlotCount;
CK_SLOT_ID_PTR pSlotList;
CK_C_GetSlotList pC_GetSlotList;
pC_GetSlotList = pFunctionList -> C_GetSlotList;
rv = (*pC_GetSlotList)(CK_FALSE, NULL_PTR, &ulSlotCount);
/* Get list of all slots */
//rv = C_GetSlotList(FALSE, NULL_PTR, &ulSlotCount);
if (rv == CKR_OK)
{
cout<<"ulSlotCount="<<ulSlotCount<<endl;
pSlotList =
(CK_SLOT_ID_PTR)
malloc(ulSlotCount*sizeof(CK_SLOT_ID));
//rv = C_GetSlotList(FALSE, pSlotList, &ulSlotCount);
rv = (*pC_GetSlotList)(CK_FALSE, pSlotList, &ulSlotCount);
if (rv == CKR_OK)
{
/* Now use that list of all slots */
l_lSlotId = pSlotList[0];
cerr<<"lSlotId="<<l_lSlotId<<endl;
}
CK_SLOT_INFO slotInfo;
CK_TOKEN_INFO tokenInfo;
CK_C_GetSlotInfo pC_GetSlotInfo;
pC_GetSlotInfo = pFunctionList -> C_GetSlotInfo;
/* Get slot information for first slot */
rv = (*pC_GetSlotInfo)(pSlotList[0], &slotInfo);
fprintf(stderr, "pC_GetSlotInfo: rv = 0x%.8X\n", rv);
if(rv == CKR_OK)
{
/* Get token information for first slot */
cerr<<"pC_GetSlotInfo OK"<<endl;
CK_C_GetTokenInfo pC_GetTokenInfo;
pC_GetTokenInfo = pFunctionList -> C_GetTokenInfo;
rv = (*pC_GetTokenInfo)(pSlotList[0], &tokenInfo);
}
fprintf(stderr, "pC_GetTokenInfo: rv = 0x%.8X\n", rv);
if (rv == CKR_TOKEN_NOT_PRESENT)
{
cerr<<"CKR_TOKEN_NOT_PRESENT"<<endl;
}
free(pSlotList);
}
Can anybody give idea about what is happening? I believe opensc-pkcs11 can be used for just software implementation also.
Thanks in advance.
PKCS#11 library shipped with OpenSC acts "only as a driver" for a bunch of generally available cryptographic smart cards so unless you have a physical card reader connected to your computer it won't find any slots. If you are looking for a pure software PKCS#11 implementation then I believe you should pick one from my answer to your previous question. If none of them suits your need then maybe you could use some general purpose cryptographic library such as OpenSSL, GnuTLS or Botan.

Erasing page on stm32 fails with FLASH_ERROR_WRP

I am trying to erase one page in flash on an STM32F103RB like so:
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPRTERR | FLASH_FLAG_OPTERR);
FLASHStatus = FLASH_ErasePage(Page);
However, FLASH_ErasePage fails producing FLASH_ERROR_WRP
Manually enabling/disabling write protection in the stm32-linker tool doesn't fix the problem.
Basically FLASH_ErasePage fails with WRP error without trying to do anything if there's previous WRP error in the status register.
What comes to your FLASH_ClearFlag call, at least FLASH_FLAG_BSY will cause assert_param(IS_FLASH_CLEAR_FLAG(FLASH_FLAG)); to fail (though I'm not really sure what happens in this case).
#define IS_FLASH_CLEAR_FLAG(FLAG) ((((FLAG) & (uint32_t)0xFFFFC0FD) == 0x00000000) && ((FLAG) != 0x00000000))
What is your page address ? Which address are you trying to access ?
For instance, this example is tested on STM32F100C8 in terms of not only erasing but also writing data correctly.
http://www.ozturkibrahim.com/TR/eeprom-emulation-on-stm32/
If using the HAL driver, your code might look like this (cut'n paste from an real project)
static HAL_StatusTypeDef Erase_Main_Program ()
{
FLASH_EraseInitTypeDef ins;
uint32_t sectorerror;
ins.TypeErase = FLASH_TYPEERASE_SECTORS;
ins.Banks = FLASH_BANK_1; /* Do not care, used for mass-erase */
#warning We currently erase from sector 2 (only keep 64KB of flash for boot))
ins.Sector = FLASH_SECTOR_4;
ins.NbSectors = 4;
ins.VoltageRange = FLASH_VOLTAGE_RANGE_3; /* voltage-range defines how big blocks can be erased at the same time */
return HAL_FLASHEx_Erase (&ins, &sectorerror);
}
The internal function in the HAL driver that actually does the work
void FLASH_Erase_Sector(uint32_t Sector, uint8_t VoltageRange)
{
uint32_t tmp_psize = 0U;
/* Check the parameters */
assert_param(IS_FLASH_SECTOR(Sector));
assert_param(IS_VOLTAGERANGE(VoltageRange));
if(VoltageRange == FLASH_VOLTAGE_RANGE_1)
{
tmp_psize = FLASH_PSIZE_BYTE;
}
else if(VoltageRange == FLASH_VOLTAGE_RANGE_2)
{
tmp_psize = FLASH_PSIZE_HALF_WORD;
}
else if(VoltageRange == FLASH_VOLTAGE_RANGE_3)
{
tmp_psize = FLASH_PSIZE_WORD;
}
else
{
tmp_psize = FLASH_PSIZE_DOUBLE_WORD;
}
/* If the previous operation is completed, proceed to erase the sector */
CLEAR_BIT(FLASH->CR, FLASH_CR_PSIZE);
FLASH->CR |= tmp_psize;
CLEAR_BIT(FLASH->CR, FLASH_CR_SNB);
FLASH->CR |= FLASH_CR_SER | (Sector << POSITION_VAL(FLASH_CR_SNB));
FLASH->CR |= FLASH_CR_STRT;
}
Second thing to check. Is interrupts enabled, and is there any hardware access between the unlock call and the erase call?
I hope this helps