The Cortex-M0+ is an ultra-low power 32-bit ARM Cortex-M processor optimized for cost-sensitive and power-constrained embedded applications. One of its key features is the embedded flash memory or EEPROM that allows you to store and retrieve information even when the power is turned off.
Copying data to the EEPROM with Cortex M0+ can be useful for storing configuration parameters, sensor calibration values, device identifiers, and other permanent data you may need for your application. This article will provide a step-by-step guide on how to write a program to copy information into the EEPROM using the Cortex M0+ processor and its peripherals.
Prerequisites
Before we dive into the programming, let’s go over some prerequisites:
- A development board with a Cortex M0+ MCU like the STM32F0 Discovery kit
- A C compiler for ARM Cortex-M like GCC
- Basic knowledge of C programming
- Understanding of microcontroller peripherals like GPIO, USART, SPI, I2C etc
- Basics of embedded programming and toolchain setup
EEPROM Organization in Cortex M0+
The embedded flash memory in Cortex M0+ is organized into pages and sectors. A page is typically 1 KB while a sector can span multiple pages (16-256 KB). The smallest unit that can be erased is a sector. Before writing to a sector, it needs to be erased first.
The processor uses memory mapped I/O to access the flash memory. There are register mappings for flash memory control, status registers, address registers, data registers etc. By manipulating these registers, we can erase, program and access the embedded flash memory.
Steps to Copy Data to EEPROM
Here are the typical steps involved in copying information to the EEPROM with Cortex M0+:
- Include required header files like cmsis_gcc.h
- Define EEPROM base address and registers
- Define data to be written to EEPROM
- Unlock the Flash module for write access
- Erase the target sector
- Check that sector erase completed successfully
- Write data to the EEPROM page-by-page
- Verify data was written properly
- Lock the Flash module
1. Include Header Files
We need to include the CMSIS header file that contains definitions for the processor core, peripherals and device specific configuration.
#include "cmsis_gcc.h"
2. Define Flash Base Address and Registers
Define the base address for the flash memory module along with offsets to the various registers we need to access like control, status, address and data registers.
#define FLASH_BASE 0x08000000
#define FLASH_ACR *(volatile unsigned long*)(FLASH_BASE + 0x00)
#define FLASH_PECR *(volatile unsigned long*)(FLASH_BASE + 0x04)
#define FLASH_PDKEYR *(volatile unsigned long*)(FLASH_BASE + 0x08)
#define FLASH_PEKEYR *(volatile unsigned long*)(FLASH_BASE + 0x0C)
#define FLASH_PRGKEYR *(volatile unsigned long*)(FLASH_BASE + 0x10)
#define FLASH_OPTKEYR *(volatile unsigned long*)(FLASH_BASE + 0x14)
#define FLASH_SR *(volatile unsigned long*)(FLASH_BASE + 0x18)
#define FLASH_OPTR *(volatile unsigned long*)(FLASH_BASE + 0x20)
3. Define Data to be Written
Next, we define a constant array that will hold the actual data we want to write to the EEPROM.
const uint32_t data_to_write[] = {0x12345678, 0x23456789, 0xABCDEF01};
4. Unlock Flash for Write Access
The flash control registers are locked by default. We need to write the correct unlock sequence to the FLASH_PEKEYR and FLASH_PRGKEYR registers to enable writes.
FLASH_PEKEYR = 0x89ABCDEF; // Unlock PECR register
FLASH_PECR |= 1; // Enable FPRG access
FLASH_PRGKEYR = 0x8C9DAEBF; // Unlock program memory
FLASH_PRGKEYR = 0x8C9DAEBF; // Unlock again
5. Erase Target Sector
Before writing, we need to erase the target sector to 0xFFs. This is done by setting the MER and SER bits in the FLASH_PECR register along with the sector number.
FLASH_PECR |= FLASH_PECR_ERASE; // Set sector erase bit
FLASH_PECR |= 0x1 << 3; // Sector number
FLASH_PECR |= FLASH_PECR_STRT; // Trigger erase
// wait until erase completes
while(FLASH_SR & FLASH_SR_BSY) {}
// check for errors
if(FLASH_SR & FLASH_SR_EOP) {
// Erase completed successfully
} else {
// Error occurred
}
6. Write Data Page by Page
The programming is done on a page level granularity. So we write the data page by page inside a loop.
uint32_t *address = (uint32_t*)0x08010000; // Start address
for(int i=0; i<sizeof(data_to_write); i+=4) {
// Write 32-bit word to current address
*address++ = data_to_write[i/4];
// wait until write completes
while(FLASH_SR & FLASH_SR_BSY) {}
// check for errors
if(FLASH_SR & FLASH_SR_EOP) {
// programming completed successfully
} else {
// error occurred
}
}
7. Verify Data
Once the programming is done, we should always verify that the data was written correctly by reading it back and comparing it.
address = (uint32_t*)0x08010000;
for(int i=0; i<sizeof(data_to_write); i+=4) {
if(*address != data_to_write[i/4]) {
// data mismatch found
}
address++;
}
8. Lock Flash
Finally, we lock the flash control registers by writing the lock sequence.
FLASH_PECR |= 0x01;
FLASH_PRGKEYR = 0x8C9DAEBF; // Lock program memory
Example Code
Here is an example program to demonstrate all the steps:
#include "cmsis_gcc.h"
#define FLASH_BASE 0x08000000
#define FLASH_ACR *(volatile unsigned long*)(FLASH_BASE + 0x00)
#define FLASH_PECR *(volatile unsigned long*)(FLASH_BASE + 0x04)
#define FLASH_PDKEYR *(volatile unsigned long*)(FLASH_BASE + 0x08)
#define FLASH_PEKEYR *(volatile unsigned long*)(FLASH_BASE + 0x0C)
#define FLASH_PRGKEYR *(volatile unsigned long*)(FLASH_BASE + 0x10)
#define FLASH_OPTKEYR *(volatile unsigned long*)(FLASH_BASE + 0x14)
#define FLASH_SR *(volatile unsigned long*)(FLASH_BASE + 0x18)
#define FLASH_OPTR *(volatile unsigned long*)(FLASH_BASE + 0x20)
const uint32_t data_to_write[] = {0x12345678, 0x23456789, 0xABCDEF01};
int main(void) {
// Unlock sequence
FLASH_PEKEYR = 0x89ABCDEF;
FLASH_PECR |= 1;
FLASH_PRGKEYR = 0x8C9DAEBF;
FLASH_PRGKEYR = 0x8C9DAEBF;
// Sector erase
FLASH_PECR |= FLASH_PECR_ERASE;
FLASH_PECR |= 0x1 << 3; // Sector 0
FLASH_PECR |= FLASH_PECR_STRT;
while(FLASH_SR & FLASH_SR_BSY) {}
if(FLASH_SR & FLASH_SR_EOP) {
// Erase done
} else {
// Error
}
// Write data
uint32_t *address = (uint32_t*)0x08010000;
for(int i=0; i<sizeof(data_to_write); i+=4) {
*address++ = data_to_write[i/4];
while(FLASH_SR & FLASH_SR_BSY) {}
if(FLASH_SR & FLASH_SR_EOP) {
// Write done
} else {
// Error
}
}
// Verify data
address = (uint32_t*)0x08010000;
for(int i=0; i<sizeof(data_to_write); i+=4) {
if(*address != data_to_write[i/4]) {
// Data mismatch
}
address++;
}
// Lock sequence
FLASH_PECR |= 0x01;
FLASH_PRGKEYR = 0x8C9DAEBF;
return 0;
}
Summary
In this article, we went through the steps needed to copy information into the embedded EEPROM or flash memory on a Cortex M0+ microcontroller. The key steps are:
- Unlock flash control registers
- Erase target sector
- Write data page by page
- Verify data
- Lock flash registers
By following these steps and manipulating the flash memory mapped registers, you can easily store permanent data on Cortex M0+ devices. The program memory can be used for storing firmware code whereas the embedded EEPROM is ideal for non-volatile data.