diameter 发表于 2011-7-30 12:06:04

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后缀的函数好了
好了,第一节到此结束。

lch 发表于 2011-7-30 12:10:39

顶起,支持diameter

diameter 发表于 2011-7-30 13:48:39

本帖最后由 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输出的位置(原文是光标???)

Print
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:50:24

本帖最后由 diameter 于 2011-7-30 13:54 编辑

第一章入门部分完毕,大家可以提问了

(给个精吧~~~,毕竟花一个多小时写的)

路人 发表于 2011-7-30 14:03:02

当前权限无法加分,但我能前排支持!

月与映之皮丘 发表于 2011-7-30 15:44:24

直径V5。好东西。接了。谢。

chsi 发表于 2011-7-30 16:37:55

SDK教程非常有价值,希望能继续写下去。

qihai1314 发表于 2011-7-30 19:27:44

强烈要求管理员给加精!小饕强者,以后绝对是个出色的程序员!我一直跟着你学习

diameter 发表于 2011-7-30 19:57:16

我现在在纠结写不写SH ASM的内容……

py86546539 发表于 2011-7-30 21:04:07

写完了建议置顶。

ZCYZ31 发表于 2011-7-30 21:38:44

太好了,学习学习!!
建议全写完后发布word版

dieroar 发表于 2011-7-31 08:49:39

好东西,感谢楼主

GWHBOB 发表于 2011-7-31 12:44:45

IMB_WRITEMODIFY_MESH    = 3 网格绘制(像素间隔点绘制)
请给个示范,谢谢

diameter 发表于 2011-7-31 13:31:05

第一章 实践部分

第一节 先从画个圆说起

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哪一个快

其实说到浮点数的问题——你甚至可以使用定点数(超出范围不讨论)

好了这只是给我们的实践开了个小头,就是大家提个醒——有时候速度与内存不可兼得,要依
情况而定。

diameter 发表于 2011-7-31 13:33:36

第一章 实践部分
第二节 一个实心三角形

先不说别的,如果你想用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);


好了,纯图形的东西暂时告一段落,下一节我们来一点实际的——一个自定义
的菜单。

zhj 发表于 2011-7-31 15:35:35

可怜的DD刚画出来就被清除了
我邪恶了。

diameter 发表于 2011-8-1 11:26:27

第一章 实践部分

第三节 美观的菜单

一个美观的多选菜单需要什么呢?一个标题,几个选项,我觉得写这样一个选项数目是
定值的菜单对大家来说并不难。

所以,我们今天写一个可以处理选项数目不定的菜单函数

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);
效果图:

diameter 发表于 2011-8-1 11:29:56

第一章(Bdisp)的内容就到此结束了,中午整理pdf版本。
后面的章节介绍:
第二章 Bfile与控制函数
第三章 实例演练
第四章 SH汇编入门与SH C宏介绍

diameter 发表于 2011-8-1 15:00:32

第一章的pdf版本出炉

croptree 发表于 2011-8-1 15:10:44

太感谢了!!学习了!!
页: [1] 2 3 4
查看完整版本: fx-9860G SDK开发从入门到实践