本章简单介绍telink 825x flash、ram、clock、gpio相关的说明及操作。详细说明可以查看telink Developer handlebook。
1 MCU地址空间
1.1 Flash空间
8258 flash共512K,地址为0x0 - 0x7FFFF。
BLE sdk是三地址启动,分别为0x0,0x20000, 0x40000,即三个地址都可以作为起始地址。如果编译出来的firmware不超过0x20000大小,可以设置启动地址为0x0和0x20000,如果超过0x20000大小,需要设置启动地址为0和0x40000,这样ota升级的时候,会进行乒乓升级。
ota升级流程后门会详细说明。
1) flash操作说明
- flash操作有两种方式,直接访问地址和函数访问。
直接访问,例如:
u16 x=*(volatile u16*)0x10000; //读flash 0x10000 两个byte
u8 data[16];
memcpy(data, 0x20000,16); //读flash 0x20000 16 byte,copy到data中
flash 函数访问,例如:
unsigned char data[16];
flash_write_page(0x50000, 16, data);
flash_read_page(0x50000,16,data);
- flash 函数操作,读写是按page操作,擦除是按sector操作。
一个page为256 bytes,一个sector为4K。 读可以跨page,写不可以跨page,擦除一次最少擦除4K区域。
例如,
unsigned char data[32];
//错误,不能跨page写, 前16 byte在0x12000page上面,后门的在0x121000 page
flash_write_page( 0x120f0, 20, data);
读操作不受page的限制,可以任意读超过256 bytes的数据。
- flash读写擦除操作都会关闭中断,可能会影响蓝牙时序。
flash的函数操作,都会先关闭中断irq_disable(),操作完后,在恢复中断irq_restore。由于蓝牙收发包也是由中断控制的,因此,flash的操作会影响蓝牙收发包。
但是只要flash操作不占用太多的时间,就不会影响蓝牙时序。例如读写操作,只要不读写太大的数据,就不会影响蓝牙时序。但是擦除操作是几十ms级的,很大可能会影响蓝牙时序。因此,在连接状态下,不允许直接调用flash_erase_sector函数擦除flash,导致破坏蓝牙断开。如果必须要在连接的时候擦除flash,可以通过时序包含的方法来时序。这个方法后面会讲到。
- flash操作,推荐使用函数操作。
- 如果内置flash 512K不满足需求,也可以通过spi外挂flash。
2) flash操作函数
/**
* @brief This function serves to erase a sector.
* @param[in] addr the start address of the sector needs to erase.
* @return none
*/
_attribute_ram_code_ void flash_erase_sector(unsigned long addr);
/**
* @brief This function writes the buffer's content to a page.
* @param[in] addr the start address of the page
* @param[in] len the length(in byte) of content needs to write into the page
* @param[in] buf the start address of the content needs to write into
* @return none
*/
_attribute_ram_code_ void flash_write_page(unsigned long addr, unsigned long len, unsigned char *buf);
/**
* @brief This function reads the content from a page to the buf.
* @param[in] addr the start address of the page
* @param[in] len the length(in byte) of content needs to read out from the page
* @param[out] buf the start address of the buffer
* @return none
*/
_attribute_ram_code_ void flash_read_page(unsigned long addr, unsigned long len, unsigned char *buf);
3) flash函数重新封装(跨page和spi 外部flash)
重新封装的flash函数,可以跨page写数据,并且包含了spi 外置flash的实现。
#include "hal_typedef.h"
#define IRQ_DISABLE 1
#define WAIT_UNTIL_FINISHED \
do { \
reg_v = reg_spi_ctrl; \
} \
while (reg_v & FLD_SPI_BUSY)
_attribute_ram_code_ void exflash_erase_sector(u32 addr)
{
register u8 u8data;
register u8 reg_v;
#if IRQ_DISABLE
u8 r = irq_disable();
#endif
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN;
reg_spi_data = FLASH_WRITE_ENABLE_CMD; WAIT_UNTIL_FINISHED;
reg_spi_ctrl = FLD_SPI_CS | FLD_SPI_MASTER_MODE_EN;
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN;
reg_spi_data = FLASH_SECT_ERASE_CMD; WAIT_UNTIL_FINISHED;
reg_spi_data = addr >> 16; WAIT_UNTIL_FINISHED;
reg_spi_data = addr >> 8; WAIT_UNTIL_FINISHED;
reg_spi_data = addr; WAIT_UNTIL_FINISHED;
reg_spi_ctrl = FLD_SPI_CS | FLD_SPI_MASTER_MODE_EN;
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN;
reg_spi_data = FLASH_READ_STATUS_CMD; WAIT_UNTIL_FINISHED;
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN | FLD_SPI_DATA_OUT_DIS | FLD_SPI_RD;
u8data = reg_spi_data; WAIT_UNTIL_FINISHED;
do {u8data = reg_spi_data; WAIT_UNTIL_FINISHED;} while(u8data & 0x01);
reg_spi_ctrl = FLD_SPI_CS | FLD_SPI_MASTER_MODE_EN | FLD_SPI_DATA_OUT_DIS;
#if IRQ_DISABLE
irq_restore(r);
#endif
}
_attribute_ram_code_ void exflash_erase_chip(void)
{
register u8 u8data;
register u8 reg_v;
#if IRQ_DISABLE
u8 r = irq_disable();
#endif
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN;
reg_spi_data = FLASH_WRITE_ENABLE_CMD; WAIT_UNTIL_FINISHED;
reg_spi_ctrl = FLD_SPI_CS | FLD_SPI_MASTER_MODE_EN;
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN;
reg_spi_data = FLASH_CHIP_ERASE_CMD; WAIT_UNTIL_FINISHED;
reg_spi_ctrl = FLD_SPI_CS | FLD_SPI_MASTER_MODE_EN;
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN;
reg_spi_data = FLASH_READ_STATUS_CMD; WAIT_UNTIL_FINISHED;
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN | FLD_SPI_DATA_OUT_DIS | FLD_SPI_RD;
u8data = reg_spi_data; WAIT_UNTIL_FINISHED;
do {u8data = reg_spi_data; WAIT_UNTIL_FINISHED;} while(u8data & 0x01);
reg_spi_ctrl = FLD_SPI_CS | FLD_SPI_MASTER_MODE_EN | FLD_SPI_DATA_OUT_DIS;
#if IRQ_DISABLE
irq_restore(r);
#endif
}
_attribute_ram_code_ void exflash_write_page(u32 addr, u32 len, u8 *buf)
{
register u8 u8data;
register u8 reg_v;
register u32 i = 0;
#if IRQ_DISABLE
u8 r = irq_disable();
#endif
/* CS = low, enable master mode, enable SDO, enable write */
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN;
reg_spi_data = FLASH_WRITE_ENABLE_CMD; WAIT_UNTIL_FINISHED;
/* CS = high, enable master mode, enable SDO, enable write */
reg_spi_ctrl = FLD_SPI_CS | FLD_SPI_MASTER_MODE_EN;
/* CS = low, enable master mode, enable SDO, enable write */
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN;
reg_spi_data = FLASH_WRITE_CMD; WAIT_UNTIL_FINISHED;
reg_spi_data = addr >> 16; WAIT_UNTIL_FINISHED;
reg_spi_data = addr >> 8; WAIT_UNTIL_FINISHED;
reg_spi_data = addr; WAIT_UNTIL_FINISHED;
/* continue to send len bytes */
do {reg_spi_data = buf[i++]; WAIT_UNTIL_FINISHED;} while(--len);
/* CS = high, enable master mode, enable SDO, enable write */
reg_spi_ctrl = FLD_SPI_CS | FLD_SPI_MASTER_MODE_EN;
/* CS = low, enable master mode, enable SDO, enable write */
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN;
reg_spi_data = FLASH_READ_STATUS_CMD; WAIT_UNTIL_FINISHED;
/* CS is still low, enable master mode, disable SDO, enable read */
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN | FLD_SPI_DATA_OUT_DIS | FLD_SPI_RD;
/* dummy read in order to output 8 spi clock */
u8data = reg_spi_data; WAIT_UNTIL_FINISHED;
/* keep reading until bit 0 of STATUS register changed to 0 */
do {u8data = reg_spi_data; WAIT_UNTIL_FINISHED;} while(u8data & 0x01);
/* CS changed to high, enable master mode, disable SDO, enable read */
reg_spi_ctrl = FLD_SPI_CS | FLD_SPI_MASTER_MODE_EN | FLD_SPI_DATA_OUT_DIS;
#if IRQ_DISABLE
irq_restore(r);
#endif
}
_attribute_ram_code_ void exflash_read_page(u32 addr, u32 len, u8 *buf)
{
register u8 u8data;
register u8 reg_v;
register u32 i;
#if IRQ_DISABLE
u8 r = irq_disable();
#endif
// CS = low, enable master mode, enable SDO, enable write
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN;
reg_spi_data = FLASH_READ_CMD; WAIT_UNTIL_FINISHED;
reg_spi_data = addr >> 16; WAIT_UNTIL_FINISHED;
reg_spi_data = addr >> 8; WAIT_UNTIL_FINISHED;
reg_spi_data = addr >> 0; WAIT_UNTIL_FINISHED;
// CS = low, enable master mode, disable SDO, enable read
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN | FLD_SPI_DATA_OUT_DIS | FLD_SPI_RD;
// dummy read in order to output 8 spi clock
u8data = reg_spi_data; WAIT_UNTIL_FINISHED;
// continue to send len*8 spi clock and save len-1 bytes
for (i = 0; i < len - 1; i++) {
buf[i] = reg_spi_data; WAIT_UNTIL_FINISHED;
}
// save the last byte
buf[i] = reg_spi_data;
// CS = high, enable master mode, disable SDO, enable write
reg_spi_ctrl = FLD_SPI_CS | FLD_SPI_MASTER_MODE_EN | FLD_SPI_DATA_OUT_DIS;
#if IRQ_DISABLE
irq_restore(r);
#endif
}
void exflash_read_id( u8 *manufacturer_id, u16 *device_id)
{
u8 buf[3];
u8 reg_v;
int i;
#if IRQ_DISABLE
unsigned char r = irq_disable();
#endif
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN;
reg_spi_data = FLASH_GET_JEDEC_ID; WAIT_UNTIL_FINISHED;
reg_spi_ctrl = FLD_SPI_MASTER_MODE_EN | FLD_SPI_DATA_OUT_DIS | FLD_SPI_RD;
buf[0] = reg_spi_data; WAIT_UNTIL_FINISHED;
for (i = 0; i < 2; i++) {
buf[i] = reg_spi_data; WAIT_UNTIL_FINISHED;
}
buf[i] = reg_spi_data;
reg_spi_ctrl = FLD_SPI_CS | FLD_SPI_MASTER_MODE_EN | FLD_SPI_DATA_OUT_DIS;
*manufacturer_id = buf[0];
*device_id = (buf[1]<<8)|buf[2];
#if IRQ_DISABLE
irq_restore(r);
#endif
}
typedef void (*FLASH_WRITE_PAGE)(u32 addr, u32 len, u8 *buf);
typedef void (*FLASH_READ_PAGE)(u32 addr, u32 len, u8 *buf);
typedef void (*FLASH_ERASE_SECTOR)(u32 addr);
typedef void (*FLASH_ERASE_CHIP)(void);
_attribute_ram_code_ void hal_flash_write_page( u32 addr, u32 len, u8 *buf)
{
FLASH_WRITE_PAGE flash_write_page_fn;
u8 left = 0;
if( addr < 0x80000 ){
flash_write_page_fn = flash_write_page;
}
else{
flash_write_page_fn = exflash_write_page;
addr -= 0x80000;
}
left = addr%256;
if (left){
left = 256 - left;
if (left >= len){
flash_write_page_fn( addr, len, buf );
return;
}
else {
flash_write_page_fn( addr, left, buf );
addr += left;
buf += left;
len -= left;
}
}
while (len>=256){
flash_write_page_fn( addr, 256, buf );
addr += 256;
buf += 256;
len -= 256;
}
if (len > 0){
flash_write_page_fn( addr, len, buf );
}
}
void hal_flash_erase_sector( u32 addr)
{
FLASH_ERASE_SECTOR flash_erase_sector_fn;
if( addr < 0x80000 )
flash_erase_sector_fn = flash_erase_sector;
else {
flash_erase_sector_fn = exflash_erase_sector;
addr -= 0x80000;
}
flash_erase_sector_fn(addr);
}
_attribute_ram_code_ void hal_flash_read_page( u32 addr, u32 len, u8 *buf)
{
FLASH_READ_PAGE flash_read_page_fn;
if( addr < 0x80000 ){
flash_read_page_fn = flash_read_page;
}
else{
flash_read_page_fn = exflash_read_page;
addr -= 0x80000;
}
flash_read_page_fn( addr, len, buf );
}
void hal_flash_read_id( u8 *manufacturer_id, u16 *device_id)
{
exflash_read_id(manufacturer_id, device_id);
}
void hal_flash_extern_eraseall(void)
{
exflash_erase_chip();
}
void hal_flash_extern_enable(u8 enable)
{
if(0x0 == enable)
{
//disable
}
else
{
//enable
//spi_master_init((unsigned char)(CLOCK_SYS_CLOCK_HZ/(2*500000)-1),SPI_MODE0); //div_clock. spi_clk = sys_clk/((div_clk+1)*2),mode select
spi_master_init((unsigned char)0,SPI_MODE0); //div_clock. spi_clk = sys_clk/((div_clk+1)*2),mode select
spi_master_gpio_set(SPI_GPIO_GROUP_A2A3A4D6); //master mode ��spi pin set
}
}
1.2 SRAM操作
SRAM 有32K、48K、64K ,地址空间分别为:
32K:0x840000 - 0x848000
48K: 0x840000 - 0x84C000
64K: 0x840000 - 0x850000
RAM又分为两部分,一部分是普通的ram空间,另一部分是retention ram(睡眠不丢数据)。
RAM空间操作也有两种方法:直接方法,函数访问。
直接访问:
u32 y=*(volatile u32*)0x840000; //读ram 0x40000-0x40003地址的值
*(volatile u32*)0x840000 = 0x12345678; //写ram
函数访问:
//addr为实际地址-0x80000
write_reg8(addr,value);
write_reg16(addr,value);
write_reg32(addr,value);
read_reg8(addr);
read_reg16(addr);
read_reg32(addr);
注意:读写ram,要确保地址是2 byte/ 4byte字节对齐,否则会发生读写错误。