fx-9860G SDK开发从入门到实践
第一章pdf整理版在19L第二章 入门部分 pdf在22L
fx-9860G SDK开发从入门到实践
作者 Anderain Lewis (diameter)
QQ:1582163328
前言
首先说明,这个系列不会对C语言进行讲解,对于C的语法与工程文件结构,请自行查看有关书籍。
再次推荐几本书:
重点讲解C的书籍:
《K&R》(既《The C Programming Language》)
清华大学出版社的《C语言程序设计》
陈正冲的《C语言深度剖析》
林锐《高质量C/C++编程指南》
……
重点讲解数据结构、算法的书籍:
Mark Allen Weiss《Data Structures and Algorithm Analysis in C》
《Beautiful Code》(作者好多的……)
……
对于不懂C的人,我的建议就一个:现在传统的IDE下把C的基本功练好,掌握标准库的用法,再来学SDK是很简单的。
推荐些IDE:
Visual C++ 6.0(不是很推荐.net版本的VC++)
TC 2.0 以上版本(3.0版本支持C++)
MinGW (GCC的一个Windows替代品)
好了,如果你觉得C的基础打好了,那么跟我一起踏上征途吧!
第一章:什么叫做——显示(Bdisp_)
首先你的手头应该有CASIO fx-9860G SDK与完整的说明文档(pdf文件)
里面应该有以下文件
C Standard Libraries.pdf 介绍C语言的标准库,灰色标注的部分是9860SDK不支持的函数
fx9860g_sdk_starters_guide.pdf 简单介绍SDK(如何安装,如何运行,模拟器的区别)
fx-9860G Character Set.pdf 介绍9860SDK的特殊字符集(与ASCII的区别),另外还有双字节字符
fx-9860G Key Code List.pdf 介绍9860SDK的按键宏
fx-9860G Libraries.pdf 介绍9860SDK的库函数
Installing_Add-in.pdf 介绍如何安装Add-in
SHC Manual.PDF 介绍9860SDK使用的编译器(如何使用C、C++以及汇编、代码优化等内容)(C++与汇编不在本文讨论范围之内)
我们目前只使用《fx-9860G Libraries》这一个参考手册
第一节 DD与VRAM
使用浏览工具打开fx-9860G Libraries,可以看到目录,前面有长长的一串以“Bdisp_”开头的函数。这些都是与绘图、显示有关的。这些函数顾名思义望文生义(某管理注:错用成语),很容易推断用途。我们这一节主要讨论这些函数名称的后缀——DD与VRAM
参看函数的说明,我们知道,DD(Display Driver)是显示驱动,VRAM是显存。
这两者之间有什么关联呢???
我们写一个程序来看一下。
#include "fxlib.h"
int AddIn_main(int isAppli, unsigned short OptionNum)
{
unsigned int key;
int i;
Bdisp_AllClr_DDVRAM();
for (i=0;i<32;++i){
Bdisp_SetPoint_DD (0,i,1);
Bdisp_SetPoint_VRAM (12,i,1);
}
Sleep(1000);
while(1){
GetKey(&key);
}
return 1;
}
#pragma section _BR_Size
unsigned long BR_Size;
#pragma section
#pragma section _TOP
int InitializeSystem(int isAppli, unsigned short OptionNum){
return INIT_ADDIN_APPLICATION(isAppli, OptionNum);
}
#pragma section
这是一段可以完整编译的程序。其中Sleep函数让程序暂停(参数的单位是毫秒),SetPoint画点
运行效果很明显,现有一条直线,暂停一秒后变到了右边根据代码,我们知道,
Bdisp_SetPoint_DD (0,i,1);这个函数绘制的线条在左
Bdisp_SetPoint_VRAM (12,i,1); 这个函数绘制的线条在右
现在我们,就明白了VRAM与DD的关系了。
对DD的操作可以直接显示,对VRAM的操作不能直接显示。对VRAM的操作需要等系统强制刷新显存,将VRAM的内容送入DD才能显示。
我们可爱的GetKey(unsigned int*)函数就有清空显存并刷新DD的功能。
如果我们想让对VRAM的操作直接显示呢?老用GetKey不好吧,我们可以使用Bdisp_PutDisp_DD,来将VRAM里面的东西放到DD里面去,就显示出来啦。
稍作修改:
for (i=0;i<32;++i){
Bdisp_SetPoint_DD (0,i,1);
Bdisp_SetPoint_VRAM (12,i,1);
Bdisp_PutDisp_DD();
}
再运行一下,发现只有右边的线了(可怜的DD刚画出来就被清除了)
如果你觉得不保险,总想把东西画出来的话……使用带DDVRAM后缀的函数好了
好了,第一节到此结束。 顶起,支持diameter 本帖最后由 diameter 于 2011-8-1 09:11 编辑
第三节:图形函数注解
图形的初级内容就到这里了,下面把fxlib.h提供的图形相关的库函数都注解一下吧
Bdisp_AllClr_DD/ Bdisp_AllClr_VRAM/ Bdisp_AllClr_DDVRAM
全部清除(DD、VRAM或DD VRAM一块)(见第一节)
Bdisp_AreaClr_DD/ Bdisp_AreaClr_VRAM/ Bdisp_AreaClr_DDVRAM
区域清除(DD、VRAM或DD VRAM一块)(见第一节)
可以使用封装的函数area_clear
sel取值:0.清除区域,1.画一个框,其他.清除区域+画框
void area_clear (int left,int top,int right,int bottom,int sel)
{
DISPBOX box;
box.left = left;box.top = top;box.right = right;box.bottom = bottom;
if (sel != 1)
Bdisp_AreaClr_DDVRAM (&box);
if (sel != 0){
Bdisp_DrawLineVRAM (box.left,box.top,box.right,box.top);
Bdisp_DrawLineVRAM (box.left,box.bottom,box.right,box.bottom);
Bdisp_DrawLineVRAM (box.left,box.top,box.left,box.bottom);
Bdisp_DrawLineVRAM (box.right,box.top,box.right,box.bottom);
}
}
Bdisp_AreaReverseVRAM
VRAM区域反色
Bdisp_GetDisp_DD/ Bdisp_GetDisp_VRAM
把DD的内容存入VRAM/把VRAM的内容存入DD
Bdisp_PutDisp_DD
强制将VRAM送入DD以显示
Bdisp_SetPoint_DD/ Bdisp_SetPoint_VRAM/ Bdisp_SetPoint_DDVRAM
画一个点(DD、VRAM或DD VRAM一块)(见第一节)
最后一个参数决定是画点还是清除点
Bdisp_GetPoint_VRAM
从VRAM获取一个点(1 or 0)
Bdisp_WriteGraph_DD/ Bdisp_WriteGraph_VRAM/ Bdisp_WriteGraph_DDVRAM
(见第一节)
Bdisp_ReadArea_DD / Bdisp_ReadArea_VRAM
将DD/VRAM的一部分读入一个字符数组
Bdisp_DrawLineVRAM
Bdisp_ClearLineVRAM
画线、清除线
locate
定位Print、PrintC、PrintRev、PrintRevC输出的位置(原文是光标???)
PrintRev
输出/反色输出字符串
PrintC
PrintRevC
输出/反色输出字符串的首字符
附带一个Printf,需要stdarg.h
关于变参函数与参数堆栈后面章节讲解
int Printf(char *cpFormat, ...)
{
int len;
char cpBuffer;
va_list pArgList;
va_start(pArgList, cpFormat);
len = vsprintf(cpBuffer, cpFormat, pArgList);
va_end(pArgList);
Print((unsigned char*)cpBuffer);
return len;
}
PrintLine
PrintRLine
输出/反色输出一行字符串,最后一个参数决定行的最大字符数
PrintXY
在任意位置输出字符串,最后一个参数决定是否反色
PrintMini
在任意位置使用小字符输出字符串,最后一个参数决定绘制方式
MINI_OVER 覆盖
MINI_OR 不覆盖
MINI_REV 反色覆盖
MINI_REVOR 反色不覆盖
SaveDisp
RestoreDisp
保存/显示显示屏图
唯一的参数只能为
SAVEDISP_PAGE1
SAVEDISP_PAGE2
SAVEDISP_PAGE3
PopUpWin
画一个窗口(与系统弹出的相同)
唯一的参数决定窗口能包含的行数 本帖最后由 diameter 于 2011-7-30 13:54 编辑
第一章入门部分完毕,大家可以提问了
(给个精吧~~~,毕竟花一个多小时写的) 当前权限无法加分,但我能前排支持! 直径V5。好东西。接了。谢。 SDK教程非常有价值,希望能继续写下去。 强烈要求管理员给加精!小饕强者,以后绝对是个出色的程序员!我一直跟着你学习 我现在在纠结写不写SH ASM的内容…… 写完了建议置顶。 太好了,学习学习!!
建议全写完后发布word版 好东西,感谢楼主 IMB_WRITEMODIFY_MESH = 3 网格绘制(像素间隔点绘制)
请给个示范,谢谢 第一章 实践部分
第一节 先从画个圆说起
fxlib.h中并没有提供画圆的函数,这样,我们自己写一个——借此也讨论一些技术的问题
首先我们应该知道圆的参数方程:
x = r * cos(t) + h
y = r * sin(t) + k
(h,k)为圆心坐标,r为半径。t∈[0,2*pi)
由此我们就可以写一个画圆的函数了
void circle1(int cx,int cy,int r)
{
const float PI = 3.1415926535,step = PI/48.0;
int x,y,px,py;
float t;
px = cx + r;
py = cy;
for (t = step;t<2 * PI;t += step){
x = cosf(t) * r + cx;
y = sinf(t) * r + cy;
Bdisp_DrawLineVRAM(x,y,px,py);
px = x,py = y;
}
x = cx + r;
y = cy;
Bdisp_DrawLineVRAM(x,y,px,py);
}
注意啊,我们这里用的自变量t是float类型的,所调用的函数不是sin、cos而是sinf与cosf,
所以你要包含的头文件是<mathf.h>而非<math.h>
运行一下,circle1(63,31,20);
效果貌似还可以(比PRGM的快多了,至少没有让人看见停顿),其实这个函数效率低的吓人
每次调用三角函数都要对浮点数进行压栈,效率很低(如果是double型的话会更慢)。只是
我们看不见而已。
所以在这里我们要讨论一个严肃的话题——要速度,还是要内存
(我原来是搞win32游戏编程的^_^)所以我知道一种省时间但是浪费内存的方法(win32平台
的话不算浪费),那就是使用查找表——如果你想多次使用查找表的话。
如此这般
const float PI = 3.1415926535;
float sin_table;
float cos_table;
void recalc_table()
{
int t;
for (t = 0;t < 360 ;++t){
sin_table = sinf(PI*t/180.0);
cos_table = cosf(PI*t/180.0);
}
}
void circle2(int cx,int cy,int r)
{
int x,y,px,py,t;
px = cx + r;
py = cy;
for (t = 0;t<360;t++){
x = cos_table * r + cx;
y = sin_table * r + cy;
Bdisp_DrawLineVRAM(x,y,px,py);
px = x,py = y;
}
x = cx + r;
y = cy;
Bdisp_DrawLineVRAM(x,y,px,py);
}
在程序初始化的时候调用一下recalc_table函数,以后所有的跟三角函数有关的都可以调用
查找表它(比如位图旋转、笛卡尔坐标转直角坐标……)
不过这个画的图有看起来有点粗是吧…………
好,改这一行
for (t = 0;t<360;t+=4){
这就差不多了哈。这个方法虽说有点浪费内存,但是——它很快,想想,在内存中取一个值
快还是函数反复压栈退栈快?
如果你不相信,可以测试一下
int x = 63,y = 31,i;
unsigned int key;
recalc_table(); // 不要把这个忘了
// loop 1
for (i=0;i<80;++i){
circle1 (x,y,20+i);
Bdisp_PutDisp_DD();
}
GetKey(&key);
Bdisp_AllClr_DDVRAM();
// loop 2
for (i=0;i<80;++i){
circle2 (x,y,20+i);
Bdisp_PutDisp_DD();
}
GetKey(&key);
看看这两个loop哪一个快
其实说到浮点数的问题——你甚至可以使用定点数(超出范围不讨论)
好了这只是给我们的实践开了个小头,就是大家提个醒——有时候速度与内存不可兼得,要依
情况而定。 第一章 实践部分
第二节 一个实心三角形
先不说别的,如果你想用9860 SDK把你的程序的图形界面写的很炫的话,有一样
东西是必须的——画一个实心(填充的)多边形。
看下图,好像做到这一点并不难
图上垂直的红线的意思是按一个方向(比如从左向右)一路用直线填充过去,多
边形就成为实心的了。
上图看上去好像很简单,只要把四条边的解析式求出来不就行了么?那我只能说
你漏了凹四边形。那可就不好玩了。所以,我们把四边形分解成三角形来绘制。
如图,两种四边形都可以分解为两个三角形
但是问题又来了——三角形也画不了啊,填充的时候还是没法计算填充线的位置
于是我们继续向下分割
如图,三角形总能分解成有一条边是水平的两个三角形。这样问题就迎刃而解啦
如图,水平边的两个端点约定为(x1,y1)与(x2,y2),两条非水平边的方程可设为
已知(x1,y1),(x2,y2),(x3,y3)
线段1:y = k1 * x + b1
线段2:y = k2 * x + b2
可以推出
k1 = (y1 - y3)/(x1 - x3);
k2 = (y2 - y3)/(x2 - x3);
b1 = y1 - k1 * x1;
b2 = y2 - k2 * x2;
有人可能会问了,有一条边事垂直的怎么办?那样斜率算出来是±∞啊(在C里确实如此)。
很简单,垂直的话(x1=x3或x2=x3),那一部分就不绘制了,反正垂直了也那边的一部分也
就不存在了。
有人还问了,如果是一个钝角三角形怎么办?不好绘制吧?
看图:
看到红色部分的那个扫描的直线l1没有?画这条直线的时候让x在区间内不就行了
l的两个端点分别为 (x,k1*x+b1),(x,k2*x+b2)
钝角在左边的也是如此。
好了不多说了,把代码贴上
void draw_horizontal_triangle
(float x1,float y1,float x2,float y2,float x3,float y3)
{
// (x1,y1)o------o(x2,y2)
// \ /
// \/
// \/
// o(x3,y3)
float k1,k2,b1,b2;
int x,y,ty;
if (y1 != y2) return;
if (x1>x2){
float t;
t = x2;x2 = x1;x1 = t;
}
k1 = (y1 - y3)/(x1 - x3);
k2 = (y2 - y3)/(x2 - x3);
b1 = y1 - k1 * x1;
b2 = y2 - k2 * x2;
if (x1 < x3){
int t = x3 > x2 ? x2 : x3;
for (x = x1;x <= t;++x){
y = k1 * x + b1;
Bdisp_DrawLineVRAM(x,y1,x,y);
}
}else{
for (x = x3;x <= x1;++x){
y= k1 * x + b1;
ty = k2 * x + b2;
Bdisp_DrawLineVRAM(x,ty,x,y);
}
}
if (x3 < x2){
for (x = x3 > x1 ? x3 : x1;x <= x2;++x){
y = k2 * x + b2;
Bdisp_DrawLineVRAM(x,y2,x,y);
}
}else{
for (x = x2;x <= x3;++x){
y= k1 * x + b1;
ty = k2 * x + b2;
Bdisp_DrawLineVRAM(x,y,x,ty);
}
}
}
将Addin_main改为以下代码
int AddIn_main(int isAppli, unsigned short OptionNum)
{
unsigned int key;
Bdisp_AllClr_DDVRAM();
draw_horizontal_triangle (24,12, 64,12, 32,32);
GetKey(&key);
Bdisp_AllClr_DDVRAM();
draw_horizontal_triangle (24,12, 64,12, 0,32);
GetKey(&key);
Bdisp_AllClr_DDVRAM();
draw_horizontal_triangle (24,12, 64,12, 100,32);
while(1){
GetKey(&key);
}
return 1;
}
测试效果
而最后的任意三角实心三角形就很好写了
void swap (int *a,int *b){int t;t = *a;*a = *b;*b = t;}
void draw_triangle (int x1,int y1,
int x2,int y2,
int x3,int y3)
{
if ((x1==x2 && x2==x3) || (y1==y2 && y2==y3)) return;
if (y2 < y1){
swap(&x1,&x2);swap(&y1,&y2);
}
if (y3 < y1){
swap(&x3,&x1);swap(&y3,&y1);
}
if (y3 < y2){
swap(&x3,&x2);swap(&y3,&y2);
}
if (y1==y2 || y2==y3)
draw_horizontal_triangle (x1,y1,x2,y2,x3,y3);
else{
int new_x = x1 + (int)((float)(y2-y1)*(float)(x3-x1)/(float)(y3-y1));
draw_horizontal_triangle (new_x,y2,x2,y2,x1,y1);
draw_horizontal_triangle (x2,y2,new_x,y2,x3,y3);
}
}
测试一下draw_triangle(32,32,127,0,63,63);
好了,纯图形的东西暂时告一段落,下一节我们来一点实际的——一个自定义
的菜单。 可怜的DD刚画出来就被清除了
我邪恶了。 第一章 实践部分
第三节 美观的菜单
一个美观的多选菜单需要什么呢?一个标题,几个选项,我觉得写这样一个选项数目是
定值的菜单对大家来说并不难。
所以,我们今天写一个可以处理选项数目不定的菜单函数
int pop_menu (char * context[],char *title,int max,int x,int y);
(看见context类型如果觉得眼晕的同学建议去查查“int main(int argc,char *argv[])”)
函数原型有了,我们需要一个画背景的函数,这样前面的area_clear就用上了。
可能有人会问了:菜单的边框大小怎么确定?要是让人一个个去算不是很麻烦么。所以我
们采用让函数自己算的方法:
int width,height,i,l;
height = (max+1) * 8;
width = strlen(title);
for (i=0;i<max;++i){
l = strlen(context);
if (l>width) width = l;
}
width *= 6;
上面的片段中明显是在title与所有context之间寻找最长的字符串,然后乘以大字符的大小
(大字符尺寸:6*8)
标题要反色,来一个Bdisp_AreaReverseVRAM好了。
另外为了防止误按键导致屏幕无谓的刷新,再加一个变量redraw。
写好的函数如下
int pop_menu (char *context[],char *title,int max,int x,int y)
{
int width,height,i,l,redraw = 1,index = 0;
unsigned int key;
height = (max+1) * 8;
width = strlen(title);
for (i=0;i<max;++i){
l = strlen(context);
if (l>width) width = l;
}
width *= 6;
area_clear(x,y,x+width+2,y+height+1,2);
PrintXY (x+1,y+1,(unsigned char*)title,0);
Bdisp_AreaReverseVRAM (x+1,y+1,x+width+1,y+8);
while (1){
if (redraw){
redraw = 0;
area_clear(x+1,y+9,x+width+1,y+height,0);
for (i=0;i<max;++i)
PrintXY(x+1,y+1+(i+1)*8,context,0);
Bdisp_AreaReverseVRAM (x+1,y+1+(index+1)*8,x+width+1,y+(index+2)*8);
}
GetKey(&key);
if (key==KEY_CTRL_UP){
index--;
if (index<0) index = max-1;
redraw = 1;
}
else if (key==KEY_CTRL_DOWN){
index++;
if (index>=max) index = 0;
redraw = 1;
}
else if (key==KEY_CTRL_EXIT) return -1;
else if (key==KEY_CTRL_EXE)returnindex;
}
}
在AddIn_main里面添加以下代码进行测试
char *c[] = {"Hit","Throw","Pick up","Run away"};
pop_menu (c,"Select Menu",4,4,4);
效果图:
第一章(Bdisp)的内容就到此结束了,中午整理pdf版本。
后面的章节介绍:
第二章 Bfile与控制函数
第三章 实例演练
第四章 SH汇编入门与SH C宏介绍 第一章的pdf版本出炉 太感谢了!!学习了!!