OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
楼主: 龙之谷

菜鸟开帖,持续更新90天,顺序学习开发板大部分实验,以此帖作为一个坚持的动力

  [复制链接]

3

主题

130

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
373
金钱
373
注册时间
2015-3-7
在线时间
43 小时
发表于 2015-9-23 21:55:20 | 显示全部楼层
为人莫作千年计,三十河东四十西,莫欺少年穷。
正点原子逻辑分析仪DL16劲爆上市
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-23 23:34:10 | 显示全部楼层
第五八天  2015年09月23日  周三     例程:视频播放器实验(一)

一、AVI
1.AVI是音频交错(Audio Video Interleaved),是微软开发的一种符合RIFF文件规范的数字音频与视频文件格式。
2.AVI格式允许视频和音频交错在一起同步播放,支持256色和RLE压缩,但AVI未限定压缩标准,其仅是一个容器,用不同压缩算法生成的AVI文件,必须使用响应解压缩算法才能播放出来。
3.RIFF(Resource Interchange File Format)资源交互文件格式,是微软定义的一种用于管理WINDOWS环境中多媒体数据的文件格式。
4.AVI文件结构如图



其中,标准类型码




二、libjpeg

1.libjpeg是一个完全用C语言编写的库,包含了被广泛使用的JPEG解码、JPEG编码和其他的JPEG功能的实现。由IJG(Independent JPEG Group(独立JPEG小组))提供并维护。
2.本章,使用libjpeg来实现MJPG数据流的解码,MJPG数据流其实就是一张张的JPEG图片拼起来的图片视频流,只要能快速解码JPEG图片,就可以实现视频播放。
3.AVI解码流程如下图



以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-23 23:35:14 | 显示全部楼层
回复【301楼】Mcu_learning:
---------------------------------
加油.....
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-23 23:43:30 | 显示全部楼层
回复【302楼】龙之谷:
---------------------------------
顶楼主。。。。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

头像被屏蔽

6168

主题

7036

帖子

1

精华

论坛元老

Rank: 8Rank: 8

积分
19705
金钱
19705
注册时间
2012-12-27
在线时间
25 小时
发表于 2015-9-24 19:37:26 | 显示全部楼层
感谢分享,版区有你更精彩!
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-24 22:05:54 | 显示全部楼层
等楼主发资料,哈哈
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-24 23:33:37 | 显示全部楼层
第五九天  2015年09月24日  周四     例程:视频播放器实验(二)

前注:对第一部分JPEG解码的八个步骤请绕行!!!关于第一部分解码的八个步骤进行了简单的函数追踪,其中关键点-----在第六步中如何在循环读取数据时将解码的数据通过h2v1_merged_upsample和h2v2_merged_upsample函数将数据丢给LCD没有找到其过程,上述两个函数中确实对LCD进行了写操作,但循环函数如何调用它们还没有找到过程;另,本次分析并无价值,但亦代表了自己当前的学习水平,也是经过一番努力,故仍更新出来,仅作留念之用。
一、JPEG解码的八个步骤再次提醒,此部分绕行
[mw_shl_code=c,true]//解码一副JPEG图片,位于mjpeg.c中 //buf:jpeg数据流数组 //bsize:数组大小 //返回值:0,成功 // 其他,错误 u8 mjpegdec_decode(u8* buf,u32 bsize) { JSAMPARRAY buffer; if(bsize==0)return 1; jpegbuf=buf; jbufsize=bsize; jmempos=0;//MJEPG解码,重新从0开始分配内存 cinfo->err=jpeg_std_error(&jerr->pub); jerr->pub.error_exit = my_error_exit; jerr->pub.emit_message = my_emit_message; //if(bsize>20*1024)printf("s:%d\r\n",bsize); if (setjmp(jerr->setjmp_buffer)) //错误处理 { jpeg_abort_decompress(cinfo); jpeg_destroy_decompress(cinfo); return 2; } jpeg_create_decompress(cinfo); jpeg_filerw_src_init(cinfo); jpeg_read_header(cinfo, TRUE); cinfo->dct_method = JDCT_IFAST; cinfo->do_fancy_upsampling = 0; jpeg_start_decompress(cinfo); LCD_Set_Window(imgoffx,imgoffy,cinfo->output_width,cinfo->output_height); LCD_WriteRAM_Prepare(); //开始写入GRAM while (cinfo->output_scanline < cinfo->output_height) { jpeg_read_scanlines(cinfo, buffer, 1); } LCD_Set_Window(0,0,lcddev.width,lcddev.height);//恢复窗口 jpeg_finish_decompress(cinfo); jpeg_destroy_decompress(cinfo); return 0; }[/mw_shl_code]

1.首先,定义变量struct jpeg_decompress_struct *cinfo;
struct jpeg_decompress_struct是一个很重要的结构体数据类型,它包含着jpeg数据的详细信息,也保存着解码之后输出数据的详细信息,该结构体定义于jpeglib.h。
②一般情况每次调用libjpeg库API的时候都需要把这个变量作为第一个参数传入。
③用户可通过修改该变量来修改libjpeg行为,比如输出数据格式、libjpeg库可用的最大内存等。

其次,定义变量struct my_error_ptr jerr;其中,struct my_error_mgr结构体定义如下
[mw_shl_code=c,true]struct my_error_mgr { struct jpeg_error_mgr pub; jmp_buf setjmp_buffer; //for return to caller }; typedef struct my_error_mgr * my_error_ptr;[/mw_shl_code]

本步骤主要实现两点:其一,错误管理,使用setjmp和longjmp机制执行错误管理(释放内存、关闭文件等);其二,初始化解码对象,jpeg_create_decompress(cinfo); 此函数完成对cifo变量初始化

2.初始化解码数据源jpeg_filerw_src_init(cinfo); 
[mw_shl_code=c,true]//初始化jpeg解码数据源 static void jpeg_filerw_src_init(j_decompress_ptr cinfo) { if (cinfo->src == NULL) /* first time for this JPEG object? */ { cinfo->src = (struct jpeg_source_mgr *) (*cinfo->mem->alloc_small)((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(struct jpeg_source_mgr)); } cinfo->src->init_source = init_source; cinfo->src->fill_input_buffer = fill_input_buffer; cinfo->src->skip_input_data = skip_input_data; cinfo->src->resync_to_restart = jpeg_resync_to_restart; /* use default method */ cinfo->src->term_source = term_source; cinfo->src->bytes_in_buffer = 0; /* forces fill_input_buffer on first read */ cinfo->src->next_input_byte = NULL; /* until buffer loaded */ } [/mw_shl_code]
3.读取文件参数jpeg_read_header(cinfo, TRUE);
[mw_shl_code=c,true]GLOBAL(int) jpeg_read_header (j_decompress_ptr cinfo, boolean require_image) { int retcode; if (cinfo->global_state != DSTATE_START && cinfo->global_state != DSTATE_INHEADER) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); retcode = jpeg_consume_input(cinfo); switch (retcode) { case JPEG_REACHED_SOS: retcode = JPEG_HEADER_OK; break; case JPEG_REACHED_EOI: if (require_image) /* Complain if application wanted an image */ ERREXIT(cinfo, JERR_NO_IMAGE); /* Reset to start state; it would be safer to require the application to * call jpeg_abort, but we can't change it now for compatibility reasons. * A side effect is to free any temporary memory (there shouldn't be any). */ jpeg_abort((j_common_ptr) cinfo); /* sets state = DSTATE_START */ retcode = JPEG_HEADER_TABLES_ONLY; break; case JPEG_SUSPENDED: /* no work */ break; } return retcode; }[/mw_shl_code]
4.设置解码参数
[mw_shl_code=c,true]cinfo->dct_method = JDCT_IFAST; cinfo->do_fancy_upsampling = 0; [/mw_shl_code]
设置为假,以提高解码速度

5.开始解码
其一,jpeg_start_decompress(cinfo); 其中
[mw_shl_code=c,true]GLOBAL(boolean) jpeg_start_decompress (j_decompress_ptr cinfo) { if (cinfo->global_state == DSTATE_READY) { /* First call: initialize master control, select active modules */ jinit_master_decompress(cinfo); if (cinfo->buffered_image) { /* No more work here; expecting jpeg_start_output next */ cinfo->global_state = DSTATE_BUFIMAGE; return TRUE; } cinfo->global_state = DSTATE_PRELOAD; } if (cinfo->global_state == DSTATE_PRELOAD) { /* If file has multiple scans, absorb them all into the coef buffer */ if (cinfo->inputctl->has_multiple_scans) { #ifdef D_MULTISCAN_FILES_SUPPORTED for (;;) { int retcode; /* Call progress monitor hook if present */ if (cinfo->progress != NULL) (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); /* Absorb some more input */ retcode = (*cinfo->inputctl->consume_input) (cinfo); if (retcode == JPEG_SUSPENDED) return FALSE; if (retcode == JPEG_REACHED_EOI) break; /* Advance progress counter if appropriate */ if (cinfo->progress != NULL && (retcode == JPEG_ROW_COMPLETED || retcode == JPEG_REACHED_SOS)) { if (++cinfo->progress->pass_counter >= cinfo->progress->pass_limit) { /* jdmaster underestimated number of scans; ratchet up one scan */ cinfo->progress->pass_limit += (long) cinfo->total_iMCU_rows; } } } #else ERREXIT(cinfo, JERR_NOT_COMPILED); #endif /* D_MULTISCAN_FILES_SUPPORTED */ } cinfo->output_scan_number = cinfo->input_scan_number; } else if (cinfo->global_state != DSTATE_PRESCAN) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); /* Perform any dummy output passes, and set up for the final pass */ return output_pass_setup(cinfo); }[/mw_shl_code]
其二,在读取数据之前,做一些处理,如设定LCD窗口,设定LCD起始坐标
LCD_Set_Window(imgoffx,imgoffy,cinfo->output_width,cinfo->output_height);
LCD_WriteRAM_Prepare();     //开始写入GRAM
6.循环读取数据
[mw_shl_code=c,true]while (cinfo->output_scanline < cinfo->output_height) { jpeg_read_scanlines(cinfo, buffer, 1); } [/mw_shl_code]
其中,
[mw_shl_code=c,true]GLOBAL(JDIMENSION) jpeg_read_scanlines (j_decompress_ptr cinfo, JSAMPARRAY scanlines,JDIMENSION max_lines) { JDIMENSION row_ctr; if (cinfo->global_state != DSTATE_SCANNING) ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); if (cinfo->output_scanline >= cinfo->output_height) { WARNMS(cinfo, JWRN_TOO_MUCH_DATA); return 0; } /* Call progress monitor hook if present */ // if (cinfo->progress != NULL) { // cinfo->progress->pass_counter = (long) cinfo->output_scanline; // cinfo->progress->pass_limit = (long) cinfo->output_height; // (*cinfo->progress->progress_monitor) ((j_common_ptr) cinfo); // } /* Process some data */ row_ctr = 0; (*cinfo->main->process_data) (cinfo, scanlines, &row_ctr, max_lines); cinfo->output_scanline += row_ctr; return row_ctr; }[/mw_shl_code]
7.结束解码
[mw_shl_code=c,true]jpeg_finish_decompress(cinfo); [/mw_shl_code]
其定义
[mw_shl_code=c,true]GLOBAL(boolean) jpeg_finish_decompress (j_decompress_ptr cinfo) { if ((cinfo->global_state == DSTATE_SCANNING || cinfo->global_state == DSTATE_RAW_OK) && ! cinfo->buffered_image) { /* Terminate final pass of non-buffered mode */ if (cinfo->output_scanline < cinfo->output_height) ERREXIT(cinfo, JERR_TOO_LITTLE_DATA); (*cinfo->master->finish_output_pass) (cinfo); cinfo->global_state = DSTATE_STOPPING; } else if (cinfo->global_state == DSTATE_BUFIMAGE) { /* Finishing after a buffered-image operation */ cinfo->global_state = DSTATE_STOPPING; } else if (cinfo->global_state != DSTATE_STOPPING) { /* STOPPING = repeat call after a suspension, anything else is error */ ERREXIT1(cinfo, JERR_BAD_STATE, cinfo->global_state); } /* Read until EOI */ while (! cinfo->inputctl->eoi_reached) { if ((*cinfo->inputctl->consume_input) (cinfo) == JPEG_SUSPENDED) return FALSE; /* Suspend, come back later */ } /* Do final cleanup */ (*cinfo->src->term_source) (cinfo); /* We can use jpeg_abort to release memory and reset global_state */ jpeg_abort((j_common_ptr) cinfo); return TRUE; }[/mw_shl_code]
8.释放解码对象资源
[mw_shl_code=c,true]jpeg_destroy_decompress(cinfo);[/mw_shl_code]
函数定义
[mw_shl_code=c,true]GLOBAL(void) jpeg_destroy_decompress (j_decompress_ptr cinfo) { jpeg_destroy((j_common_ptr) cinfo); /* use common routine */ }[/mw_shl_code]
进一步跟踪
[mw_shl_code=c,true]GLOBAL(void) jpeg_destroy (j_common_ptr cinfo) { /* We need only tell the memory manager to release everything. */ /* NB: mem pointer is NULL if memory mgr failed to initialize. */ if (cinfo->mem != NULL) (*cinfo->mem->self_destruct) (cinfo); cinfo->mem = NULL; /* be safe if jpeg_destroy is called twice */ cinfo->global_state = 0; /* mark it destroyed */ }[/mw_shl_code]

二、setjmp和longjmp机制

1.使用之前加入头文件#include "setjmp.h"。
2.两者结合使用,且需遵守严格调用顺序,先setjmp,后longjmp。
3.setjmp与longjmp的作用同goto语句类似,能实现本地的跳转。但goto只能实现函数内部跳转,setjmp和longjmp则可以在整个程序全局中跳转。
4.函数原型
int setjmp(jmp_buf env);
设置jumper点,jumper是一个jum_buf类型变量,在setjmp.h文件中由jum_buf定义,是一个结构体数组。
调用该函数对env初始化,初始化后返回一个int值,第一次调用这个int值为0。

void longjmp(jmp_buf env, int val);
第一个参数是setjmp(env)中设置的jumper点;第二个参数给setjmp(env)重新赋值,为val值。
重新赋值后会跳转到setjmp处重新开始执行。
5.例子程序
[mw_shl_code=c,true]#include <stdio.h> #include <setjmp.h> void subroutine(void); void subroutine_2(void); jmp_buf jumper; int main() { int value; int i = 0; value = setjmp(jumper);//设置jump点,初始化jumper,返回值0赋给value i++; if(value == 0) { subroutine(); } else if(value == 1) { subroutine_2(); } else { //...... } return 0; } void subroutine(void) { //跳转到jumper初始化的地方,即setjmp(jumper)处,并将1赋给setjmp(jumper) longjmp(jumper, 1); return; } void subroutine_2(void) { longjmp(jumper, 3); return; }[/mw_shl_code]

执行过程示意图


参考/相关链接
【1】http://blog.sina.com.cn/s/blog_5b7258900100p1in.html
【2】http://blog.chinaunix.net/uid-7673620-id-2598668.html
【3】http://wenku.baidu.com/view/ddc18deb6294dd88d0d26b17.html
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-25 01:05:35 | 显示全部楼层
回复【305楼】xouou_53320:
---------------------------------
谢谢支持

不知道是不是自身网速问题,打开TinyPic过程略卡
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-25 01:06:03 | 显示全部楼层
回复【306楼】正点原子:
---------------------------------
汗颜+无地自容+找地缝中.....
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-25 22:14:57 | 显示全部楼层
回复【309楼】龙之谷:
---------------------------------
已经很不错了.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-25 23:14:15 | 显示全部楼层
第六十天  2015年09月25日  周五     例程:FPU测试(Julia分形)实验

一、FPU
1.FPU即浮点运算单元(Float Point Unit)。
2.浮点运算,对于定点CPU(无FPU的CPU)需按照IEEE-754标准的算法完成运算,相当耗时。而对于由FPU的CPU来说,只需几条指令,速度相当快。
3.STM32F4属于Cortex M4F架构,带有32位单精度硬件FPU,支持浮点指令集。
4.STM32F4开启FPU步骤:
其一,设置CPACR寄存器bit20~23为1,使能硬件FPU,其使能代码在startup_stm32f40_41xxx.s文件中Reset_Hander函数中,



其二,MDK编译器Code Generation里面设置:Use FPU



二、Julia分形
1.Julia分形即Julia集,最早由法国数学家Gaston Julia发现而得名。
2.对于Julia分形,高数渣渣表示不能理解,单纯手动截图如下


以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-25 23:17:12 | 显示全部楼层
本章Julia及下章DSP涉及数学方面内容脑细胞跟不上,一览而过,也趁此机会偷懒一下吧
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-26 12:01:53 | 显示全部楼层
恭喜楼主完成三分之二了。。。。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-26 14:33:10 | 显示全部楼层
回复【313楼】正点原子:
---------------------------------
是啊,六十天过去了,离预期有一定差距,但也学到了知识,主要是自己不够踏实耐心

时间过得还是挺快的,无论快乐还是感伤,无论空闲还是繁忙,一切都会过去
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-26 14:57:16 | 显示全部楼层
小福利

公司中秋发了苹果、梨各一箱,自己吃不完,有没有附近的坛友可以来领取(自带袋)

刚才看了一下,苹果15个、梨40多个,留一些(三分之一或四分之一)自己吃以外,如有符合以下条件者可来免费领取(如有符合条件者不要羞涩,这也是在帮我合理使用这些食物)

1.注册时间超过1天,一般坛友可领取苹果2个、梨5个
2.符合以下任一条件可领取苹果5个、梨10个:乐于帮助别人的坛友(在别人帖子回复帮助别人超过10次)、等级为5钻、在本帖或以前个人帖子回复我帮助/鼓励我的可爱的坛友

我的位置:石家庄桥西区
领取方式:在本帖或通过短信等可以联系上我的方式通知我
截止时间:即日起至苹果/梨   没吃完/没送完
当前时间:2015年09月26日
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-26 23:05:31 | 显示全部楼层
第六一天  2015年09月26日  周六     例程:DSP实验

一、STM32F4在数字信号处理方面增加了DSP指令集,执行所有DSP指令集都可以在单周期内完成,而Cortex-M3则需要多个指令和多个周期才能完成同样功能。

二、DSP运行环境搭建
1.添加文件arm_cortexM4lf_math.lib和相关头文件并将其包含进工程
2.添加几个全局宏定义




三、几个“头大”的概念

1.FFT即快速傅里叶变换,可以将一个时域信号变换到频域。因为有些信号在时域上很难看出什么特征,如果变换到频域之后,就很容易看出特征,这就是很多信号分析采用FFT的原因。
2.实际应用中,一般处理过程是先对一个信号在时域进行采集,如通过ADC,按照一定大小采样频率F去采集信号,采集N个点,那么通过对这N个点进行FFT运算,就可以得到这个信号灯频谱特性。
3.采样定理,又称奈奎斯特定理,即在进行模拟/数字信号的转换过程中,当采样频率f大于信号中最高频率fmax的2倍时,采样之后的数字信号完整的保留了原始信号的信息。
4.N个采样点数据,经过FFT之后,就可以得到N个点的FFT结果,为了方便进行FFT运算,通常N取2的整数次方。
5.



6.如果按照F的采样频率采集一个信号的N个点,那么基波频率(频率分辨率)就是f=F/N,第n个点对应信号频率为:F*(n-1)/N;其中,n大于等于1,当n=1时为直流分量。
7.STM32F4的DSP库里面提供了定点和浮点FFT实现,且有基4的也有基2的,对于基4的FFT输入点数必须是4的n次方,而基2的FFT输入点数则是2的n次方,并且基4的FFT算法要比基2的快。
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-27 11:23:31 | 显示全部楼层
第六二天  2015年09月27日  周日     例程:手写识别实验

一、手写识别理论部分

1.手写识别过程:其一,训练学习过程;其二,识别过程。


2.识别系统示意图如下



3.训练学习过程如下图



4.识别过程如下




二、手写识别应用


1.添加文件:

    ATKNCR_M_V2.0.lib/ATKNCR_N_V2.0.lib:识别的库文件(二选一),前者使用需内存管理,后者则通过全局变量定义缓冲区(大于3K的RAM)
    atk_ncr.c:实现内存管理函数
    atk_ncr.h:识别库文件同外部函数的接口函数声明

2.实现手写识别四步骤:

其一,调用alientek_ncr_init函数,初始化识别程序
[mw_shl_code=c,true] alientek_ncr_init(); //初始化手写识别 [/mw_shl_code]
其二,获取输入的点阵数据,通过触屏获取输入轨迹点阵坐标,然后存放到一个缓存区,输入点数推荐范围【100,200
[mw_shl_code=c,true] if(tp_dev.sta&TP_PRES_DOWN)//有按键被按下 { delay_ms(1);//必要的延时,否则老认为有按键按下. tcnt=0;//松开时的计数器清空 if((tp_dev.x[0]<(lcddev.width-20-2)&&tp_dev.x[0]>=(20+2))&&(tp_dev.y[0]<(lcddev.height-5-2)&&tp_dev.y[0]>=(115+2))) { if(lastpos[0]==0XFFFF) { lastpos[0]=tp_dev.x[0]; lastpos[1]=tp_dev.y[0]; } lcd_draw_bline(lastpos[0],lastpos[1],tp_dev.x[0],tp_dev.y[0],2,BLUE);//画线 lastpos[0]=tp_dev.x[0]; lastpos[1]=tp_dev.y[0]; if(pcnt<200)//总点数少于200 { if(pcnt) { if((READ_BUF[pcnt-1].y!=tp_dev.y[0])&&(READ_BUF[pcnt-1].x!=tp_dev.x[0]))//x,y不相等 { READ_BUF[pcnt].x=tp_dev.x[0]; READ_BUF[pcnt].y=tp_dev.y[0]; pcnt++; } }else { READ_BUF[pcnt].x=tp_dev.x[0]; //<-----② READ_BUF[pcnt].y=tp_dev.y[0]; pcnt++; } } } }[/mw_shl_code]
其三,调用alientek_ncr函数,得到识别结果,结果将保存在result参数里面,采用ASCII码格式存储
[mw_shl_code=c,true]else //按键松开了 { lastpos[0]=0XFFFF; tcnt++; delay_ms(10); //延时识别 i++; if(tcnt==40) { if(pcnt)//有有效的输入 { printf("总点数:%d\r\n",pcnt); alientek_ncr(READ_BUF,pcnt,6,mode,(char*)res); //<-----③ printf("识别结果:%s\r\n",res); pcnt=0; POINT_COLOR=BLUE;//设置画笔蓝色 LCD_ShowString(60+72,90,200,16,16,res); } LCD_Fill(20,115,lcddev.width-20-1,lcddev.height-5-1,WHITE); } } [/mw_shl_code]
其四,调用alientek_ncr_stop函数,终止识别(不需要继续识别时调用,如需继续重复二三步即可)
[mw_shl_code=c,true]//停止识别器 void alientek_ncr_stop(void); [/mw_shl_code]

3.对手写识别四步骤的注释补充

首先,轨迹点数的定义
[mw_shl_code=c,true]//最大记录的轨迹点数 atk_ncr_point READ_BUF[200]; [/mw_shl_code]
然后,识别函数参数含义
[mw_shl_code=c,true]//识别器识别 //track:输入点阵集合 //potnum:输入点阵的点数,就是track的大小 //charnum:期望输出的结果数,就是你希望输出多少个匹配结果 //mode:识别模式 //1,仅识别数字 //2,仅识别大写字母 //3,仅识别小写字母 //4,混合识别(全部识别) //result:结果缓存区(至少为:charnum+1个字节) void alientek_ncr(atk_ncr_point * track,int potnum,int charnum,unsigned char mode,char*result);[/mw_shl_code]
最后,画线函数参数含义
[mw_shl_code=c,true]//画一条粗线 //(x1,y1),(x2,y2):线条的起始坐标 //size:线条的粗细程度 //color:线条的颜色 void lcd_draw_bline(u16 x1, u16 y1, u16 x2, u16 y2,u8 size,u16 color)[/mw_shl_code]

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2015-9-27
在线时间
1 小时
发表于 2015-9-27 12:47:50 | 显示全部楼层
第〇一天:2015年07月22日  周三     例程:跑马灯实验

我想请教一下LZ,你调用的 delay_ms 函数,使用的是delay.c 源文件, 从中文注释不难看出是原点自己写的实现。
不是API或者库函数。 也就是说,延迟等函数,你都不是自己写的,是直接用的原点的,这样对学习也有帮助吗?
我的感觉就像是在学易语言一样,别人封装好的模块,你只是负责调用模块接口而已。

我是一个刚刚接触的新人,电路什么的也都是一点都不懂,这只是我个人的一个小疑问,如果有什么不懂,问错的地方,希望LZ能见谅。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-27 13:18:51 | 显示全部楼层
回复【318楼】plmmzhangshun:
---------------------------------
 咱们论坛是有爱的论坛,只要学习态度端正、提问方式合理,都会乐于互相帮助的,坛友可放心

延迟函数等许多都是基于原子哥sys文件夹,这些原子哥提供的接口给STM32的开发提供了很大便利,工作中的项目开发也是以此为基础的,它也相当于API了,我们使用的时候直接调用就可以,会节省很多时间

对于你的问题,整体读下来我理解起来不是很顺畅,你可以分成1.2.3.点问,便于理解
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2015-9-27
在线时间
1 小时
发表于 2015-9-27 13:36:07 | 显示全部楼层
1.  我不明白 delay_ms  是原子大哥封装的函数? 还是 单片机本身自带的函数?
2.  如果是原子哥封装的函数,那么我们学习起来是否有帮助? 
    (因为我感觉这样子就好比别人写好的产品,我们只是负责使用这个产品。学习单片机的过程,不是应该自己制造产品,而不是直接使用别人的产品,不是吗?)
3.  如果是系统的API函数。那为什么原子大哥可以直接在源文件上做中文注释做修改?
    (在windows开发中,无论是 c  c++  asm  vb  等语言, 都是不能参与API修改的。)

祝楼主中秋佳节快乐。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-27 13:59:02 | 显示全部楼层
回复【320楼】plmmzhangshun:
---------------------------------
1.delay_ms函数时原子哥的函数,在sys文件夹delay.c中实现的
2.这是非常底层的函数,每个产品基本都会用到这样的底层函数,这个函数跟产品中间还有一段距离,就像炒菜,别管炒什么菜都需要锅
3.原子哥的sys文件夹非常经典,使用很广泛,工作中也是在此基础进行开发的,所以可以看成是API进行直接调用即可
4.要静下心来慢慢学,很多问题在学习中慢慢就理解了,别人解答你只能理解表面,无法深入掌握。个人没有其他语言开发经验,你的基础很好,如果C语言掌握的不错,相对来说学起来会很快的。

同样祝你中秋节快乐~~~~~
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

0

主题

3

帖子

0

精华

新手上路

积分
31
金钱
31
注册时间
2015-9-27
在线时间
1 小时
发表于 2015-9-27 14:02:11 | 显示全部楼层
感谢为我答疑。
回复 支持 反对

使用道具 举报

3

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2015-9-27
在线时间
5 小时
发表于 2015-9-27 20:34:24 | 显示全部楼层
支持一个  同是初学者  特地来看看
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-28 00:12:21 | 显示全部楼层
回复【323楼】squall001:
---------------------------------
共勉.....
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

3

主题

11

帖子

0

精华

初级会员

Rank: 2

积分
58
金钱
58
注册时间
2015-9-27
在线时间
5 小时
发表于 2015-9-28 00:33:33 | 显示全部楼层
回复【324楼】龙之谷:
---------------------------------
http://www.openedv.com/posts/list/60391.htm
这个帖子帮我看看吧
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-28 21:36:30 | 显示全部楼层
支持....
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-28 23:15:48 | 显示全部楼层
第六三天  2015年09月28日  周一     例程:T9拼音输入法实验

一、T9输入法全名智能输入法,字库九千多姿,支持十多种语言,由美国特捷通讯(Tegic Communications)软件公司开发。

二、通过一个和数字串对应的拼音索引码来实现T9拼音输入:
第一步,先将汉语拼音所有可能的组合(称之为码表)全部列出来



第二步,将以上码表与其对应的数字串对应起来,组成一个拼音索引码


其中,py_index是一个结构体,定义如下
[mw_shl_code=c,true]//拼音码表与拼音的对应表 typedef struct { u8 *py_input;//输入的字符串 u8 *py; //对应的拼音 u8 *pymb; //码表 }py_index;[/mw_shl_code]

有了以上索引表之后,只需要将输入的数字串和py_index3索引表里面所有成员的py_input对比,将所有完全匹配的情况记录下来,用户要输入的汉字就被确定了,然后由用户选择可能的拼音组成(假设由多个匹配的项目),再选择对应的汉字,即完成依次汉字输入。当然如果找遍索引表,也没有发现一个完全符合要求的成员,那么会将从左到右连续匹配最多字符的情况,作为最佳结果,反馈给用户。

三、实现过程
1.键盘按下,在主函数记录按下按键值,将其保存至数组inputstr[inputlen]中;
2.在get_matched_pymb函数中通过调用str_match函数对按键数组进行比对,找出完全匹配或最接近结果,并将其存入t9.pymb数组中;
3.py_show_result函数通过其参数(传递为2中数组的下标)将t9.pymb中匹配的拼音对应汉字按照码表顺序输出。

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-29 22:29:24 | 显示全部楼层
本帖最后由 龙之谷 于 2016-1-28 09:18 编辑

<span style="font-size:16px;">第**天 &nbsp;2015年09月29日 &nbsp;周二 &nbsp; &nbsp; 例程:串口IAP实验(一)</span><br>
<span style="font-size:16px;"></span><br>
<span style="font-size:18px;color:#E53333;">一、IAP理论部分</span><br>
<span style="font-size:16px;"><br>
1.定义:IAP(In Application Programming)即在应用编程,IAP是用户自己的程序在运行的过程中对User Flash的部分进行烧写。</span><br>
<span style="font-size:16px;"><br>
2.目的:为了在产品发布后可以方便的通过预留的通信口对产品中的固件程序进行更新升级。</span><br>
<span style="font-size:16px;"><br>
3.实现:实现IAP功能时,即用户程序运行中作自身的更新操作,需要在设计固件程序时编写两个项目代码:</span><br>
<span style="font-size:16px;"></span><span style="font-size:16px;">第一个项目程序不执行正常的功能操作,而只是通过某种通信方式(如USB、USART)接收程序或数据,执行对第二部分代码的更新;第二个项目代码才是真正的功能代码。</span><span style="font-size:16px;"></span><br>
<span style="font-size:16px;">以上两个项目代码同时烧录在User Flash中,当芯片上电后,首先时第一个项目代码开始运行,作如下操作:</span>

<br>
<span style="font-size:16px;"></span><br>
<span style="font-size:16px;"><br>
4.烧录:第一部分代码必须通过其他手段,如JTAG或ISP烧入;第二部分代码可以使用第一部分代码IAP功能烧入,也可以和第一部分代码一起烧入,以后需要程序更新时再通过第一部分IAP代码更新。</span><br>
<span style="font-size:16px;"><span style="font-size:16px;"><br>
5.存放:将第一个项目代码称之为Bootloader程序,一般从最低地址开始存放;第二个项目代码称之为APP程序,紧跟第一个项目代码其后。(注:如果FLASH容量足够,可以设计很多APP程序)</span><br>
<span style="font-size:16px;"> <br>
6.环境:可以运行在FLASH中,也可以运行在SRAM中。在SRAM中运行的过程和在FLASH基本一致,只是需要设置向量表的地址为SRAM的地址。</span></span><br>
<span style="font-size:16px;"><span style="font-size:16px;"><br>
7.STM32F4正常程序运行流程</span><br>

</span><br>
<span style="font-size:16px;"></span><br>
<span style="font-size:16px;"><span style="font-size:16px;">STM32F4是基于Cortex-M4内核的微控制器,其内部通过一张“中断向量表”来响应中断,根据中断源取出对应的中断向量执行中断服务程序。</span><br>
<span style="font-size:16px;"> 如上图中,STM32F4复位后,</span><br>
<span style="font-size:16px;"> ①先从0X08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序;</span><br>
<span style="font-size:16px;"> ②在复位中断服务程序执行完之后,会跳转到main函数;</span><br>
<span style="font-size:16px;"> ③在main函数一般都是死循环,其执行过程中如果接收到中断请求,此时会强制将PC指针指回中断向量表处;</span><br>
<span style="font-size:16px;"> ④根据中断源进入相应中断服务函数;</span><br>
<span style="font-size:16px;"> ⑤执行完中断服务函数后,程序再次放回main函数执行。</span></span><br>
<span style="font-size:16px;"><span style="font-size:16px;"><br>
8.加入IAP后的程序运行流程</span><br>

</span><br>
<span style="font-size:16px;"></span><br>
<span style="font-size:16px;"><span style="font-size:16px;">如上图,STM32F4复位后,</span><br>
<span style="font-size:16px;"> 还是从0X08000004地址取出复位中断向量的地址,并跳转到复位中断服务程序,在运行完复位中断服务程序之后跳转到IAP的main函数,如标号①;</span><br>
<span style="font-size:16px;"> 在执行完IAP以后(即将新的APP代码写入STM32F4的FLASH,灰底部分,新程序复位中断向量起始地址为0X08000004+N+M),跳转至新写入程序的复位向量表,取出新程序复位中断向量的地址,并跳转到执行新程序的复位中断服务程序,随后跳转至新程序main函数,如标号②和③,同样main函数为一个死循环;</span><br>
<span style="font-size:16px;"> 在上图,不同位置有两个中断向量表,但在main函数执行过程中,如果cpu得到一个中断请求,PC指针仍强制跳转到地址0X08000004中断向量表处,而不是新程序中断向量表,如标号④;</span><br>
<span style="font-size:16px;"> 程序再根据我们设置的中断向量表偏移,跳转到对应中断源新的中断服务程序中,如标号⑤;</span><br>
<span style="font-size:16px;"> 执行完中断服务程序后,程序返回main函数继续运行,如标号⑥。</span><br>
<span style="font-size:16px;"> <br>
9.IAP程序必须满足两个要求:</span><br>
<span style="font-size:16px;"> 其一,新程序必须在IAP程序之后的某个偏移量为x的地址开始;</span><br>
<span style="font-size:16px;"> 其二,必须将新程序的中断向量表响应的移动,移动偏移量为x。</span><br>
<br>
<span style="font-size:16px;"> <span style="color:#E53333;font-size:18px;">二、IAP配置部分</span><br>
</span><span style="font-size:16px;"><br>
0.Bootloader及其他普通程序常规地址配置(放置于此仅作对照)</span><br>

<br>
<br>
<span style="font-size:16px;"> <br>
1.FLASH中APP</span><br>
<span style="font-size:16px;"> 其一,起始地址设置。点击Option fot target-&gt;Target</span><br>

</span><br>
<span style="font-size:16px;"><br>
<span style="font-size:16px;"> 默认条件下,图中IROM1的起始地址(Start)一般为0X08000000,大小(Size)为0X100000,即从0X08000000开始的1024K空间为程序存储区。而上图我们设置起始地址(Start)为0X08008000,即偏移量0X8000(32K字节),因而,留给APP用的FLASH空间(Size)为0X1000000-0X8000=0XF8000(992K字节)大小。</span><br>
<span style="font-size:16px;"> 以上完成了APP程序的起始地址设置,这里的32K字节,需大家根据Bootloader程序大小进行选择,如本章的Bootloader的大小为27K左右,理论上只需要确保APP起始地址在Bootloader之后,并且偏移量为0x200的倍数即可。选择32K(0X8000)字节,留有了一些余量,便于Bootloader以后的升级修改。</span><br>
<span style="font-size:16px;"> 其二,中断向量表的偏移量设置方法。通过修改sys.c里面的Stm32_Clock_Init函数</span><br>

<br>
<span style="font-size:16px;"> 只需要修改最后两行代码,默认情况下VECT_TAB_RAM是没有定义的的,执行MY_NVIC_SetVectorTable(0, 0x0);这是正常情况的向量表偏移量(为0),本章,修改为偏移量0x8000,即</span><span style="font-size:16px;line-height:24px;">MY_NVIC_SetVectorTable(0, 0x8000。</span><br>
<br>
<span style="font-size:16px;"> <br>
2.SRAM中APP</span><br>
<span style="font-size:16px;"> 其一,</span><span style="font-size:16px;line-height:24px;"><span style="font-size:16px;">起始地址设置。点击Option fot target-&gt;Target</span><br>

</span><br>
<span style="font-size:16px;"> 这里将IROM1的起始地址(Start)定义为0X20001000,大小为0X19000(100K字节),即从地址0X20000000偏移0X1000开始,存放APP代码。因为整个STM32F407ZGT6的SRAM大小(不计CCM)为128K,所以IRAM1(SRAM)的起始地址变为0X2001A000,大小只有0X6000(24K字节)。这样,整个芯片的SRAM(不计CCM)分配情况:最开始4K给Bootloader程序使用,随后的100K存放APP程序,最后24K用作APP程序内存。(此处我们设置偏移量为0X1000,可根据实际情况修改,但需保证为0X200的倍数)</span><br>
<span style="font-size:16px;"> 其二,</span><span style="font-size:16px;line-height:24px;">中断向量表的偏移量设置方法。</span><span style="font-size:16px;line-height:24px;"><span style="font-size:16px;">通过修改sys.c里面的Stm32_Clock_Init函数</span><br>
<span style="font-size:16px;"> 先定义VECT_TAB_RAM,点击Options for Target-&gt;C/C++选项卡,在Preprocessor Symbols栏添加定义:VECT_TAB_RAM。</span><br>

<br>
<span style="font-size:16px;"> 然后即可对代码进行修改,MY_NVIC_SetVectorTable(1&lt;&lt;29, 0x0);这里0X0是默认的设置,修改此句为:MY_NVIC_SetVectorTable(1&lt;&lt;29, 0X1000),即设置偏移量为0X1000。</span><br>
</span><br>
<span style="font-size:16px;"> <br>
3.MDK默认生成的文件是.hex文件,并不方便用作IAP更新,我们希望生成的文件是.bin文件,这样可以方便进行IAP升级。可以通过MDK自带格式转换工具fromelf.exe来实现.axf文件到.bin文件的转换(转换过程参考探索者开发指南716页或以下相关链接)。</span><br>
<br>
<br>
<span style="font-size:16px;"> <span style="font-size:18px;color:#E53333;">参考/相关链接:</span><br>
</span><br>
<span style="font-size:18px;"> 【1】bin文件生成-----http://www.openedv.com/posts/list/34031.htm</span><br>
<span style="font-size:18px;"> 【2】hex文件与bin文件区别-----http://wenku.baidu.com/view/ba3043265901020207409cf2.html</span><br>
<span style="font-size:18px;"> 或http://www.openedv.com/posts/list/7621.htm</span><br>
<span style="font-size:18px;"> 【3】bin文件生成-----http://blog.163.com/tianjunqiang ... 119201343011487819/</span><br>
<span style="font-size:18px;"> 【4】bin文件生成-----</span><span style="font-size:18px;line-height:24px;">http://wenku.baidu.com/link?url=5OnbsdMuay0SDhjxCa_A8o9JjxWZyNLp02_QRMjPIok_Doz00Rt8p77t6c44LJpQIm_Y-VocsPtx50w6kTsvLTWICDU1AJJ25xoA0fsV6My</span><span style="font-size:16px;line-height:24px;"></span><br>
<span style="font-size:18px;"> 【5】战舰IAP讨论帖-----http://www.openedv.com/posts/list/11494.htm</span><br>
<span style="font-size:18px;"> 【6】坛友带CRC和重发IAP程序-----http://www.openedv.com/posts/list/38799.htm</span><br>
<span style="font-size:18px;"> 【7】坛友SD卡实现IAP升级-----http://www.openedv.com/posts/list/10027.htm</span><br>
<span style="font-size:18px;"> 【8】http://www.openedv.com/posts/list/27156.htm</span><br>
<span style="font-size:18px;"> 【9】GPRS远程升级IAP-----http://www.openedv.com/posts/list/42639.htm</span><br>
<span style="font-size:18px;"> 【10】讨论帖-----http://www.openedv.com/posts/list/60187.htm</span><br>
<span style="font-size:18px;"> 【11】资料下载帖(没注册,无法下载,不知是否可用)-----http://www.pudn.com/downloads467 ... /detail1961749.html<br>
【12】NVIC向量偏移-----http://www.openedv.com/posts/list/392.htm</span></span><br>
<span style="font-size:16px;"></span>
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-29 22:30:35 | 显示全部楼层
回复【328楼】龙之谷:
---------------------------------
今天好早啊,呵呵.
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-29 22:32:39 | 显示全部楼层
回复【329楼】正点原子:
---------------------------------
今天下雨公司停电,下午就没上班,玩会游戏,更新一下,这种节奏赶脚不错~~~~~
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-29 22:36:20 | 显示全部楼层
回复【330楼】龙之谷:
---------------------------------
天天停电就爽了
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-29 22:53:17 | 显示全部楼层
回复【331楼】正点原子:
---------------------------------
老大说出了我们公司员工的心声,哈哈
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-30 22:47:21 | 显示全部楼层
第六五天  2015年09月30日  周三     例程:串口IAP实验(二)

一、串口接收APP程序后,①SRAM APP,可通过跳转函数直接执行这个接收到的SRAM APP程序;②FLASH APP将串口接收到的APP程序存放到STM32F4的FLASH,之后再通过跳转函数执行这个FLASH APP。

二、用户代码区第一个字用于存放栈顶地址,第二个字为开始地址(复位地址)。

三、FLASH APP流程
1.串口接收。通过串口接收APP程序,在usart.h中,定义USART_REC_LEN为120K字节,即串口最大一次可接收120k字节的数据,通过USART_RX_CNT计数
[mw_shl_code=c,true]u8 USART_RX_BUF[USART_REC_LEN] __attribute__ ((at(0X20001000)));//接收缓冲,最大USART_REC_LEN个字节,起始地址为0X20001000. [/mw_shl_code]
[mw_shl_code=c,true] if(USART_RX_CNT) { if(oldcount==USART_RX_CNT)//新周期内,没有收到任何数据,认为本次数据接收完成. { applenth=USART_RX_CNT; oldcount=0; USART_RX_CNT=0; printf("用户程序接收完成!\r\n"); printf("代码长度:%dBytes\r\n",applenth); }else oldcount=USART_RX_CNT; } ......//其他处理代码[/mw_shl_code]

2.接收完成后,判断用户代码去第二个字是否是有效值,如果是,则将代码更新到FLASH中
[mw_shl_code=c,true] if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX.USART_RX_BUF[4]处开始 { iap_write_appbin(0x08008000,USART_RX_BUF,applenth);//更新FLASH代码 LCD_ShowString(30,210,200,16,16,"Copy APP Successed!!"); printf("固件更新完成!\r\n"); }[/mw_shl_code]
其中,iap_write_appbin()函数用于将串口接收buf里面的APP程序写入到FLASH
[mw_shl_code=c,true]u32 iapbuf[512]; //2K字节缓存 //appxaddr:应用程序的起始地址 //appbuf:应用程序CODE. //appsize:应用程序大小(字节). void iap_write_appbin(u32 appxaddr,u8 *appbuf,u32 appsize) { u32 t; u16 i=0; u32 temp; u32 fwaddr=appxaddr;//当前写入的地址 u8 *dfu=appbuf; for(t=0;t<appsize;t+=4) { temp=(u32)dfu[3]<<24; temp|=(u32)dfu[2]<<16; temp|=(u32)dfu[1]<<8; temp|=(u32)dfu[0]; dfu+=4;//偏移4个字节 iapbuf[i++]=temp; if(i==512) { i=0; STMFLASH_Write(fwaddr,iapbuf,512); fwaddr+=2048;//偏移2048 512*4=2048 } } if(i)STMFLASH_Write(fwaddr,iapbuf,i);//将最后的一些内容字节写进去. }[/mw_shl_code]
3.跳转到APP程序运行
[mw_shl_code=c,true] if(((*(vu32*)(0x08008000+4))&0xFF000000)==0x08000000)//判断是否为0X08XXXXXX. { iap_load_app(0x08008000);//执行FLASH APP代码 }[/mw_shl_code]
其中,iap_load_app()函数实现了程序跳转
[mw_shl_code=c,true]//跳转到应用程序段 //appxaddr:用户代码起始地址. void iap_load_app(u32 appxaddr) { if(((*(vu32*)appxaddr)&0x2FFE0000)==0x20000000) //检查栈顶地址是否合法.栈位于SRAM中 { jump2app=(iapfun)*(vu32*)(appxaddr+4); //用户代码区第二个字为程序开始地址(复位地址) MSR_MSP(*(vu32*)appxaddr); //初始化APP堆栈指针(用户代码区的第一个字用于存放栈顶地址) jump2app(); //跳转到APP. } } [/mw_shl_code]
附:
[mw_shl_code=c,true]typedef void (*iapfun)(void); //定义一个函数类型的参数. [/mw_shl_code]
[mw_shl_code=c,true]iapfun jump2app; [/mw_shl_code]
[mw_shl_code=c,true][/mw_shl_code] [mw_shl_code=c,true]//设置栈顶地址 //addr:栈顶地址 __asm void MSR_MSP(u32 addr) { MSR MSP, r0 //set Main Stack value BX r14 }[/mw_shl_code]

iap_load_app()函数
先判断栈顶地址是否合法,在得到合法的栈顶地址后,通过MSR_MSP()函数设置栈顶地址,最后通过一个虚拟函数jump2app跳转到APP程序执行代码。

四、SRAM APP流程
1.串口接收APP代码。
2.判断代码区第二个字是否是有效值,如果是,直接跳转到APP代码执行
[mw_shl_code=c,true] if(((*(vu32*)(0X20001000+4))&0xFF000000)==0x20000000)//判断是否为0X20XXXXXX. { iap_load_app(0X20001000);//SRAM地址 }[/mw_shl_code]
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-9-30 22:59:48 | 显示全部楼层
回复【333楼】龙之谷:
---------------------------------
明天放假了。。。。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-9-30 23:07:47 | 显示全部楼层
回复【334楼】正点原子:
---------------------------------
是啊,晚上给家里打了电话这次不回家了,以后年年回,家里还有五亩玉米待收,自己想趁这个假期加把劲,以后很难有这样的学习机会了,越来越感到时间紧,唉,纠结...

原子哥十一什么安排啊
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-10-1 08:59:02 | 显示全部楼层
实验51~实验55关于USB、网络通信的章节先跳过
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-10-1 11:03:05 | 显示全部楼层
第六六天  2015年10月01日 09:07:31  周四     例程:UCOSII实验(一)

一、UCOSII是一个可以基于ROM运行的、可裁剪的、抢占式、实时多任务内核,具有高度可移植性,特别适合微处理器和控制器,是和很多商业操作系统性能相当的实时操作系统(RTOS)。

二、UCOSII体系结构


上图中,定时器的作用是为了UCOSII提供系统时钟节拍,实现任务切换和任务延时等功能。这个时钟节拍由OS_TICKS_PER_SEC(在os_cfg.h中定义)设置,一般设置UCOSII的系统时钟节拍为1ms~100ms,具体根据处理器和使用需要来设置。开发指南例程使用SYSTICK定时器来提供UCOSII时钟节拍。

三、UCOSII文件框图




四、工程文件添加




五、知识点琐记
1.任务,其实就是一个死循环函数,实现一定功能,一个工程可以有很多这样的任务,UCOSII对这些任务进行调度管理,让这些任务可以并发工作(不是同时工作!!)。并发只是各个人物轮流占用CPU,任何时候只有1个任务能够占用CPU。
2.任务调度其实就是CPU运行环境的切换,即:PC指针、SP指针和寄存器组等内容的存取过程。

------------------------------------以下知识点摘自ucosii在STM32上的移植详解.pdf-------------------------------------------------


3.移植的目标芯片是STM32,但操作系统的移植基本是针对Cortex-M4内核而言的,所以只需了解CM4内核就好。STM32芯片就是CM4内核加上各种各样的外设。

4.操作模式:①处理者模式,异常处理;②线程模式,主程序。
5.特权级别:①特权级,程序始终工作在特权级;②用户级。
6.向量表,初始在0X00000000处,可以通过向量表偏移量寄存器(VTOR)(地址0XE000ED08)更改,一般无需更改。
7.中断/异常相应序列,当CM3开始响应一个中断时:①xPSR、PC、LR、R12、R3、R2、R1、R0入栈;②取向量;③选择堆栈指针MSP/PSP,更新堆栈指针SP,更新连接寄存器LR,更新程序计数器PC。
8.汇编与C接口:①当主调函数序号传递参数(实参)时,他们使用R0-R3。其中,R0传递第一个,R1传递第二个...在返回时,把返回值写到R0中。②在函数中,用汇编写代码时,R0-R3、R12可以随便使用,R4-R11则必须先PUSH后POP。
9.CM3中,栈是由高地址向低地址增长的,因此OS_STK_GROWTH定义为1。
10.中断后栈中情形:①xPSR、PC、LR、R12、R3、R2、R1、R0被自动保存到栈中,R11-R4如果需要保存只能手工保存;②这些值里R12-R1都没有什么意义;③PC肯定指向任务入口;④R0用于传递任务函数的参数,因此等于p_arg。
11.必须在调用OSStart()后,才能开启时钟节拍器(SysTick),一般会把它放在第一个任务(启动任务)中。(例程貌似没有相关点体现,此处不清楚)
12.一般应用程序都有多个任务,任务可以分为周期任务和非周期任务。周期任务是周期性循环处理事情的任务,而非周期任务一般是某个条件触发才执行的任务。对于周期任务SysTick的时间一般取周期性任务中时间最短的任务值。当然,在SysTick时间较小时,要注意系统负荷问题,这时最好测一下CPU使用率及各个任务的时间等。


参考/相关链接:
【1】钩子函数讨论帖-----http://www.openedv.com/posts/list/0/54308.htm
【2】裸机程序(裸奔)-----http://zhidao.baidu.com/link?url=5HEuR5E2kzM7OtzpDYhoabTS0-UEUK70D2YVJzd7dHXEDV5LZEhEDnbiIFgWtX8pm_JoBU_3Ac9WMLPUfQfuYa


------------------------------------------------------------------------------------------------------------------------------------------------

注:附件是在光盘资料中Ucos-邵贝贝老师书籍word版本基础上进行对个人感觉重要点加入背景色以便突出,随后上传此类附件皆是同理,不再说明。↓↓↓↓↓

第1章-范例.doc

331.5 KB, 下载次数: 921

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

38

主题

526

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1419
金钱
1419
注册时间
2011-11-27
在线时间
122 小时
发表于 2015-10-1 13:24:17 | 显示全部楼层
国庆节楼主还在努力学习,楼主的精神值得学习!
永远保持一颗学习的心态。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-10-1 17:00:00 | 显示全部楼层
回复【338楼】科科1987:
---------------------------------
共勉~~~~~
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-10-1 17:14:23 | 显示全部楼层
第六六天  2015年10月01日 17:08:06  周四     例程:UCOSII实验(二)

一、用户的每个任务都是一个死循环,每个任务都处于五种状态之一:
1.睡眠状态,任务没有分配任务控制块或被剥夺了任务控制块的状态;
2.就绪状态,系统为任务配备了任务控制块且任务就绪表中进行了就绪等级,任务已经准备好了,但由于该任务的优先级比正在运行的任务的优先级低,还暂时不能运行的状态;
3.运行状态,任务获得CPU的使用权,并正在运行中的状态;
4.等待状态,正在运行的任务,需要等待一段时间或需要等待一个事件发生再运行时,该任务把CPU的使用权让给别的任务而进入的状态;
5.中断服务状态:一个正在运行的任务一旦响应中断申请就会中止运行而去执行中断服务程序的状态。

二、五种状态转换关系如下



第2章-实时系统概念.doc

649 KB, 下载次数: 905

第3章-内核结构.doc

644.5 KB, 下载次数: 884

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

38

主题

526

帖子

1

精华

金牌会员

Rank: 6Rank: 6

积分
1419
金钱
1419
注册时间
2011-11-27
在线时间
122 小时
发表于 2015-10-1 17:43:48 | 显示全部楼层
回复【340楼】龙之谷:
---------------------------------
楼主今儿又贴了一篇,顶~~
永远保持一颗学习的心态。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-10-1 18:10:44 | 显示全部楼层
第六六天  2015年10月01日 17:22:44  周四     例程:UCOSII实验(三)--任务管理

一、任务相关函数
1.建立任务:INT8U OSTaskCreate(void (*task)(void *pd), void *pdata, OS_STK *ptos, INTU prio);
 参数含义--①task:指向任务代码的指针;②pdata:任务开始执行时,传递给任务的参数的指针;③ptos:分配给任务的堆栈的栈顶指针;④prio:分配给任务的优先级。
2.建立任务:INT8U OSTaskCreateExt(void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio, INT16U id, OS_STK *pbos, INT32U stk_size, void *pext, INT16U opt);
 参数含义--①②③④同上;⑤id:为要建立的任务创建一个特殊的标识符;⑥pbos:指定任务的堆栈栈底的指针;⑦stk_size指定堆栈成员数目的容量;⑧pext:指向用户附加的数据域的指针,用来扩展OS_TCB;⑨opt:用于设定函数选项,指定是否允许堆栈检验、堆栈清零、浮点操作等。
3.堆栈检验:INT8U OSTaskStkChk(INT8U prio, OS_STK_DATA *pdata);
 参数含义--①prio:分配给任务的优先级;②pdata:传递给任务的参数的指针。
4.删除任务:INT8U OSTaskDel(INT8U prio);
 参数含义--prio:要删除任务的优先级。
5.请求删除任务:INT8U OSTaskDelReq(INT8U prio);
 参数含义--prio:要请求删除任务的优先级。
6.改变任务优先级:INT8U OSTaskChangePrio(INT8U oldprio, INT8U newprio);
 参数含义--①oldprio:原优先级;②newprio:新优先级。
7.挂起任务:INT8U OSTaskSuspend(INT8U prio);
 参数含义--prio:任务优先级。
8.恢复任务:INT8U OSTaskResume(INT8U prio);
 参数含义--prio:任务优先级。

二、知识点琐记
1.设置函数堆栈大小,需要根据函数的需求来设置,如果任务函数的局部变量多,嵌套层数多,那么相应的堆栈设置大一些,如果设置小了,很可能出现的结果就是CPU进入HardFault。另外,有些地方还需注意堆栈字节对齐问题,如果任务运行出现莫名其妙错误(如用到sprintf出错),可考虑是不是字节对齐问题。
2.在STM32F4上运行UCOSII步骤:移植UCOSII-----编写任务函数并设置其堆栈大小和优先级等参数-----初始化UCOSII函数,即调用OSInit()函数-----创建任务(至少一个)-----启动UCOSII,调用OSStart()函数。
3.任何中断服务函数,都应该加上OSIntEnter和OSIntExit函数,因为UCOSII是一个可剥夺型的内核,中断服务子程序运行之后,系统会根据情况进行一次任务调度去运行优先级别最高的就绪任务,而并不一定接着运行被中断的任务。
4.在应用程序中经常有一些代码段需不受任何干扰的持续运行,这样的代码段叫做临界代码(或临界区)。因此,为了使临界段在运行时不受中断所打断,在临界段代码前必须用关中断指令使CPU屏蔽中断请求,而在临界段代码后必须用开中断指令解除屏蔽使得CPU可以响应中断请求。UCOSII提供OS_ENTER_CRITICAL和OS_EXIT_CRITICAL两个宏来实现,这两个或需要在移植UCOSII的时候实现,采用方法3(即OS_CRITICAL_METHOD为3)来实现这两个宏。因为临界段代码不能被中断打断,将严重影响系统的实时性,所以临界段代码越短越好。
5.一个任务里面一般时必须有延时函数的,以释放CPU使用权,否则可能导致低优先级的任务因高优先级的任务不释放CPU使用权而移植无法得到CPU使用权,从而无法运行。
6.UCOSII早期版本只支持64个任务,但是从2.80版本开始,支持任务数提高到255个,不过一般64个任务都是足够多的。UCOSII从中保留了四个最高优先级和四个最低优先级总共八个任务供自己使用,但实际上UCOSII一般只占用了最低两个优先级,分别是空闲任务(倒数No.1)和统计任务(倒数No.2),所以用户可以使用的任务高达253个。

三、任务调度实验
1.ALIENTEK提供的SYSTEM文件夹里面的系统函数直接支持UCOSII,只需要在sys.h文件里面将:SYSTEM_SUPPORT_UCOS宏定义改为1即可通过delay_init函数初始化UCOSII的系统时钟节拍,为UCOSII提供时钟节拍。
2.对os_cfg里面定义OS_TICKS_PER_SEC的值为200,也就是设置UCOSII的时钟节拍为5ms。此时若使用UCOSII提供的延时函数:OSTimeDly和OSTimeDlyHMSM,这两个函数的最少延时单位只能是1个UCOSII时钟节拍即5ms,显然不能实现us级别的延时,而us级的延时在很多时候非常有用,如模拟IIC时序、DS18B20等单总线操作等。
3.创建任务过程



4.UCOSII工程流程
int main(void)
{
    ...//各种初始化
   OSInit();
   OSTaskCreate();//创建任务
   OSStart();
}

void X_task(void *pdata)
{
    while(1)
   {
   ...//任务
   }
}
5.若有临界段代码需要进行屏蔽,则需要三步
void X_task(void *pdata)
{
    OS_CPU_SR cpu_sr=0     //①屏蔽中断需使用到此变量
    pdata = pdata;
    OS_ENTER_CRITICAL();     //②
    .....
    OS_EXIT_CRITICAL();     //③
}

第4章-任务管理.doc

425 KB, 下载次数: 833

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-10-1 18:13:35 | 显示全部楼层
回复【341楼】科科1987:
---------------------------------
谢谢科科兄支持

十一刷屏周进行中~~~~~
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-10-2 09:50:14 | 显示全部楼层
第六七天  2015年10月02日 09:36:16  周五     例程:UCOSII实验(四)--时间管理

时间管理相关函数
1.任务延时函数:void OSTimeDly(INU16U ticks);
 参数含义--①ticks:延时节拍数
2.按时分秒延时函数:INT8U OSTimeDlyHMSM(INT8U hours, INT8U minutes, INT8U seconds, INT8U milli);
 参数含义--①hours:时;②minutes:分;③seconds:秒;④milli:毫秒。
3.使处在延时期的任务结束延时函数:INT8U OSTimeDlyResume(INT8U prio);
 参数含义--①prio:任务优先级
4.获取系统时间:INT32U OSTimeGet(void);
 函数返回:ticks--节拍数
5.设置系统时间:void OSTimeGet(INT32U ticks);
 参数含义--①ticks:节拍数

【注】无论时钟节拍何时发生,UCOSII都会将一个32位的计数器加1,这个计数器在用户调用OSStart()初始化多任务和4294967295个节拍执行完一遍的 时候从0开始计数。在时钟节拍的频率等于100Hz的时候,这个32位的计数器每隔497天就重新开始计数。

第5章-时间管理.doc

108.5 KB, 下载次数: 921

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-10-2 11:50:59 | 显示全部楼层
第六七天  2015年10月02日 10:05:56  周五     例程:UCOSII实验(五)--任务之间通讯与同步

一、知识琐记

1.任务间的同步依赖于任务间的通信。在UCOSII中,使用信号量、邮箱(消息邮箱)和消息队列这些被称作事件的中间环节来实现任务间的通信。


2.为了把描述事件的数据结构统一起来,UCOSII使用叫做事件控制块(ECB)的数据结构来描述诸如信号量、邮箱(消息邮箱)、消息队列这些事件。事件控制块结构体定义如下

typedef struct
{
    INT8U   OSEventType;     //事件类型
    INT16U OSEventCnt;     //信号量计数器
    void    *OSEventPtr;     //消息或消息队列指针
    INT8U   OSEventGrp;     //等待事件的任务组
    INT8U   OSEventTbl[OS_EVENT_TBL_SIZE];     //任务等待表
#if OS_EVENT_NAME_EN > 0u
    INT8U *OSEventName;     //事件名
#endif
}
其中,
OSEventPtr指针,只有在所定义的事件使邮箱或消息队列时才使用。当所定义的事件是邮箱时,它指向一个消息;当所定义的事件是消息队列时,它指向一个数据结构。
OSEventCnt,当事件是一个信号量时,用于信号量的计数器。
OSEventType,定义了事件的具体类型,可以是信号量(OS_EVENT_SEM)、邮箱(OS_EVENT_TYPE_MBOX)或消息队列(OS_EVENT_TYPE_Q)中的一种。

3.信号量分两种:①二值信号量,又称互斥型信号量;②N值信号量,又称计数型信号量,即普通信号量。


4.邮箱。在多任务操作系统中,常常需要在任务与任务之间通过传递一个数据(即“消息”)的方式来进行通信,为了达到这个目的,可以在内存中一个存储空间作为该数据的缓冲区,如果把这个缓冲区称之为消息缓冲区,这样在任务间传递数据(消息)的最简单的办法就是传递消息缓冲区指针。把用来传递消息缓冲区指针的数据结构叫做邮箱(消息邮箱)。

在UCOSII中,通过事件控制块的OSEventPtr来传递消息缓冲区指针,同时使事件控制块成员OSEventType为常数OS_EVENT_TYPE_MBOX,则该事件控制块就叫做消息邮箱。

5.①消息队列可以在人物之间传递多条消息。

②消息队列由三部分组成:事件控制块、消息队列、消息。当把事件控制块成员OSEventType的值置为OS_EVENT_TYPE_Q时,该事件控制块描述的就是一个消息队列。
③消息队列相当于一个共用一个任务等待列表的消息邮箱数组,事件控制块OSEventType指向了一个叫做队列控制块(OS_Q)的结构,该结构管理一个数组MsgTbl[],该数组中的元素都是一些指向消息的指针。


队列控制块(OS_Q)的结构体定义如下
typedef struct os_q
{
    struct os_q *OSQPtr;
    void  **OSQStart;
    void  **OSQEnd;
    void  **OSQIn;
    void  **OSQOut;
    INT16U OSQSize;
    INT16U OSQEntries;
}OS_Q;
各参数函数如下



其中,可移动的指针OSQIn和OSQOut,而指针OSQStart和OSQEnd只是一个标志(常指针)。当可移动指针OSQIn和OSQOut移动到数组末尾,也就是OSQEnd相等时,可移动指针将会被调整到数组的起始位置OSQStart,也就是说,从效果来看,指针OSQEnd与OSQStart等值。于是,这个由消息队列指针构成的数组就头尾衔接起来形成了一个循环的队列。

二、信号量相关函数
1.创建一个信号量函数:OS_EVENT *OSSemCreate(INT16U cnt);
 参数含义--①cnt:信号量计数器初始值
2.等待一个信号量函数:void OSSemPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
 参数含义--①pevent:被请求信号量的指针;②timeout:等待时限;③err:错误信息。
3.发送一个信号量函数:INT8U OSSemPost(OS_EVENT *pevent);
 函数返回:会根据实际情况返回OS_ON_ERR、OS_ERR_EVENT_TYPE或OS_SEM_OVF。
4.无等待地请求一个信号量函数:INT16U OSSemAccept(OS_EVENT *pevent);
 参数含义--①pevent:等待信号量的指针。
5.查询一个信号量的当前状态函数:INT8U OSSemQuery(OS_EVENT *pevent, OS_SEM_DATA *pdata);
 参数含义--①pevent:信号量指针;②pdata:指向用于记录信号量信息的数据结构OS_SEM_DATA的指针,使用前必须先定义。
6.删除信号量函数:OS_EVENT *OSSemDel(OS_EVENT *pevent, INT8U opt, INT8U *err);
 参数含义--①pevent:要删除的信号量指针;②opt:删除条件选项;③err:错误信息。


三、邮箱相关函数
1.创建一个邮箱:OS_EVENT *OSMboxCreate(void *msg);
 参数含义--①msg:消息指针。
2.请求邮箱函数:void *OSMboxPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
 参数含义--①pevent:请求邮箱指针;②timeout:等待时限。③err:错误信息。
3.向邮箱发送消息函数:INT8U OSMboxPost(OS_EVENT *pevent, void *msg);
 参数含义--①pevent:消息邮箱指针;②msg:消息指针。
4.无等待地从邮箱得到一个消息函数:void *OSMboxAccept(OS_EVENT *pevent);
 参数含义--①pevent:消息邮箱指针。
5.查询邮箱状态函数:INT8U OSMboxQuery(OS_EVENT *pevent, OS_MBOX_DATA *pdata);
 参数含义--①pevent:消息邮箱指针;②pdata:存放消息邮箱的结构。
6.删除邮箱函数:OS_EVENT *OSMboxDel(OS_EVENT *pevent, INT8U opt, INT8U *err);
 参数含义--①pevent:消息邮箱指针;②opt:删除选项;③err:错误信息。

四、消息队列相关函数
1.创建消息队列函数:OS_EVENT *OSQCreate(void **start, INT16U size);
 参数含义--①start:存放消息缓冲区指针数组地址;②size:数组大小。
2.等待一个消息队列中的消息函数:void *OSQPend(OS_EVENT *pevent, INT16U timeout, INT8U *err);
 参数含义--①pevent:所请求的消息队列的指针;②timeout:任务等待时限;③err:错误信息。
3.向消息发送消息函数:
    INT8U OSQPost(OS_EVENT *pevent, void *msg);[FIFO先进先出]
    INT8U OSQPostFront(OS_EVENT *pevent, void *msg);[LIFO后进先出]
 参数含义--①pevent:消息队列指针;②msg:待发消息指针。
4.无等待的从一个消息队列取得信息函数:void *OSQAccept(OS_EVENT *pevent);

 参数含义:①pevent:消息队列指针。
5.情况一个消息队列:INT8U OSQFlush(OS_EVENT *pevent);
 参数含义:①pevent:消息队列指针。
6.查询一个消息队列状态函数:INT8U OSQQuery(OS_EVENT *pevent, OS_Q_DATA *pdata);
 参数含义:①pevent:消息队列指针;②pdata:指向OS_Q_DATA数据结构指针。

第6章-任务之间的通讯与同步.doc

769.5 KB, 下载次数: 866

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-10-2 15:55:58 | 显示全部楼层
第六七天  2015年10月02日 15:41:50  周五     例程:UCOSII实验(六)--内存管理

一、内存控制块(Memory Control Block)
typedef struct
{
    void *OSMemAddr;
    void *OSMemFreeList;
    INT32U OSMemBlkSize;
    INT32U OSMemNBlks;
    INT32U OSMemNFree;
}OS_MEM;
其中,
OSMemAddr是指向内存分区起始地址的指针;
OSMemFreeList是指向下一个空闲内存控制块或者下一个空闲的内存块的指针;
OSMemBlkSize是内存分区中内存块的大小;
OSMemNBlks是内存分区中总的内存块数量;
OSMemNFree是内存分区中当前可以得到空闲内存块的数量。

二、内存管理相关函数
1.建立内存分区函数:OS_MEM *OSMemCreate(void *addr, INT32U nblks, INT32U blksize, INT8U *err);
  参数含义--①addr:内存分区起始地址;②nblks:分区内的内存块总块数;③blksize:每个内存块的字节数;④err:指向错误信息代码的指针。
2.申请一个内存块函数:void *OSMemGet(OS_MEM *pmem, INT8U *err);
 参数含义--①pmem:指向用户希望从其中分配内存块的内存分区;②err:指向错误代码指针。
3.释放一个内存块函数:INT8U OSMemPut(OS_MEM *pmem, void *pblk);
 参数含义--①pmem:指向内存块所属内存分区的指针;②err:指向错误代码指针。
4.查询一个内存分区的状态函数:INT8U OSMemQuery(OS_MEM *pmem, OS_MEM_DATA *pdata);
 参数含义:①pmem:指向内存块所属内存分区的指针;②pdata:将查询到的有关信息存放到此指针指向的数据结构中。

第7章-内存管理.doc

213.5 KB, 下载次数: 895

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-10-2 17:18:44 | 显示全部楼层
第六七天  2015年10月02日 16:53:16  周五     例程:UCOSII实验(七)--信号量集、软件定时器

一、信号量集基础知识
1.实际应用中,任务常常需要与多个事件同步,即要根据多个信号量组合作用的结果来决定任务的运行方式,UCOSII为了实现多个信号量组合的功能定义了一种特殊的数据结构-----信号量集。
2.信号量集所能管理的信号量都是一些二值信号,所有信号量集实质上是对多个输入的逻辑信号进行逻辑运算的组合逻辑。
3.UCOSII使用标志组的结构OS_FLAG_GRP来描述信号量集
typedef struct
{
    INT8U OSFlagType;     //识别是否为信号量集的标志
    void  *OSFlagWaitList;     //指向等待任务链表的指针
    OS_FLAGS OSFlagFlags;     //所有信号列表
}OS_FLAG_GRP;

二、信号量集相关函数
1.创建信号量集函数:OS_FLAG_GRP *OSFlagCreate(OS_FLAGS flags, INT8U *err);
 参数含义:①flags:信号量的初始值;②err:错误信息指针。
2.请求信号量集函数:OS_FLAGS OSFlagPend(OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U wait_type, INT16U timeout, INT8U *err);
 参数含义:①pgrp:请求的信号量集指针;②flags:滤波器;③wait_type:逻辑运算类型;④timeout:等待时限;⑤err:错误信息指针。
3.向信号量集发送信号函数:OS_FLAGS OSFlagPost(OS_FLAG_GRP *pgrp, OS_FLAGS flags, INT8U opt, INT8U *err);
 参数含义:①pgrp:请求的信号量集指针;②flags:选择要发送的信号;③opt:信号有效选项;④err:错误信息指针。

三、软件定时器基础知识
 软件定时器定义了一个单独的计数器OSTmrTime,用于软件定时器计时,UCOSII并不在OSTimeTick中进行软件定时器的到时判断与处理,而是创建了一个高于应用程序中所有其他任务优先级的定时器管理任务OSTmr_Task,在这个任务中进行定时器的到时判断与处理,时钟节拍函数通过信号量给这个高优先级任务发信号。这种方法缩短了中断程序服务程序的执行时间,但也使得定时器到时处理函数的响应受到中断退出时恢复现场和任务切换的影响。

四、软件定时器相关函数
1.创建软件定时器函数:OS_TMR *OSTmrCreate(INT32U dly, INT32U period, INT8U opt, OS_TMR_CALLBACK callback, void *callback_arg, INT8U *pname, INT8U *perr);
 参数含义:①dly:初始化定时时间;②period:周期定时模式为软件定时器的周期溢出时间;③opt:设置软件定时器工作模式;④callback:软件定时器回调函数;⑤callback_arg:回调函数参数;⑥pname:软件定时器名字;⑦perr:指向错误信息指针。
2.开启软件定时器函数
 BOOLEAN OSTmrStart(OS_TMR *ptmr, INT8U *perr);
 参数含义:①ptmr:要开启的软件定时器指针;②perr:指向错误信息的指针。
3.停止软件定时器函数:BOOLEAN OSTmrStop(OS_TMR *ptmr, INT8U opt, void *callback_arg, INT8U *perr);
 参数含义:①ptmr:要停止的软件定时器指针;②opt:停止选项;③callback_arg:新的回调函数参数;④perr:只想错误信息的指针。

第12章-配置手册.doc

67.5 KB, 下载次数: 879

第8章-移植.doc

244.5 KB, 下载次数: 939

第11章-函数参考手册.doc

365 KB, 下载次数: 911

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-10-3 14:08:52 | 显示全部楼层
第六七天  2015年10月02日  周五     例程:UCOSII实验(八)

注:此楼层简单总结开发指南中UCOSII实验2和实验3的程序。原本为了保持更新的顺序相关,计划留楼层当天晚上更新,但没有执行,实际更新事件2015年10月05日 17:00:17,但更新时间仍按预期。

一、UCOSII实验2-信号量和邮箱

1.首先定义两个指针
[mw_shl_code=c,true]OS_EVENT * msg_key; //按键邮箱事件块指针 OS_EVENT * sem_beep; //蜂鸣器信号量指针 [/mw_shl_code]
2.等待信号量
[mw_shl_code=c,true]OSSemPend(sem_beep,0,&err); //等待信号量[/mw_shl_code]
发送信号量
[mw_shl_code=c,true]OSSemPost(sem_beep); //发送信号量[/mw_shl_code]
3.等待消息
[mw_shl_code=c,true]key=(u32)OSMboxPend(msg_key,10,&err);[/mw_shl_code]
发送消息
[mw_shl_code=c,true]OSMboxPost(msg_key,(void*)key);//发送消息[/mw_shl_code]
4.挂起任务
[mw_shl_code=c,true]OSTaskSuspend(TOUCH_TASK_PRIO); //挂起触摸屏任务 [/mw_shl_code]
恢复任务
[mw_shl_code=c,true]OSTaskResume(TOUCH_TASK_PRIO); //解挂[/mw_shl_code]

二、UCOSII实验3-消息队列、信号量集和软件定时器

1.全局变量定义
[mw_shl_code=c,true]OS_EVENT * msg_key; //按键邮箱事件块 OS_EVENT * q_msg; //消息队列 OS_TMR * tmr1; //软件定时器1 OS_TMR * tmr2; //软件定时器2 OS_TMR * tmr3; //软件定时器3 OS_FLAG_GRP * flags_key; //按键信号量集 void * MsgGrp[256]; //消息队列存储地址,最大支持256个消息[/mw_shl_code]
[mw_shl_code=c,true] msg_key=OSMboxCreate((void*)0); //创建消息邮箱 q_msg=OSQCreate(&MsgGrp[0],256); //创建消息队列 flags_key=OSFlagCreate(0,&err); //创建信号量集 [/mw_shl_code]

2.请求消息队列
[mw_shl_code=c,true]p=OSQPend(q_msg,0,&err);//请求消息队列[/mw_shl_code]
发送消息队列
[mw_shl_code=c,true]err=OSQPost(q_msg,p); //发送队列[/mw_shl_code]
3.等待消息
[mw_shl_code=c,true]key=(u32)OSMboxPend(msg_key,10,&err); [/mw_shl_code]
发送消息
[mw_shl_code=c,true]OSMboxPost(msg_key,(void*)key);//发送消息[/mw_shl_code]
4.等待信号量
[mw_shl_code=c,true]flags=OSFlagPend(flags_key,0X001F,OS_FLAG_WAIT_SET_ANY,0,&err);//等待信号量[/mw_shl_code]
发送信号量
[mw_shl_code=c,true]OSFlagPost(flags_key,1<<(key-1),OS_FLAG_SET,&err);//设置对应的信号量为1[/mw_shl_code]
5.定时器,回调函数
[mw_shl_code=c,true]//软件定时器1的回调函数 //每100ms执行一次,用于显示CPU使用率和内存使用率 void tmr1_callback(OS_TMR *ptmr,void *p_arg) { static u16 cpuusage=0; static u8 tcnt=0; POINT_COLOR=BLUE; if(tcnt==5) { LCD_ShowxNum(182,10,cpuusage/5,3,16,0); //显示CPU使用率 cpuusage=0; tcnt=0; } cpuusage+=OSCPUUsage; tcnt++; LCD_ShowxNum(182,30,my_mem_perused(SRAMIN),3,16,0); //显示内存使用率 LCD_ShowxNum(182,50,((OS_Q*)(q_msg->OSEventPtr))->OSQEntries,3,16,0X80);//显示队列当前的大小 }[/mw_shl_code]
创建定时器
[mw_shl_code=c,true]tmr1=OSTmrCreate(10,10,OS_TMR_OPT_PERIODIC,(OS_TMR_CALLBACK)tmr1_callback,0,"tmr1",&err); //100ms执行一次[/mw_shl_code]
启动定时器
[mw_shl_code=c,true]OSTmrStart(tmr1,&err);//启动软件定时器1[/mw_shl_code]
关闭定时器
[mw_shl_code=c,true]OSTmrStop(tmr1,OS_TMR_OPT_NONE,0,&err); //关闭软件定时器1[/mw_shl_code]
重新开启定时器
[mw_shl_code=c,true]OSTmrStart(tmr1,&err); //重新开启软件定时器1[/mw_shl_code]
以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

72

主题

2711

帖子

2

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
3505
金钱
3505
注册时间
2014-8-4
在线时间
696 小时
 楼主| 发表于 2015-10-3 23:39:55 | 显示全部楼层
第六八天 2015年10月03日  周六     例程:UCOSIII(一)--移植

一、UCOSIII和UCOSII一个比较大的区别:UCOSIII允许一个任务优先级被多个任务使用,当这个优先级处于最高就绪态的时候,UCOSIII就会轮流调度处于这个优先级的任务,让每个任务运行一段由用户指定的时间长度,叫做时间片。

二、移植过程仿写

1.移植时将3.03版本的UCOSIII代替3.04版本UCOSIII,因为开发手册指出3.03版本移植后更为稳定。

2.新建UCOSIII文件夹,在其文件夹再新建五个文件夹:①UCOS_BSP;②UCOS_CONFIG;③UCOS_CPU;④UCOS_LIB;⑤UCOS_PORT;⑥UCOS_CORE。

3.各文件夹文件来源如下
********************************************************


********************************************************


********************************************************


********************************************************


********************************************************


********************************************************




4.移植后文件修改

其一,修改bsp.c和bsp.h文件【见ucos开发手册_V2.1之53页】;
其二,修改os_cpu_a.asm文件【见ucos开发手册_V2.1之56页】;
其三,修改os_cpu_c.c文件【见ucos开发手册_V2.1之56页】;
其四,修改os_cfg_app.h文件【见ucos开发手册_V2.1之59页】
其五,修改SYSTEM文件夹中sys.h文件【见ucos开发手册_V2.1之60页】
其六,修改SYSTEM文件夹中delay.c文件【见ucos开发手册_V2.1之60页】

【注】以上只是简单总结UCOSIII移植过程,尚未在开发板实际验证是否移植成功。

以我资质之鲁钝,当尽平心静气、循序渐进、稳扎稳打之力。
回复 支持 反对

使用道具 举报

530

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
165309
金钱
165309
注册时间
2010-12-1
在线时间
2108 小时
发表于 2015-10-4 09:51:05 | 显示全部楼层
回复【349楼】龙之谷:
---------------------------------
快学完了啊。。。
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-11-22 17:02

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表