OS
基于44b0x的ucos开发,网上开源很多,可以参考,但是最有益的是一切从头自己搭建,自己调试!环境基于ADS+wiggler调试。
平台最主要的是清晰,一目了然,而且换不同cpu不同的os,代码结构不需要重大更改,比如ucos升级到新的版本,直接替换相应目录即可;比如如果应用比较大,需要更换操作系统,kernel的系统api做封装就不用担心了;比如cpu芯片更换或者升级,只需要更换相应的bsp;总之改动做到最小
搭建的环境目录如下:
ucos目录是官方os代码
bsp/common目录是ucos的基于ARM7的官方SDK包
bsp/cpu目录是三星44b0x的官方SDK包,增加bsp.c文件,包含系统的外部中断处理函数,以及系统时钟的实现,延时函数,当然cpu的初始化函数port_init也在此实现
kernel目录是提供os的系统API封装,printf,udelay等
app目录是main函数入口,主要是多任务调度以及消息队列
file://D:/ucos2 (7 folders, 2 files, 445.72 KB, 4.46 MB in total.)
│ucos.elf 234.92 KB
│ucos2.mcp 210.81 KB
├─app (0 folders, 2 files, 15.86 KB, 15.86 KB in total.)
│main.c 7.64 KB
│main.c.bak 8.22 KB
├─bsp (2 folders, 0 files, 0 bytes, 125.74 KB in total.)
│├─cpu (2 folders, 0 files, 0 bytes, 67.50 KB in total.)
││├─src (0 folders, 5 files, 37.76 KB, 37.76 KB in total.)
│││44binit.s 13.63 KB
│││44blib.c 8.38 KB
│││bsp.c 4.87 KB
│││bsp.c.bak 3.00 KB
│││rtl8019.c 7.88 KB
││└─inc (0 folders, 11 files, 29.73 KB, 29.73 KB in total.)
││44b.h 15.28 KB
││44blib.h 1.54 KB
││bsp.h 460 bytes
││bsp.h.bak 486 bytes
││def.h 434 bytes
││includes.h 1.30 KB
││Memcfg.s 2.49 KB
││Option.h 837 bytes
││Option.s 1.22 KB
││rtl8019.h 5.02 KB
││types.h 751 bytes
│└─common (3 folders, 0 files, 0 bytes, 58.24 KB in total.)
│├─src (2 folders, 1 files, 11.97 KB, 31.38 KB in total.)
│││os_cpu_c.c 11.97 KB
││├─arm (0 folders, 1 files, 9.95 KB, 9.95 KB in total.)
│││os_cpu_a.S 9.95 KB
││└─gcc (0 folders, 1 files, 9.46 KB, 9.46 KB in total.)
││os_cpu_a.Sgcc 9.46 KB
│├─arm (0 folders, 1 files, 4.04 KB, 4.04 KB in total.)
││ac_halt.c 4.04 KB
│└─inc (0 folders, 5 files, 22.82 KB, 22.82 KB in total.)
│armdefs.h 2.21 KB
│def.h 2.59 KB
│halt.h 2.57 KB
│os_cfg.h 8.15 KB
│os_cpu.h 7.30 KB
├─kernel (0 folders, 2 files, 502 bytes, 502 bytes in total.)
│xkernel.c 223 bytes
│xkernel.h 279 bytes
├─lwip-1.1.0 (2 folders, 4 files, 22.65 KB, 659.37 KB in total.)
.......
├─shell (0 folders, 3 files, 36.02 KB, 36.02 KB in total.)
│cmds.c 15.17 KB
│shell.c 18.83 KB
│shell.h 2.01 KB
├─ucos (0 folders, 10 files, 294.83 KB, 294.83 KB in total.)
│os_core.c 43.44 KB
│os_flag.c 43.76 KB
│os_mbox.c 23.29 KB
│os_mem.c 13.79 KB
│os_mutex.c 27.26 KB
│os_q.c 33.79 KB
│os_sem.c 19.04 KB
│os_task.c 35.21 KB
│os_time.c 9.68 KB
│ucos_ii.h 45.59 KB
└─
1. 移植流程
内部时钟:先搭好ucos和bsp,不需要修改任何文件,新建main.c,新建两个task看能否在之间调度;显然是不可能的,所以动手写的第一行代码就是系统时钟函数,新建bsp.c,作为三星的SDK的补充来用,用timer0做系统tick,定时会产生内部中断,__vT0Interrupt是中断处理函数,这里挂ucos的BSP的API就行,当然别忘记了rINTMSK = ~(BIT_GLOBAL |BIT_TIMER0); 打开timer0中断,至于中断方式是向量中断还是非向量中断方式,在interrupt文章中记录,具体配置参考注释
rTCFG0=(MCLK>>1)/1000000-1;
/* 此为预分频值,MCLK为外部时钟频率 */
rTCFG1=0x00000000;
/* mux0 = 1/2 */
/* 定时器0输入时钟频率=MCLK/(预分频值+1)/mux0,即=MCLK/[(MCLK>>1)/1000000-1 + 1]/(1/2),计算得输入时钟频率为1M */
/* 实际应用中是根据需要的输入时钟频率来赋值1000000的 */
rTCNTB0= 1000000/OS_TICKS_PER_SEC;
/* 每一秒有OS_TICKS_PER_SEC个tick产生,这个根据实际需要更改 */
rTCMPB0= 0x0;
rTCON=0x2;
rTCON=0x9;
/* 内部中断控制寄存器配置 */
pISR_TIMER0= (unsigned)__vT0Interrupt;
中断:有点小复杂,单独新开一篇中断的总结
中断函数添加之后,调试用的printf,就是一个最基础的驱动了
int printf(const char* pbFmt, ...)
{
va_list pArg;
char abString[256];
int rv;
va_start(pArg, pbFmt);
/* 根据最后一个固定参数取可变参数表的首地址,以后可以根据该首地址逐一取各变量地址;此时pArg指向第一个可变参数 */
rv = vsprintf(abString, pbFmt, pArg);
/* 同sprintf一样*/
Uart_SendString(abString);
/* 调底层驱动 */
va_end(pArg);
return rv;
}
串口接收就需要起一个dbg_entry的task,为了方便调试,这里是porting一个命令行过来,这几个函数是从RTEMS移植过来的,可以自行添加调试命令,Uart_GetString(line)函数也可以实现记忆小功能,包括上下左右等键
void dbg_entry(void* Id)
{
unsigned char line[MAX_COUNT];
unsigned char * dbg_prompt_s ;
dbg_prompt_s = (unsigned char *) "dbg> " ;
printf("OSPrioCur = %d/n", OSPrioCur);
memset(line, 0, sizeof(line));
shell_add_cmd(NULL, NULL, NULL, NULL); /* init the chain list*/
/* 这个就是porting过来的一个命令行调试函数 */
for (; ;)
{
printf((char *) dbg_prompt_s);
if (Uart_GetString(line) < 0) /* 底层驱动函数 */
break;
if ((line[0] == 0) || (line[0] == '/n'))
continue;
process_line(line);
/* 这个也是porting过来的解析命令行调试函数 */
}
}
内存分配:挂接预先分配好的44b0x的堆的首地址和尾地址
延时函数,因为OSTimeDly是基于tick数目来的,所以做一个封装,转换成ms可读性强的API
void udelay(unsigned short ms)
{
unsigned short ucos_timeout;
if(ms)
{
ucos_timeout = (ms * OS_TICKS_PER_SEC)/1000;
if(ucos_timeout < 1)
ucos_timeout = 1;
else if(ucos_timeout > 65535)
ucos_timeout = 65535;
}
else
{
ucos_timeout = 0;
}
OSTimeDly(ucos_timeout);
}
ucos的系统api,因为ucos相对简单,任务间通信不复杂的平台采用它最好不过了,任务状态也相对简单----休眠,等待,就绪,运行
首先在os_cfg.h中使能需要用到的api,这里配置至少需要任务,事件,信号量,消息队列,系统心跳数以及时钟函数
#define OS_MAX_TASKS 63 /* Max. number of tasks in your application ... */
#define OS_FLAG_EN 1 /* Enable (1) or Disable (0) code generation for EVENT FLAGS */
#define OS_SEM_EN 1 /* Enable (1) or Disable (0) code generation for SEMAPHORES */
#define OS_Q_EN 1 /* Enable (1) or Disable (0) code generation for QUEUES */
#define OS_TICKS_PER_SEC 200 /* Set the number of ticks in one second */
#define OS_TIME_GET_SET_EN 1 /* Include code for OSTimeGet() and OSTimeSet() */
接下来就创建任务间需要传递的信号量和消息队列,当然任务也根据需要创建,注释代码如下
OS_EVENT* RecTcpQFlag;
RecPackedFlag = OSSemCreate(0);
/* 创建一个信号量 */
RecUdpQFlag = OSQCreate(&RecUdpQ[0], Q_Max_Size);
/* 创建一个消息队列,RecUdpQFlag是这个消息队列对应的事件 */
OSTaskCreate(task1, (void *) &Id1, (OS_STK *) &Stack1[STACKSIZE - 1], 1);
OSTaskCreate(task2, (void *) &Id2, (OS_STK *) &Stack2[STACKSIZE - 1], 2);
/* 创建两个任务,STACKSIZE是他们的栈大小,这个时候任务处于休眠状态 */
FRMWRK_vStartTicker(OS_TICKS_PER_SEC); /* os_cfg.h */
/* 起系统的tick */
OSStart();
/* 任务非休眠状态了,或等待或就绪或运行 */
task1通过UdpTemp = OSQPend(RecUdpQFlag, 0, &eer);处于等待状态,在等待在RecUdpQFlag事件,task2处于运行状态,如果这个时候task2调用一个发送消息队列的函数OSQPost(RecUdpQFlag, (void *) send_buf);则task可以顺利的接收数据到UdpTemp
如果这个task2也等待一个事件,并且是在task1中发送的,那么这样就死锁了
void task1(void* Id)
{
void* UdpTemp;
U8 eer;
printf("OSPrioCur = %d/n", OSPrioCur);
for (; ;)
{
UdpTemp = OSQPend(RecUdpQFlag, 0, &eer);
/* 0代表tick数,最好的方法是利用(ms * OS_TICKS_PER_SEC)/1000转换一下,ms代表n毫秒,超时后如果还没有得到消息则恢复成就绪状态,并返回error,如果该值设置成零则表示任务将持续地等待消息 */
if (eer == OS_NO_ERR)
{
printf("UdpTemp:0x%x", UdpTemp);
eer = eer;
}
}
}
再结合外围接口的驱动代码和文件系统,这样整个平台就搭建起来了,就可以开工写应用代码了,用ucos做应用也只用到这些API了,也足够应付一个普通平台的设计了
2. ucos的移植分析
EXPORT OSStartHighRdy
EXPORT OSCtxSw
EXPORT OSIntCtxSw
EXPORT UCOS_IRQHandler
和底层相关的压栈,出栈,挂接中断函数后的处理
3. 小结
买公司性质的板子,服务配套好,读书时候的板子是周立功的,平台几乎都帮你搭好了,省了很多事;相反山寨一切都要自己弄,不过更能锻炼人,对整个平台细节都非常清晰
在ADS中为了编写烧入flash跑的bin,采用-info totals -ro-base 0 -first 44binit.o(init) 这样烧入不成功的,跳了好久呢,没搞清楚 ro和rw的不同
MCLK要根据实际硬件来,要不然配置串口的时候波特率后,串口打的是乱码
OS_Q_EN默认的没开启,需要使能,要不然编译出错
uart_init在portinit之前就做,因为我中断函数加了打印信息,中断初始化后中断马上来了,这个时候串口没来得及初始化
OS
基于44b0x的ucos开发,网上开源很多,可以参考,但是最有益的是一切从头自己搭建,自己调试!环境基于ADS+wiggler调试。
平台最主要的是清晰,一目了然,而且换不同cpu不同的os,代码结构不需要重大更改,比如ucos升级到新的版本,直接替换相应目录即可;比如如果应用比较大,需要更换操作系统,kernel的系统api做封装就不用担心了;比如cpu芯片更换或者升级,只需要更换相应的bsp;总之改动做到最小
搭建的环境目录如下:
ucos目录是官方os代码
bsp/common目录是ucos的基于ARM7的官方SDK包
bsp/cpu目录是三星44b0x的官方SDK包,增加bsp.c文件,包含系统的外部中断处理函数,以及系统时钟的实现,延时函数,当然cpu的初始化函数port_init也在此实现
kernel目录是提供os的系统API封装,printf,udelay等
app目录是main函数入口,主要是多任务调度以及消息队列
file://D:/ucos2 (7 folders, 2 files, 445.72 KB, 4.46 MB in total.)
│ucos.elf 234.92 KB
│ucos2.mcp 210.81 KB
├─app (0 folders, 2 files, 15.86 KB, 15.86 KB in total.)
│main.c 7.64 KB
│main.c.bak 8.22 KB
├─bsp (2 folders, 0 files, 0 bytes, 125.74 KB in total.)
│├─cpu (2 folders, 0 files, 0 bytes, 67.50 KB in total.)
││├─src (0 folders, 5 files, 37.76 KB, 37.76 KB in total.)
│││44binit.s 13.63 KB
│││44blib.c 8.38 KB
│││bsp.c 4.87 KB
│││bsp.c.bak 3.00 KB
│││rtl8019.c 7.88 KB
││└─inc (0 folders, 11 files, 29.73 KB, 29.73 KB in total.)
││44b.h 15.28 KB
││44blib.h 1.54 KB
││bsp.h 460 bytes
││bsp.h.bak 486 bytes
││def.h 434 bytes
││includes.h 1.30 KB
││Memcfg.s 2.49 KB
││Option.h 837 bytes
││Option.s 1.22 KB
││rtl8019.h 5.02 KB
││types.h 751 bytes
│└─common (3 folders, 0 files, 0 bytes, 58.24 KB in total.)
│├─src (2 folders, 1 files, 11.97 KB, 31.38 KB in total.)
│││os_cpu_c.c 11.97 KB
││├─arm (0 folders, 1 files, 9.95 KB, 9.95 KB in total.)
│││os_cpu_a.S 9.95 KB
││└─gcc (0 folders, 1 files, 9.46 KB, 9.46 KB in total.)
││os_cpu_a.Sgcc 9.46 KB
│├─arm (0 folders, 1 files, 4.04 KB, 4.04 KB in total.)
││ac_halt.c 4.04 KB
│└─inc (0 folders, 5 files, 22.82 KB, 22.82 KB in total.)
│armdefs.h 2.21 KB
│def.h 2.59 KB
│halt.h 2.57 KB
│os_cfg.h 8.15 KB
│os_cpu.h 7.30 KB
├─kernel (0 folders, 2 files, 502 bytes, 502 bytes in total.)
│xkernel.c 223 bytes
│xkernel.h 279 bytes
├─lwip-1.1.0 (2 folders, 4 files, 22.65 KB, 659.37 KB in total.)
.......
├─shell (0 folders, 3 files, 36.02 KB, 36.02 KB in total.)
│cmds.c 15.17 KB
│shell.c 18.83 KB
│shell.h 2.01 KB
├─ucos (0 folders, 10 files, 294.83 KB, 294.83 KB in total.)
│os_core.c 43.44 KB
│os_flag.c 43.76 KB
│os_mbox.c 23.29 KB
│os_mem.c 13.79 KB
│os_mutex.c 27.26 KB
│os_q.c 33.79 KB
│os_sem.c 19.04 KB
│os_task.c 35.21 KB
│os_time.c 9.68 KB
│ucos_ii.h 45.59 KB
└─
1. 移植流程
内部时钟:先搭好ucos和bsp,不需要修改任何文件,新建main.c,新建两个task看能否在之间调度;显然是不可能的,所以动手写的第一行代码就是系统时钟函数,新建bsp.c,作为三星的SDK的补充来用,用timer0做系统tick,定时会产生内部中断,__vT0Interrupt是中断处理函数,这里挂ucos的BSP的API就行,当然别忘记了rINTMSK = ~(BIT_GLOBAL |BIT_TIMER0); 打开timer0中断,至于中断方式是向量中断还是非向量中断方式,在interrupt文章中记录,具体配置参考注释
rTCFG0=(MCLK>>1)/1000000-1;
/* 此为预分频值,MCLK为外部时钟频率 */
rTCFG1=0x00000000;
/* mux0 = 1/2 */
/* 定时器0输入时钟频率=MCLK/(预分频值+1)/mux0,即=MCLK/[(MCLK>>1)/1000000-1 + 1]/(1/2),计算得输入时钟频率为1M */
/* 实际应用中是根据需要的输入时钟频率来赋值1000000的 */
rTCNTB0= 1000000/OS_TICKS_PER_SEC;
/* 每一秒有OS_TICKS_PER_SEC个tick产生,这个根据实际需要更改 */
rTCMPB0= 0x0;
rTCON=0x2;
rTCON=0x9;
/* 内部中断控制寄存器配置 */
pISR_TIMER0= (unsigned)__vT0Interrupt;
中断:有点小复杂,单独新开一篇中断的总结
中断函数添加之后,调试用的printf,就是一个最基础的驱动了
int printf(const char* pbFmt, ...)
{
va_list pArg;
char abString[256];
int rv;
va_start(pArg, pbFmt);
/* 根据最后一个固定参数取可变参数表的首地址,以后可以根据该首地址逐一取各变量地址;此时pArg指向第一个可变参数 */
rv = vsprintf(abString, pbFmt, pArg);
/* 同sprintf一样*/
Uart_SendString(abString);
/* 调底层驱动 */
va_end(pArg);
return rv;
}
串口接收就需要起一个dbg_entry的task,为了方便调试,这里是porting一个命令行过来,这几个函数是从RTEMS移植过来的,可以自行添加调试命令,Uart_GetString(line)函数也可以实现记忆小功能,包括上下左右等键
void dbg_entry(void* Id)
{
unsigned char line[MAX_COUNT];
unsigned char * dbg_prompt_s ;
dbg_prompt_s = (unsigned char *) "dbg> " ;
printf("OSPrioCur = %d/n", OSPrioCur);
memset(line, 0, sizeof(line));
shell_add_cmd(NULL, NULL, NULL, NULL); /* init the chain list*/
/* 这个就是porting过来的一个命令行调试函数 */
for (; ;)
{
printf((char *) dbg_prompt_s);
if (Uart_GetString(line) < 0) /* 底层驱动函数 */
break;
if ((line[0] == 0) || (line[0] == '/n'))
continue;
process_line(line);
/* 这个也是porting过来的解析命令行调试函数 */
}
}
内存分配:挂接预先分配好的44b0x的堆的首地址和尾地址
延时函数,因为OSTimeDly是基于tick数目来的,所以做一个封装,转换成ms可读性强的API
void udelay(unsigned short ms)
{
unsigned short ucos_timeout;
if(ms)
{
ucos_timeout = (ms * OS_TICKS_PER_SEC)/1000;
if(ucos_timeout < 1)
ucos_timeout = 1;
else if(ucos_timeout > 65535)
ucos_timeout = 65535;
}
else
{
ucos_timeout = 0;
}
OSTimeDly(ucos_timeout);
}
ucos的系统api,因为ucos相对简单,任务间通信不复杂的平台采用它最好不过了,任务状态也相对简单----休眠,等待,就绪,运行
首先在os_cfg.h中使能需要用到的api,这里配置至少需要任务,事件,信号量,消息队列,系统心跳数以及时钟函数
#define OS_MAX_TASKS 63 /* Max. number of tasks in your application ... */
#define OS_FLAG_EN 1 /* Enable (1) or Disable (0) code generation for EVENT FLAGS */
#define OS_SEM_EN 1 /* Enable (1) or Disable (0) code generation for SEMAPHORES */
#define OS_Q_EN 1 /* Enable (1) or Disable (0) code generation for QUEUES */
#define OS_TICKS_PER_SEC 200 /* Set the number of ticks in one second */
#define OS_TIME_GET_SET_EN 1 /* Include code for OSTimeGet() and OSTimeSet() */
接下来就创建任务间需要传递的信号量和消息队列,当然任务也根据需要创建,注释代码如下
OS_EVENT* RecTcpQFlag;
RecPackedFlag = OSSemCreate(0);
/* 创建一个信号量 */
RecUdpQFlag = OSQCreate(&RecUdpQ[0], Q_Max_Size);
/* 创建一个消息队列,RecUdpQFlag是这个消息队列对应的事件 */
OSTaskCreate(task1, (void *) &Id1, (OS_STK *) &Stack1[STACKSIZE - 1], 1);
OSTaskCreate(task2, (void *) &Id2, (OS_STK *) &Stack2[STACKSIZE - 1], 2);
/* 创建两个任务,STACKSIZE是他们的栈大小,这个时候任务处于休眠状态 */
FRMWRK_vStartTicker(OS_TICKS_PER_SEC); /* os_cfg.h */
/* 起系统的tick */
OSStart();
/* 任务非休眠状态了,或等待或就绪或运行 */
task1通过UdpTemp = OSQPend(RecUdpQFlag, 0, &eer);处于等待状态,在等待在RecUdpQFlag事件,task2处于运行状态,如果这个时候task2调用一个发送消息队列的函数OSQPost(RecUdpQFlag, (void *) send_buf);则task可以顺利的接收数据到UdpTemp
如果这个task2也等待一个事件,并且是在task1中发送的,那么这样就死锁了
void task1(void* Id)
{
void* UdpTemp;
U8 eer;
printf("OSPrioCur = %d/n", OSPrioCur);
for (; ;)
{
UdpTemp = OSQPend(RecUdpQFlag, 0, &eer);
/* 0代表tick数,最好的方法是利用(ms * OS_TICKS_PER_SEC)/1000转换一下,ms代表n毫秒,超时后如果还没有得到消息则恢复成就绪状态,并返回error,如果该值设置成零则表示任务将持续地等待消息 */
if (eer == OS_NO_ERR)
{
printf("UdpTemp:0x%x", UdpTemp);
eer = eer;
}
}
}
再结合外围接口的驱动代码和文件系统,这样整个平台就搭建起来了,就可以开工写应用代码了,用ucos做应用也只用到这些API了,也足够应付一个普通平台的设计了
2. ucos的移植分析
EXPORT OSStartHighRdy
EXPORT OSCtxSw
EXPORT OSIntCtxSw
EXPORT UCOS_IRQHandler
和底层相关的压栈,出栈,挂接中断函数后的处理
3. 小结
买公司性质的板子,服务配套好,读书时候的板子是周立功的,平台几乎都帮你搭好了,省了很多事;相反山寨一切都要自己弄,不过更能锻炼人,对整个平台细节都非常清晰
在ADS中为了编写烧入flash跑的bin,采用-info totals -ro-base 0 -first 44binit.o(init) 这样烧入不成功的,跳了好久呢,没搞清楚 ro和rw的不同
MCLK要根据实际硬件来,要不然配置串口的时候波特率后,串口打的是乱码
OS_Q_EN默认的没开启,需要使能,要不然编译出错
uart_init在portinit之前就做,因为我中断函数加了打印信息,中断初始化后中断马上来了,这个时候串口没来得及初始化