不知道自己是哪根筋搭错了还是怎么回事,一时心血来潮,突发奇想,决定把SQLite移植到STM32F429上。在此记录一下过程,也确认一下此事可行。
先说一下结果:移植后的SQLite可运行,可操作,但是比较耗资源(相对单片机来说),内存小的,就别想了,根本玩不起来的。
这是未加载时的内存使用情况:
这是打开数据库时内存的情况:
这是使用查询后的内存使用情况:
从内存使用的情况上来看,小单片机就算了,即使是STM32F429,高达192K的内存,也经不起这样折腾,还是得老实点加外存。
OK,结果也看了,现在来说说移植要点。
背景:STM32F429,32M外存SDRAM。RTT。FATFS。SD卡。
一切在SQLite的说明文档时已经交待清楚了,这里只是重复一下原作者的声明而已。
1、用替代实现替换内置的互斥锁子系统。
2、完全禁用在单线程应用程序中使用的所有静音。
3、重新配置内存分配子系统,以使用标准库中的malloc()实现以外的其他内存分配器。
4、重新对齐内存分配子系统,以便它根本不会调用malloc(),而是使用在启动时分配给SQLite的固定大小的内存缓冲区来满足所有内存请求。
5、用替代设计替换文件系统的接口。换句话说,覆盖SQLite进行的所有系统调用,以便使用一组完全不同的系统调用与磁盘进行对话。
6、覆盖其他操作系统接口,例如获取Zulu或本地时间的调用。
其实以上六条只是三个子系统的接口需要处理。分别是:
1、配置或替换Mutex子系统
2、配置或更换内存分配子系统
3、添加新的虚拟文件系统
最后补全这两个函数
sqlite3_os_init()
sqlite3_os_end()
做好上述工作,写个简单的测试程序。OK,接下来就可以放心测试调整了。
值得说的是:
仔细观察内存使用,这耗费了我太多的时间。栈空间从2K起,一直调整到16K,才算勉强成功。
另外:
得了解POSIX,若是你没有这个基础,需要花点时间了解下什么标准操作接口。
得了解VFS,若是你不知道这是什么东西,需要了解下虚拟文件系统的组成及原理。
得了解下单片机是怎么操作外存的,尤其是STM32Fx系列,我用的是Keil MDK,外挂32MSDRAM。
至于在调试过程中会遇到莫名其妙的出错时,请不要怀疑Sqlite,把Shell的栈空间放大点,至少到16K。2K是肯定不要想了,4K只够打开数据库,8K免强可以查询一下,16K测试下还目前还算稳定。
SQLite作者的原话:
为了将SQLite移植到新的操作系统(默认情况下不支持该操作系统),应用程序必须提供…
1、有效的互斥子系统(但仅在多线程的情况下),
2、一个工作的内存分配子系统(假设它在其标准库中缺少malloc()),
3、并且有效的VFS实施。
OK,从一开始,我拼命折腾SQLite源码,各种宏开关修改了个遍,最后才发现其实只要额外定义几个作者描述的宏之外,根本不需要动任何东西。事实上我最终也没有动任何源码,另外加了一个头文件用于声明宏开关。
若是想移植到freeRTOS,注意上面的三点,仿照RTT重写部分接口,一定会成功的。小伙伴们不要在SQLite源码里折腾了。
这是测试文件,移植代码不附了。请参考RTT就行。
#include "stdio.h"
#include "sqlite3.h"
#include "rtthread.h"
#include "string.h"
sqlite3 *db;
void DB_Open()
{
int rc = 0;
sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
sqlite3_initialize();
rc = sqlite3_open("test.db",&db);
if(rc)
{
rt_kprintf("Can't open database!\r\n");
return;
}
rt_kprintf("Open database success!\r\n");
}
MSH_CMD_EXPORT(DB_Open,SQLite3 runing);
void DB_Close()
{
sqlite3_close(db);
}
MSH_CMD_EXPORT(DB_Close,SQLite3 closed);
static int callback_create(void *NotUsed, int argc, char **argv, char **azColName){
int i;
for(i=0; i<argc; i++){
rt_kprintf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
rt_kprintf("\n");
return 0;
}
void CreateTable(void)
{
int rc;
char *zErrMsg = 0;
char *sql;
/* Create SQL statement */
sql = "CREATE TABLE COMPANY(" \
"ID INT PRIMARY KEY NOT NULL," \
"NAME TEXT NOT NULL," \
"AGE INT NOT NULL," \
"ADDRESS CHAR(50)," \
"SALARY REAL );";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql, callback_create, 0, &zErrMsg);
if( rc != SQLITE_OK ){
rt_kprintf("SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
rt_kprintf("Table created successfully\n");
}
sqlite3_close(db);
}
MSH_CMD_EXPORT(CreateTable,Create Table);
//控制台调用形式:
// Insert 5 murphy 32 California 2000.00
void Insert(int argc,char **argv)
{
char *zErrMsg = 0;
int rc;
char *sql;
if(argc < 2)
return;
char sqlCmd[128] = {0};
/* Create SQL statement */
// sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) " \
// "VALUES (6, 'Murphy2', 32, 'California', 20000.00 ); ";
sql = "INSERT INTO COMPANY (ID,NAME,AGE,ADDRESS,SALARY) VALUES (";
//6, 'Murphy2', 32, 'California', 20000.00 ); ";
strcpy(sqlCmd,sql);
strcat(sqlCmd,argv[1]); //ID
strcat(sqlCmd,", '");
strcat(sqlCmd,argv[2]); //name
strcat(sqlCmd,"', ");
strcat(sqlCmd,argv[3]); //age
strcat(sqlCmd,", '");
strcat(sqlCmd,argv[4]); //address
strcat(sqlCmd,"', ");
strcat(sqlCmd,argv[5]); //salary
strcat(sqlCmd,"); ");
/* Execute SQL statement */
rc = sqlite3_exec(db, sqlCmd, callback_create, 0, &zErrMsg);
if( rc != SQLITE_OK ){
rt_kprintf( "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
rt_kprintf( "Records created successfully\n");
}
}
MSH_CMD_EXPORT(Insert,Insert record to table);
static int callback_query(void *data, int argc, char **argv, char **azColName){
int i;
rt_kprintf( "%s: ", (const char*)data);
for(i=0; i<argc; i++){
rt_kprintf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
rt_kprintf("\n");
return 0;
}
void Query()
{
char *zErrMsg = 0;
int rc;
char *sql;
const char* data = "Callback function called";
/* Create SQL statement */
sql = "SELECT * from COMPANY";
/* Execute SQL statement */
rc = sqlite3_exec(db, sql, callback_query, (void*)data, &zErrMsg);
if( rc != SQLITE_OK ){
rt_kprintf( "SQL error: %s\n", zErrMsg);
sqlite3_free(zErrMsg);
}else{
rt_kprintf( "Operation done successfully\n");
}
}
MSH_CMD_EXPORT(Query,Query infomation from table);
static int callback_Delete(void *data, int argc, char **argv, char **azColName){
int i;
rt_kprintf( "%s: ", (const char*)data);
for(i=0; i<argc; i++){
rt_kprintf("%s = %s\n", azColName[i], argv[i] ? argv[i] : "NULL");
}
rt_kprintf("\n");
return 0;
}
//调用形式:DeleteByID 10 删除ID = 10 的一条记录
void DeleteByID(int argc,char **argv)
{
int rc;
char *sql;
char *zErrMsg = 0;
const char * data = "Callback function called";
if(argc < 2)
return;
sql = "DELETE FROM COMPANY WHERE ID = ";
char sqlCmd[50] = {0};
strcpy(sqlCmd,sql);
strcat(sqlCmd,argv[1]);
rc = sqlite3_exec(db,sqlCmd,callback_query,(void *)data,&zErrMsg);
}
MSH_CMD_EXPORT(DeleteByID,Delete infomation from table);