架构与代码仓库 本项目使用了ARM架构下的Cortex-M7核心,用户只需要复制必要文件就可以完成相关操作
Git仓库:https://github.com/iMinloha/H7OS.git
文件系统DrT 目前实现了RAMFS、动态内存管理、动态线程管理以及指令体系。其中RAMFS使用了DrT树结构,结构图如下:
结构体FS_t实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 struct FS { char * path; DrTNode_t node; int node_count; Task_t tasklist; FS_t parent; FS_t next; FS_t child; };static FS_t RAM_FS;
我们可以对一个FS_t节点添加线程节点、设备节点或者其他FS_t节点,其中在层级添加就是在当前目录同层新建文件夹,而在子级就是在文件夹下新建文件夹。也就是兄弟节点和子节点关系。
对于设备和线程需要有相关的属性,其中对设备的使用需要考虑多线程的互斥,所以每个设备节点必须自带一个互斥锁,在此之前我们为了确保RAM空间的充足就必须要实现SDRAM的内存管理,这里使用了tlsf算法:
1 2 3 4 5 6 7 8 9 10 11 void MemControl_Init () { flashSDRAM(); kernel_pool = tlsf_create_with_pool((void *)SDRAM_BANK_ADDR, 2 * 1024 * 1024 ); mem_pool = tlsf_create_with_pool((void *)SDRAM_BANK_ADDR + 2 * 1024 * 1024 , 30 * 1024 * 1024 ); }
首先对SDRAM空间进行刷新,也就是将内部莫名的数据清0,然后分别区分为内核空间和程序运行空间,保留出足够DrT使用的内核空间(2M足够了)后其他的空间作为用户应用程序空间即可。所以需要实现kernel_alloc和ram_alloc算法,两者极其相似:
1 2 3 4 5 6 7 8 9 10 11 12 13 void * ram_alloc (uint32_t size) { using_mem += size; return tlsf_malloc(mem_pool, size); }void * kernel_alloc (uint32_t size) { return tlsf_malloc(kernel_pool, size); }
然后就可以声明设备结构体了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 enum DeviceType { DEVICE_TIMER, DEVICE_BS, DEVICE_STORAGE, DEVICE_DISPLAY, DEVICE_INPUT, DEVICE_SERIAL, DEVICE_TRANSPORT, DEVICE_VOTAGE, DEVICE_TASK, FILE_SYSTEM, FILE, };typedef enum DeviceType DeviceType_E ;enum DeviceStatus { DEVICE_OFF, DEVICE_ON, DEVICE_SUSPEND, DEVICE_ERROR, DEVICE_BUSY, };typedef enum DeviceStatus DeviceStatus_E ;typedef struct FS * FS_t ;typedef struct DrTNode * DrTNode_t ;struct DrTNode { void * device; DeviceStatus_E status; DeviceType_E type; char * name; char * description; void * data; Func_t driver; Mutex_t mutex; DrTNode_t next; FS_t parent; };
为此我们需要确保线程数据安全,必须实现以下Mutex_t锁的相关内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <stdatomic.h> typedef struct param * param_t ;typedef void (*Func_t) (param_t param) ;struct _mutex { atomic_int lockflag; atomic_int count; atomic_int owner; void (*lock)(struct _mutex *self); void (*unlock)(struct _mutex *self); int (*status)(struct _mutex *self); };typedef struct _mutex * Mutex_t ;#define MUTEX_SIZE sizeof(struct _mutex) void mutex_init (Mutex_t self) ;void mutex_lock (Mutex_t self) ;void mutex_unlock (Mutex_t self) ;int mutex_status (Mutex_t self) ;void LockFunc (Mutex_t self, Func_t func, param_t param) ;
这里我使用了几个原子操作进行状态绑定,在同时保护设备的同时还可以确定设备拥有者和设备使用计数周期,这样就可以完成精确的设备保护了。
为了定位串口终端所处位置,我们需要新建两个结构体指向根文件系统和终端节点。
1 2 3 4 5 static FS_t RAM_FS;void DrTInit () ;
然后我们需要实现快捷的添加设备,添加文件夹,添加线程等等方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 void addFSChild (FS_t parent, char *path) { FS_t child = (FS_t) kernel_alloc(sizeof (struct FS)); child->path = (char *) kernel_alloc(strlen (path) + 1 ); strcopy(child->path, path); child->node = NULL ; child->node_count = 0 ; child->parent = parent; child->next = NULL ; child->child = NULL ; FS_t p = parent->next; if (p == NULL ) { parent->next = child; return ; }else { while (p->child != NULL ) p = p->child; p->child = child; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 void addDevice (char *path, void * devicePtr, char *name, char *description, DeviceType_E type, DeviceStatus_E status, Func_t driver) { FS_t node = getFSChild(RAM_FS, path); if (node == NULL ) return ; DrTNode_t device = (DrTNode_t) kernel_alloc(sizeof (struct DrTNode)); device->name = (char *) kernel_alloc(strlen (name) + 1 ); device->description = (char *) kernel_alloc(strlen (description) + 1 ); strcopy(device->name, name); strcopy(device->description, description); device->device = devicePtr; device->status = status; device->type = type; device->data = kernel_alloc(128 ); device->driver = driver; Mutex_t mutex = (Mutex_t) kernel_alloc(MUTEX_SIZE); mutex_init(mutex); device->mutex = mutex; device->parent = node; device->next = NULL ; DrTNode_t p = node->node; if (p == NULL ) { node->node = device; node->node_count++; return ; }else { while (p->next != NULL ) p = p->next; p->next = device; node->node_count++; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 void addThread (Task_t task) { FS_t node = getFSChild(RAM_FS, "proc" ); if (node == NULL ) return ; Task_t p = node->tasklist; if (p == NULL ) { node->tasklist = task; return ; }else { while (p->next != NULL ) p = p->next; p->next = task; } }
根据FS_t的name可以组建文件路径,我们当然可以从路径获取FS_t的对象:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 FS_t getFSChild (FS_t parent, char *path) { FS_t p = parent->next; while (p != NULL ){ if (strcmp (p->path, path) == 0 ) return p; p = p->child; } return NULL ; }
这里的实现方法极为简单。接下来完整组成:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 FS_t loadPath (char * path) { FS_t node = RAM_FS; if (strcmp (path, "/" ) == 0 ) return node; else { if (path[0 ] == '/' ) path++; char *token = strtok (path, "/" ); while (token != NULL ) { node = getFSChild (node, token); if (node == NULL ) return NULL ; token = strtok (NULL , "/" ); } } return node; }
从此方法就可以获得FS节点信息,如果路径对应的FS_t对象不存在就会返回NULL。
除了加载文件夹路径的方法外,还需要加载设备:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 DrTNode_t loadDevice (char * path_aim) { char *path = (char *) kernel_alloc(strlen (path_aim) + 1 ); strcopy(path, path_aim); FS_t node; if (path[0 ] == '/' ) node = RAM_FS; else node = currentFS; if (strcmp (path, "/" ) == 0 ) return NULL ; else { if (path[0 ] == '/' ) path++; char *token = strtok(path, "/" ); while (token != NULL ) { FS_t tmp_node = getFSChild(node, token); if (tmp_node == NULL ) break ; token = strtok(NULL , "/" ); node = tmp_node; } if (strcmp (token, strtok(token, "/" )) != 0 ){ return NULL ; } else { DrTNode_t p = node->node; while (p != NULL ) { if (strcmp (p->name, token) == 0 ) return p; p = p->next; } } } return NULL ; }
加载任务也如此简单,和加载设备方法一样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Task_t loadTask (char * path) { FS_t node; if (path[0 ] == '/' ) node = RAM_FS; else node = currentFS; if (strcmp (path, "/" ) == 0 ) return NULL ; else { if (path[0 ] == '/' ) path++; char *token = strtok(path, "/" ); while (token != NULL ) { FS_t tmp_node = getFSChild(node, token); if (tmp_node == NULL ) break ; token = strtok(NULL , "/" ); node = tmp_node; } if (strcmp (token, strtok(token, "/" )) != 0 ){ return NULL ; } else { Task_t p = node->tasklist; while (p != NULL ) { if (strcmp (p->name, token) == 0 ) return p; p = p->next; } } } return NULL ; }
线程管理u_Thread 线程结构体实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 enum TaskStatus { TASK_READY, TASK_RUNNING, TASK_SUSPEND, TASK_STOP };typedef enum TaskStatus TaskStatus_E ;enum TaskPriority { TASK_PRIORITY_NORMAL, TASK_PRIORITY_HIGH, TASK_PRIORITY_ROOT, TASK_PRIORITY_SYSTEM, };typedef enum TaskPriority TaskPriority_E ;typedef struct Task * Task_t ;struct Task { char *name; TaskStatus_E status; float cpu; TaskPriority_E priority; osThreadId handle; uint32_t lastWakeTime; uint32_t accumulatedTime; uint8_t PID; Task_t next; };
我们只需要实现对应的方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 Task_t xTaskManager; Task_t xShell; Task_t xTest;uint8_t PID_Global = 0 ; Task_t RAMFS_TASK_Create (char *name, TaskStatus_E status, TaskPriority_E priority, osThreadId handle) { Task_t task = (Task_t) kernel_alloc(sizeof (struct Task)); task->name = kernel_alloc(strlen (name) + 1 ); strcopy(task->name, name); task->status = status; task->PID = PID_Global++; task->cpu = 0 ; task->priority = priority; task->handle = handle; task->next = NULL ; return task; }void ThreadInit () { osThreadDef(xShell, ShellTask, osPriorityNormal, 0 , 1024 ); xShellHandle = osThreadCreate(osThread(xShell), NULL ); osThreadDef(xTaskManager, TaskManager, osPriorityAboveNormal, 0 , 1024 ); xTaskManagerHandle = osThreadCreate(osThread(xTaskManager), NULL ); osThreadDef(xTaskInit, QueueInit, osPriorityIdle, 0 , 2048 ); xTaskInitHandle = osThreadCreate(osThread(xTaskInit), NULL ); osThreadDef(xTaskTest, testFunc, osPriorityRealtime, 0 , 2048 ); xTaskTestHandle = osThreadCreate(osThread(xTaskTest), NULL ); xTaskManager = RAMFS_TASK_Create("xTaskManager" , TASK_READY, TASK_PRIORITY_SYSTEM, xTaskManagerHandle); xShell = RAMFS_TASK_Create("xShell" , TASK_READY, TASK_PRIORITY_SYSTEM, xShellHandle); xTest = RAMFS_TASK_Create("xTaskTest" , TASK_READY, TASK_PRIORITY_NORMAL, xTaskTestHandle); }
这里我使用了FreeRTOS的线程调度方法,我们只需要在proc下面添加对应的结构体即可,每个线程需要实现一个专属PID,之后会在指令管理处完成利用。
然后利用任务管理器线程实现占用率统计:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 void TaskManager (void const * argument) { addThread(xShell); addThread(xTaskManager); addThread(xTest); Task_t head = getTaskList(); uint32_t lastTotalTicks = xTaskGetTickCount(); while (1 ){ TaskTickStart(xTaskManager); Task_t currentTask = head; uint32_t currentTotalTicks = xTaskGetTickCount(); uint32_t deltaTime = currentTotalTicks - lastTotalTicks; while (currentTask != NULL ){ currentTask->cpu = (float )currentTask->accumulatedTime / deltaTime * 10.0f ; currentTask = currentTask->next; } lastTotalTicks = currentTotalTicks; osDelay(1000 ); TaskTickEnd(xTaskManager); } }
在每个线程的死循环前后都需要实现运行时间统计:
1 2 3 4 TaskTickStart(xShell); osDelay(1000 ); TaskTickEnd(xShell);
然后定义一个Task头,实现各个线程的Handle和时刻统计宏:
1 2 3 4 5 6 7 8 static osThreadId xTaskInitHandle;static osThreadId xTaskManagerHandle;static osThreadId xShellHandle;static osThreadId xTaskTestHandle;#define TaskTickStart(task) task->lastWakeTime = xTaskGetTickCount(); #define TaskTickEnd(task) task->accumulatedTime = xTaskGetTickCount() - task->lastWakeTime;
指令系统CMD 指令系统使用了单链表,具体结构体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 typedef void (*Comand_t) (int argc, char **argv) ;typedef struct CMD * CMD_t ;struct CMD { char * name; char * description; char * usage; Comand_t cmd; CMD_t next; };static CMD_t CMDList;void addCMD (char * name, char * description, char * usage, Comand_t cmd) ;void execCMD (char * command) ;void helpCMD (char *cmd) ;#define CMD(name, description, usage, cmd) addCMD(name, description, usage, cmd)
这样在指令管理线程内就可以直接使用CMD宏添加内容。这里我新建了一个Command文件夹专门实现各种指令,目前实现了:
ls :显示特定目标下的子内容(包括文件夹、设备、线程) info :显示除了FS_t对象以外的对象信息(可以是设备、线程、CPU) echo :串口终端输出一些内容 cd :切换串口终端FS_t的指针 help : 指令帮助,遍历CMDList匹配name并输出usage 添加指令方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void addCMD (char * name, char * description, char * usage, Comand_t cmd) { CMD_t p = CMDList; while (p->next != NULL ) p = p->next; CMD_t newCMD = (CMD_t) kernel_alloc(sizeof (struct CMD)); newCMD->name = (char *) kernel_alloc(strlen (name) + 1 ); newCMD->description = (char *) kernel_alloc(strlen (description) + 1 ); newCMD->usage = (char *) kernel_alloc(strlen (usage) + 1 ); strcopy(newCMD->name, name); strcopy(newCMD->description, description); strcopy(newCMD->usage, usage); newCMD->cmd = cmd; newCMD->next = NULL ; p->next = newCMD; }
添加指令就是插入节点,接下来是执行指令,这里需要对指令进行解析:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 void execCMD (char * command_rel) { char * command = (char *) kernel_alloc(strlen (command_rel) + 1 ); strcopy(command, command_rel); char *argv[128 ] = {0 }; int argc = 0 ; char * token = strtok(command, " " ); while (token != NULL ){ argv[argc++] = token; token = strtok(NULL , " " ); } argc -= 1 ; CMD_t p = CMDList->next; while (p != NULL ){ if (strcmp (p->name, argv[0 ]) == 0 ){ p->cmd(argc, &argv[1 ]); return ; } p = p->next; } u_print("Command not found\n" ); }
每个CMD节点都有一个函数指针,函数指针需要指令参数数目和指令参数列表,一旦匹配到直接使用函数指针运行就可以。下图就是指令列表结构
接下来实现指令集帮助,参数输入一个需要查询的指令,直接输出信息即可:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 void helpCMD (char *cmd) { char buf[128 ]; memoryCopy(buf, cmd, strlen (cmd) + 1 ); CMD_t p = CMDList->next; u_print("Command\t\tDescription\t\tUsage\n" ); if (buf[0 ] == '\0' ) { while (p != NULL ){ u_print("%s\t\t%s\t\t%s\n" , p->name, p->description, p->usage); p = p->next; } }else { while (p != NULL ){ if (strcmp (p->name, cmd) == 0 ){ u_print("%s\t\t%s\t\t%s\n" , p->name, p->description, p->usage); return ; } p = p->next; } } }
最后需要初始化DrT数,CMD链表等等,方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 void DrTInit () { RAM_FS = (FS_t) kernel_alloc(sizeof (struct FS)); CMDList = (CMD_t) kernel_alloc(sizeof (struct CMD)); CMDList->next = NULL ; RAM_FS->path = "/" ; RAM_FS->node = NULL ; RAM_FS->node_count = 0 ; RAM_FS->parent = NULL ; RAM_FS->next = NULL ; RAM_FS->child = NULL ; currentFS = RAM_FS; addFSChild(RAM_FS, "dev" ); addFSChild(RAM_FS, "tmp" ); addFSChild(RAM_FS, "mnt" ); addFSChild(RAM_FS, "bin" ); addFSChild(RAM_FS, "usr" ); addFSChild(RAM_FS, "root" ); addFSChild(RAM_FS, "opt" ); addFSChild(RAM_FS, "etc" ); addFSChild(RAM_FS, "proc" ); addDevice("dev" , &CortexM7, "Cortex-M7" , "Central Processing Unit" , DEVICE_BS, DEVICE_BUSY, NULL ); addDevice("dev" , &huart1, "USART1" , "Serial bus device" , DEVICE_SERIAL, DEVICE_BUSY, NULL ); addDevice("mnt" , &hsdram1, "SDMMC" , "SD card" , DEVICE_STORAGE, DEVICE_ON, NULL ); addDevice("mnt" , &SDFatFS, "FATFS" , "FAT file system" , FILE_SYSTEM, DEVICE_ON, NULL ); addDevice("mnt" , &hqspi, "QSPI" , "Quad SPI" , DEVICE_STORAGE, DEVICE_ON, NULL ); register_main(); }
指令注册方法如下:
1 2 3 4 5 6 7 8 9 void register_main () { CMD("ls" , "List files" , "ls /path or ls" , ls_main); CMD("cd" , "Change Directory" , "cd /path" , cd_main); CMD("info" , "list something infomation" , "info path/file" , info_main); CMD("echo" , "echo something" , "echo your want print things" , echo_main); CMD("help" , "help using command" , "help command" , help_main); }
tree指令没有实现,所以注释了
这里使用ls指令解释一下指令系统的书写:
1 2 3 4 5 6 7 8 9 10 11 12 13 extern FS_t currentFS;void ls_main (int argc, char **argv) { if (argc > 1 ) u_print("ls: too many arguments\r\n" ); else if (argc == 1 ) ram_ls(argv[0 ]); else { char *pwd = (char *)kernel_alloc(1024 ); ram_pwd(currentFS, pwd); ram_ls(pwd); } }
这个函数写起来非常简单,其中使用了ram_pwd和ram_ls方法,实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 void ram_pwd (FS_t fs, char * path) { FS_t temp_node; if (fs == RAM_FS) { strcopy(path, "/" ); return ; }else { temp_node = fs; ram_pwd(temp_node->parent, path); strconcat(path, temp_node->path); strconcat(path, "/" ); } path[strlen (path) - 1 ] = '\0' ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 void ram_ls (char * path) { FS_t node = loadPath(path); if (node == NULL ) return ; FS_t temp = node->next; while (temp != NULL ){ u_print("%s " , temp->path); temp = temp->child; } if (node->node_count != 0 ) { DrTNode_t p = node->node; while (p != NULL ){ u_print("%s " , p->name); p = p->next; } } if (node->tasklist != NULL ){ Task_t p = node->tasklist; while (p != NULL ){ u_print("%s\t" , p->name); p = p->next; } } u_print("\n" ); }
其中pwd可以直接获取当前FS_t节点的路径,后期也可以实现pwd指令获取当前串口终端位置。
其他对象与U库 虽然线程管理也属于U库,但是因为与DrT交集过多,需要单独提出。
CPU对象是一个结构体:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 typedef float (*CPU_Func_t) (void ) ;struct CPU { char * name; char * description; uint32_t frequency; double temperature; double load; };typedef struct CPU * CPU_t ;static CPU_t CortexM7;void createCPU () ;void showCPUInfo () ;
H743iit6的adc3可以采样CPU温度,所以我们实现一下这个方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 float updateCPU () { HAL_ADC_Start(&hadc3); uint16_t adc_v = HAL_ADC_GetValue(&hadc3); double adc_x = (110.0 -30.0 )/(*(unsigned short *)(0x1FF1E840 ) - *(unsigned short *)(0x1FF1E820 )); return adc_x * (adc_v - *(unsigned short *)(0x1FF1E820 )) + 30 ; }void createCPU () { CortexM7 = (CPU_t) kernel_alloc(sizeof (struct CPU)); CortexM7->name = "Cortex-M7" ; CortexM7->description = "Cortex-M7 CPU" ; CortexM7->frequency = HAL_RCC_GetSysClockFreq(); CortexM7->temperature = 0 ; }void showCPUInfo () { u_print("CPU name: %s\n" , CortexM7->name); u_print("CPU description: %s\n" , CortexM7->description); u_print("CPU frequency: %d HZ\n" , CortexM7->frequency); float temp = updateCPU(); u_print("CPU temperature:" ); put_double(temp, 10 , 1 ); u_print(" C\n" ); }
其中showCPUInfo是显示CPU信息,U库的u_print输出小数存在浮点精度问题,所以单独put_num就可以了。这个showCPUInfo是给info指令使用的,一旦info了CPU就可以显示温度了。
U库实现了u_stdio文件,主要是u_print与字符串方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 #include <stdint.h> #define TRUE 1 #define FALSE 0 #define EOL '\n' #define EOLR '\r' #define EOLN '\0' #define ThreadLock __disable_irq() #define ThreadUnlock __enable_irq() #define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)) #define va_start(ap,v) (ap = (va_list)&v + _INTSIZEOF(v)) #define va_arg(ap,t) (*(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t))) #define va_end(ap) ( ap = (va_list)0 ) typedef char * va_list;#define _OS_WEAK __attribute__((weak)) #define type(x) typeof(x) int u_print (const char *fmt, ...) ;static int v_printf (const char *fmt, va_list ap) ;void put_char (char c) ;void put_num (int num, int base, int sign) ;void put_double (double num, int base, int sign) ;void put_huge_num (uint32_t num, int base, int sign) ;void put_address_num (uint32_t num, int base, int sign) ;void put_s (char *str) ;int strlen (char *str) ;char * strtok (char *str, char *delim) ;int strspilt (char *str, char *delim, char *outlist[]) ;void u_sprintf (char *out, const char *fmt, ...) ;void strcopy (char *dest, char *src) ;void strconcat (char *dest, char *src) ;int strcmp (char *str1, char *str2) ;#define LED_ON HAL_GPIO_WritePin(GPIOH, GPIO_PIN_7, GPIO_PIN_RESET) #define LED_OFF HAL_GPIO_WritePin(GPIOH, GPIO_PIN_7, GPIO_PIN_SET)
内存管理不仅可以管理SDRAM还可以管理QSPI设备(flash),这里flash是ROM,可以掉电保存数据,之后会单开线程保存SDRAM的2M系统内容到flash内。不过目前还用不到,同时也把SD卡挂载了FatFS。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 void memoryCopy (void *dest, void *src, int size) ;void memorySet (void *dest, char val, int size) ;int memoryCompare (void *dest, void *src, int size) ;void flashWrite (uint32_t addr, uint8_t *data, uint32_t size) ;void flashRead (uint32_t addr, uint8_t *data, uint32_t size) ;#define IO_U8 *(__IO uint8_t*) #define IO_U16 *(__IO uint16_t*) #define IO_U32 *(__IO uint32_t*) #define IO_U64 *(__IO uint64_t*) #define IO_VOID *(__IO void*) #define IO_STRUCT(Pointer) *(__IO Pointer)
这里为了之后的自定义编程语言,在SDRAM内实现了结构体和函数数据指针,函数也是一组指令,也可以保存在内存,所以IO_STRUCT非常有必要。
为了获取随机数,获取系统时间,这里打开了RTC和RAM方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <stdint-gcc.h> #define Correct_Year 2024 #define Correct_Month 8 - 1 #define Correct_Day 14 - 1 #define Correct_Hour 22 #define Correct_Minute 9 uint8_t getSecond () ;uint8_t getMinute () ;uint8_t getHour () ;uint8_t getDay () ;uint8_t getMonth () ;uint16_t getYear () ;void UpdateTime () ;uint32_t Random () ;
而其中重要的就是updateTime:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void UpdateTime () { HAL_RTC_GetTime(&hrtc,&Time_Struct,RTC_FORMAT_BIN); HAL_RTC_GetDate(&hrtc,&Date_Struct,RTC_FORMAT_BIN); }uint32_t Random () { uint32_t *random = NULL ; HAL_RNG_GenerateRandomNumber(&hrng, random); return *random; }
u库都是硬件驱动,其他的内容都在线程内像开发操作系统一样开发即可。
最后就是一些杂七杂八的初始化:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 void taskGlobalInit () { HAL_SD_CardInfoTypeDef SDCardInfo; HAL_SD_CardCIDTypeDef SDCard_CID; HAL_SD_GetCardCID(&hsd1,&SDCard_CID); HAL_SD_GetCardInfo(&hsd1,&SDCardInfo); uint64_t CardCap=(uint64_t )(SDCardInfo.LogBlockNbr)*(uint64_t )(SDCardInfo.LogBlockSize); u_print("SD card Drive Capacitor: %D MB\r\n" , (uint32_t )(CardCap>>20 )); HAL_ADCEx_Calibration_Start(&hadc3,ADC_CALIB_OFFSET,ADC_SINGLE_ENDED); HAL_ADC_Start(&hadc3); if (QSPI_W25Qxx_BlockErase_32K(0 ) != QSPI_W25Qxx_OK) u_print("Erase Failed\n" ); else u_print("QSPI Flash Succeed, ID: %d\n" , QSPI_W25Qxx_ReadID()); DrTInit(); createCPU(); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 void QueueInit (void const * argument) { FRESULT FSRes = f_mount(&SDFatFS,SDPath,1 ); BYTE work[_MAX_SS]; if (FSRes != FR_OK){ FSRes = f_mkfs(SDPath, FM_FAT32, 0 , work, sizeof work); if (FSRes == FR_OK) { u_print("SD card init succeed\r\n" ); f_mount(&SDFatFS,SDPath,1 ); } else u_print("Init Faild, please replace SD card\r\n" ); } else u_print("SD card Succeed\r\n" ); vTaskSuspend(xTaskInitHandle); }
项目规划 实现其他的指令等等内容,按照github内容即可。