打印
[51单片机]

从业将近十年!手把手教你单片机程序框架(连载)

[复制链接]
楼主: jianhong_wu
手机看帖
扫描二维码
随时随地手机跟帖
621
  做项目后这样写代码。

使用特权

评论回复
622
wxdx8320| | 2014-12-10 11:54 | 只看该作者

unsigned char read_eeprom(unsigned int address)   //从一个地址读取出一个字节数据
{
   unsigned char dd,cAddress;  
   cAddress=address; //把低字节地址传递给一个字节变量。
/* 注释一:
  * IIC通讯过程是一个要求一气呵成的通讯过程,中间不能被其它中断影响时序出错,因此
  * 在整个通讯过程中应该先关闭总中断,完成之后再开中断。但是,这样就会引起另外一个新
  * 问题,如果关闭总中断的时间太长,会导致动态数码管不能及时均匀的扫描,在操作EEPROM时,
  * 数码管就会出现闪烁的现象,解决这个问题最好的办法就是在做项目中尽量不要用动态扫描数码管
  * 的方案,应该用静态显示的方案。那么程序上还有没有改善的方法?有的,下一节我会讲这个问题
  * 的改善方法。
  */
   EA=0; //禁止中断
   start24(); //IIC通讯开始
   write24(0xA0); //此字节包含读写指令和芯片地址两方面的内容。
                  //指令为写指令。地址为"000"的信息,此信息由A0,A1,A2的引脚决定
   ack24(); //发送应答信号
   write24(cAddress); //发送读取的存储地址(范围是0至255)
   ack24(); //发送应答信号
   start24(); //开始
   write24(0xA1); //此字节包含读写指令和芯片地址两方面的内容。
                  //指令为读指令。地址为"000"的信息,此信息由A0,A1,A2的引脚决定
   ack24(); //发送应答信号
   dd=read24(); //读取一个字节
   ack24(); //发送应答信号
   stop24();  //停止
/* 注释二:
  * 在写入或者读取完一个字节之后,一定要加上一段延时时间。在11.0592M晶振的系统中,
  * 写入数据时经验值用delay_short(2000),读取数据时经验值用delay_short(800)。
  * 否则在连续写入或者读取一串数据时容易丢失数据。如果一旦发现丢失数据,
  * 应该适当继续把这个时间延长,尤其是在写入数据时。
  */
   delay_short(800);  //此处最关键,此处的延时时间一定要,而且要足够长,此处也是导致动态数码管闪烁的根本原因
   EA=1; //允许中断
   return(dd);
}

吴老师好!您第四十六节利用AT24C02进行掉电后的数据保存中有关导致动态数码管闪烁的部分我有疑问,请问把EA=1这条语句放到delay_short(800)之前(程序见上),是不是就可以达到和您第四十七节课讲的一气哈成的延时效果差不多呢?

使用特权

评论回复
623
jianhong_wu|  楼主 | 2014-12-15 20:02 | 只看该作者
wxdx8320 发表于 2014-12-10 11:54
unsigned char read_eeprom(unsigned int address)   //从一个地址读取出一个字节数据
{
   unsigned char ...

这样性质跟47节不一样的。如果这样做,就会导致delay的延时时间不准确,有可能影响读写eerpom的速度。

使用特权

评论回复
624
jianhong_wu|  楼主 | 2014-12-17 13:14 | 只看该作者
第八十三节:矩阵键盘输入任意数字或小数点的液晶屏显示程序。

开场白:
本来这节打算讲调用液晶屏内部字库时让某行内容反显的,但是在昨天调试过程中,发现一个很奇怪的问题,当调用内部字库时,按照数据手册,我执行一条反显指令时,应该是仅仅某一行反显,但是却同时出现两行反显。比如,当我执行
       WriteCommand(0x34); //扩充指令集
       WriteCommand(0x04); //第1行反显
指令时,发现第一行和第三行反显,后来想想,我猜测这种12864的屏应该是25632折成左右半屏,左半屏在上面,右半屏在下面。经过这次经验,我觉得大家以后尽量不要用液晶屏的内部字库模式,应该用自构字库的模式(图形模式)。因为我觉得用内部字库模式的时候,这个集成的反显扩展指令不好用。而用自构字库的模式(图形模式),却可以顺心所欲的灵活运用,适合做菜单程序。
既然发现内部字库不好用,所以不再讲内部字库模式,这节仅仅接着前面第79节内容,继续讲在自构字库的模式(图形模式)下,如何通过矩阵键盘直接输入数字和小数点,就像普通的计算器一样键盘输入。这个功能表面简单,其实有以下四个地方值得注意:
第一:如何用数组接收按键输入的BCD码数据。
第二:如何限制输入参数的小数点个数和数组的有效个数。
第三:如果第0个位置是0,那么继续输入的数据直接覆盖0,否则就移位再输入。
第四:如果第0个位置是0,那么继续输入的小数点要移位输入。
要仔细了解以上提到的关键点,必须好好研究本程序中的void set_data(…)函数。同时也要温习一下之前讲的自构字库模式的液晶屏显示内容,尤其是插入画布显示的内容。

具体内容,请看源代码讲解。

(1)     硬件平台:
基于朱兆祺51单片机学习板。数字1键对应S1键,数字2键对应S2键,数字3键对应S3键…. 数字9键对应S9键, 数字0键对应S10键。小数键对应S11,清零键对应S16,其它按键不用。

(2)     实现功能:
用矩阵键盘输入任意数字或小数点。小数点不能超过2位,一旦超过2位,再按其它按键则输入无效。有效数字也不能超过6位(包括小数点),一旦超过6位,再按其它按键则输入无效。
想重新输入,必须按S16清零按键才能重新输入。

(3)源代码讲解如下:
第八十三节源代码讲解.rar (7.13 KB)
总结陈词:
这节讲的是键盘输入数字或者小数点的BCD码用来显示,实际项目中,我们经常要知道所输入的BCD码数组到底有效数值是多少,这个该怎么办?欲知详情,请听下回分解----
实时同步把键盘输入的BCD码数组转换成数值的液晶屏显示程序。

(未完待续,下节更精彩,不要走开哦)

使用特权

评论回复
625
panamatw| | 2014-12-18 10:41 | 只看该作者
謝謝

使用特权

评论回复
626
solarddd| | 2014-12-18 14:05 | 只看该作者
期待吴工继续更新..................

使用特权

评论回复
627
西科小凡| | 2014-12-18 20:41 | 只看该作者
这看完得用多长时间啊

使用特权

评论回复
628
arm这点事| | 2014-12-19 08:54 | 只看该作者
鸿哥V5

使用特权

评论回复
629
lsc201100| | 2014-12-19 10:25 | 只看该作者
鸿哥V8

使用特权

评论回复
630
jianhong_wu|  楼主 | 2014-12-19 15:47 | 只看该作者
第八十四节:实时同步把键盘输入的BCD码数组转换成数值的液晶屏显示程序。

开场白:
    键盘直接输入的是带小数点的BCD码数组,要把它们转换成具体的数值才可以更好的在程序里运算或者处理。如何把BCD码数组实时同步转换成数值?这一节主要跟大家讲这方面的算法程序。另外,有一个地方值得注意:上一节键盘输入的小数点个数可以限制成最大2位,但是整数部分没有限制。这节为了也能限制整数部分的最大个数为3位,我修改了上一节的void set_data(…)函数。所以这节的void set_data(…)函数跟上一节的void set_data(…)函数有点不一样,需要特别注意。

具体内容,请看源代码讲解。

(1)     硬件平台:
基于朱兆祺51单片机学习板。数字1键对应S1键,数字2键对应S2键,数字3键对应S3键…. 数字9键对应S9键, 数字0键对应S10键。小数键对应S11,清零键对应S16,其它按键不用。

(2)     实现功能:
用矩阵键盘输入任意数字或小数点。小数点不能超过2位,一旦超过2位,再按其它按键则输入无效。整数部分不能超过3位,一旦超过3位,再按其它按键则输入无效。想重新输入,必须按S16清零按键才能重新输入。每次键盘输入的第一行BCD码数组会同步更新显示在第二行的数值上。

(3)源代码讲解如下:
第八十四节源代码讲解.rar (8.21 KB)
总结陈词:
    这节讲了把BCD码数组同步实时转换成数值的算法程序,相反,把数值转换成BCD码数组的逆运算程序应该怎么写?欲知详情,请听下回分解----实时同步把加减按键输入的数值转换成BCD码数组的液晶屏显示程序。

(未完待续,下节更精彩,不要走开哦)

使用特权

评论回复
631
刘远进| | 2014-12-20 10:06 | 只看该作者
好帖 感谢楼主无私奉献

使用特权

评论回复
632
wxdx8320| | 2014-12-20 10:21 | 只看该作者
吴老师,能否给我讲下,这样的程序怎么编啊?
就是按一下k1,led闪1下,按一下k2,led闪两下,按一下k3,led闪三下,...
能否给个方法啊?

使用特权

评论回复
633
jianhong_wu|  楼主 | 2014-12-20 23:33 | 只看该作者
wxdx8320 发表于 2014-12-20 10:21
吴老师,能否给我讲下,这样的程序怎么编啊?
就是按一下k1,led闪1下,按一下k2,led闪两下,按一下k3,le ...

如果你好好研究一下我前面的按键程序跟跑马灯程序那部分程序,我相信你会受到启发的。我只能提示这么多。

使用特权

评论回复
634
wxdx8320| | 2014-12-21 09:47 | 只看该作者
本帖最后由 wxdx8320 于 2014-12-21 09:48 编辑
jianhong_wu 发表于 2014-12-20 23:33
如果你好好研究一下我前面的按键程序跟跑马灯程序那部分程序,我相信你会受到启发的。我只能提示这么多。 ...

void led_flicker()
{
    if(ucLedCnt!=0){
            switch(ucLedStep)
            {
                case 0:
                    if(uiTimeCnt>=const_time_level) //时间到
                    {
                        ET0=0;  //禁止定时中断
                        uiTimeCnt=0; //时间计数器清零
                        ET0=1; //开启定时中断
                        led_sc=0;    //让LED亮
                        ucLedStep=1; //切换到下一个步骤
                    }
                    break;
                case 1:
                    if(uiTimeCnt>=const_time_level) //时间到
                    {
                        ET0=0;  //禁止定时中断
                        uiTimeCnt=0; //时间计数器清零
                        ET0=1;   //开启定时中断
                        led_sc=1;    //让LED灭
                        ucLedStep=0; //返回到上一个步骤
                        ucLedCnt--;
                    }
                    break;
            }
        }
}
给ucLedCnt赋值就可以了。:handshake

使用特权

评论回复
635
hnkf118| | 2014-12-21 17:37 | 只看该作者
jianhong_wu 发表于 2014-3-5 21:58
第四节:累计定时中断次数使LED灯闪烁。

开场白:

呵呵, 我把类似这样需求的地方,写成一个模块了, 按键也是。 通讯也是。。。。

间隔控制002.jpg (155.09 KB )

间隔控制002.jpg

间隔控制001.jpg (194.78 KB )

间隔控制001.jpg

使用特权

评论回复
636
hnkf118| | 2014-12-21 17:39 | 只看该作者
wxdx8320 发表于 2014-12-20 10:21
吴老师,能否给我讲下,这样的程序怎么编啊?
就是按一下k1,led闪1下,按一下k2,led闪两下,按一下k3,le ...

用我贴图的那个程序,你这需求就小意思的了。

使用特权

评论回复
637
hnkf118| | 2014-12-21 17:45 | 只看该作者
hnkf118 发表于 2014-12-21 17:39
用我贴图的那个程序,你这需求就小意思的了。

大概就是这样的写法了,

#include "ivl_ctr.h"

void main (void)
{
    u8_t key;

    KeyInit();
    IvlCtrlInit();

    IvlCtrlTimeSet(K1_LED, 0, 100, 100);

    while (1) {
        if (KeyHit() ) {
            key = KeyGetKey();
            if (key == K1) {
                IvlCtrlModeSet(K1_LED, IVL_MODE_CNT, 1);
                IvlCtrlEnable(K1_LED);
                continue;
            }
            if (key == K2) {
                IvlCtrlModeSet(K1_LED, IVL_MODE_CNT, 2);
                IvlCtrlEnable(K1_LED);
                continue;
            }
            .... 同上
        }
    }
}

大概这样就OK了 当然还有一个定时器的初始化,我就不写了, 定时器1MS的定时就可以了。

使用特权

评论回复
638
hnkf118| | 2014-12-21 17:50 | 只看该作者
hnkf118 发表于 2014-12-21 17:39
用我贴图的那个程序,你这需求就小意思的了。


#include "xxx.h"
#include "key.h"
#include "ivl_ctrl.h"


void main (void)
{
    u8_t key;


    KeyInit();
    IvlCtrlInit();
    IvlCtrlTimeSet(K1_LED, 0, 100, 100);                   // 无间隔控制时间,100MS开/100MS关
    while (1) {
        if (KeyHit() ) {                                   // 测试是否有建按下
            key = KeyGetKey();                             // 读取按键代码
            if (key == K1) {                               // 是K1键?
                IvlCtrlModeSet(K1_LED, IVL_MODE_CNT, 1);   // 闪烁1次
                IvlCtrlEnable(K1_LED);                     // 允许运行
                continue;
            }
            if (key == K1) {                               // 是K1键?
                IvlCtrlModeSet(K1_LED, IVL_MODE_CNT, 2);   // 闪烁2次
                IvlCtrlEnable(K1_LED);                     // 允许运行
                continue;
            }
        }
    }
}

使用特权

评论回复
639
hnkf118| | 2014-12-21 17:51 | 只看该作者
类似这样的写法了, 当然定时器的初始化我没写, 定时器时间1MS 。然后调用IvlCtrlIntHander函数就搞定了。

使用特权

评论回复
640
xiaofei558008| | 2014-12-21 20:43 | 只看该作者
不错不错,非常感谢分享~

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则