[应用方案] 简单直观的写程序

[复制链接]
1097|47
 楼主 | 2018-3-25 15:23 | 显示全部楼层 |阅读模式
芯圣的板子收到好久了,今天抽点空分享点东西给大家,共同进步

一直以来,我就不喜欢螺奔,何况官方的库看了下还是不错的,结合官方的例子,很快就可以上手。

哪么问题来了,如何才能简单直观的写程序? 上个操作系统吧, 上个简单点的。

首先我要个定时器,每1ms中断一次,这在任务调度中可有用了,代码麻不用自己写,官方的贴一个。




  1. /************************************************************************************
  2. *                                                                         模块性能介绍
  3. *        1、HC89F003的定时器0,1共有四种工作方式
  4. *           方式0:16位自动重装定时器/计数器
  5. *           方式1:16位定时器/计数器
  6. *           方式2:8位自动重装定时器/计数器
  7. *           方式3:T0分成两个(TL0/TH0)独立的8位定时器/计数器(T1无此模式)
  8. *        ************************************************************************************
  9. *                                                                         应用注意事项
  10. *        1、方式3时T0占用T1的TR1、TF1及中断源,由于TR1被T0占用,此时需要关闭T1可将T1设为工作
  11. *           方式3
  12. *        2、在方式0(Mode0)时THx(x = 0,1)和TLx(x = 0,1)读写操作遵循先高位后低位。并且在修改
  13. *           重载数据时,低位无论有无改变,只要高位被修改,低位都必须再写入一次,方式 1、2、3
  14. *           时无此要求
  15. *        ************************************************************************************/
  16. void Time_Init(void)
  17. {
  18.         TIM0_Init(TIM0_CLK_12,TIM0_SOFT_CONTROL,TIM0_INTIMING,TIM0_16BIT_AUTO_CNT_TIMING);
  19.         //时钟12分频,只需软件置TRx即可启动Tx,Tx用于内部定时,16位重装载定时器/计数器

  20.         //Tim0计算时间         = (65536 - 0xFACB) * (1 / (Fosc /Timer分频系数))
  21.         //                                = 1333 / (16000000 / 12)
  22.         //                                = 1 ms

  23.         //定时1ms
  24.         //反推初值         = 65536 - ((1/1000) / (1/(Fosc / Timer分频系数)))
  25.         //                           = 65536 - ((1/1000) / (1/(16000000 / 12)))
  26.         //                        = 65536 - 1333
  27.         //                        = 0xFACB
  28.         TIM0_TimerCnt(0xFACB);                                                        //T0定时时间1ms
  29.         TIM0_ITCmd(ENABLE);                                                                //T0中断使能
  30.         TIM0_Cmd(ENABLE);                                                                //T0使能
  31.         EA_Enable();                                                                        //使能总中断
  32. }

复制代码
 楼主 | 2018-3-26 13:12 | 显示全部楼层
接下来,需要定义两个LED, 顺便学习下IO的设置知识



  1. sbit LED1 = P0^0;
  2. sbit LED2 = P1^0;


  3. /*************************************************************************************
  4. *                                     模块性能介绍
  5. *    1、输入(无SMT)模式下VDD=5V时,低电平即输入低电压(VIL1)范围为0~1.5V、高电平即输入
  6. *       高电压(VIH1)范围为3.5~5V。
  7. *    2、输入(SMT)模式下VDD=5V时,低电平即输入低电压(VIL1)范围为0~1V、高电平即输入高电
  8. *       压(VIH1)范围为4~5V。
  9. *    3、P0xDBCT [5:0]配置的消抖时间是一个范围,分频系数*Tosc*P0xDBCT[5:0]-Tosc<消抖时间<
  10. *       分频系数*Tosc*(P0xDBCT[5:0]+1)-Tosc。
  11. *    4、MCU上电复位以及其它复位功能的复位,所有的IO相关寄存器都会恢复为默认值。
  12. *    ************************************************************************************
  13. *                                     应用注意事项
  14. *    1、在使用仿真器时,与JTAG复用的IO口会有读取或写入数据异常,因此建议使用仿真器时不要
  15. *       操作这几个IO口。断开仿真器使用电源供电即可以正常操作。
  16. *    2、分配到P0.0/P0.1/P0.2这三个管脚上的功能脚、外部中断输入、故障检测引脚都受消抖控制。
  17. *    ************************************************************************************
  18. *     P0M1 = P0M1&0xF0|0x00;                P02设置为输入(无SMT)
  19. *      P0M0 = P0M0&0x0F|0x10;                P01设置为带下拉输入(无SMT)
  20. *      P0M0 = P0M0&0xF0|0x02;                P00设置为带上拉输入(无SMT)
  21. *      P1M0 = P1M0&0x0F|0x30;                P11设置为带模拟输入
  22. *      P2M0 = P2M0&0xF0|0x04;                P20设置为输入(SMT)
  23. *      P0M2 = P0M2&0x0F|0x50;                P05设置为带下拉输入(SMT)
  24. *      P0M2 = P0M2&0xF0|0x06;                P04设置为带上拉输入(SMT)
  25. *      P0M3 = P0M3&0x0F|0x80;                P07设置为推挽输出
  26. *      P0M3 = P0M3&0xF0|0x09;                P06设置为开漏输出
  27. *      P2M1 = P2M1&0xF0|0x0A;                P22设置为带上拉开漏输出
  28. **************************************************************************************/
  29. void LED_Init(void)
  30. {
  31.         GPIO_Init(GPIOT0,LED1,GPIO_MODE_OUT_PP);  //P00 推挽输出
  32.         GPIO_Init(GPIOT1,LED2,GPIO_MODE_OUT_PP);    //P10 推挽输出
  33. }



复制代码


 楼主 | 2018-3-27 21:18 | 显示全部楼层
本帖最后由 holts 于 2018-3-27 21:34 编辑

当然还需要定义两个闪灯任务,一个闪LED1, 一个闪LED2,象这样:




  1. unsigned char  flash_led1(){

  2.   while(1){ WaitX(100); LED1=!LED1;   }

  3. }

  4. unsigned char  flash_led2(){

  5.   while(1){ WaitX(100); LED2=!LED2;   }

  6. }

复制代码


由于我们的时钟是1ms,  Waitx(100)的意思是等待100ms,  也就是每100ms对LED取反一次,效果就是闪灯, 是不是很简单

等待时CPU都在做什么,常见的做法是这样:



  1. void Delay_Ms(u16 Time_count)
  2. {
  3.         u16 j;

  4.         for(;Time_count > 0;Time_count --)
  5.         for(j = 1596;j > 0;j --);
  6. }
复制代码

可见这种方式,CPU原地绕圈,等够时间再往下走, 这是多么浪费生产力啊。

本程序不用这种方式,我们用Waitx(100),   当CPU执行到这里时,发现要等100ms,  当前没事可做,CPU立即跳转到其它地方做些有益的事,不用在原地绕圈,这就是Delay和WaitX的分别


 楼主 | 2018-3-28 21:29 | 显示全部楼层
本帖最后由 holts 于 2018-3-28 21:54 编辑

继续,我们规划这个小小的操作系统最多支持8个任务,对于这个芯圣小CPU够用了


  1. // task constant define
  2. #define Task0_ID      0
  3. #define Task1_ID      1
  4. #define Task2_ID      2
  5. #define Task3_ID      3
  6. #define Task4_ID      4
  7. #define Task5_ID      5
  8. #define Task6_ID      6
  9. #define Task7_ID      7

  10. #define MAXTASKS      8

  11. volatile unsigned char timers[MAXTASKS];
复制代码


每个任务单独配个定时器,记录分配个每个任务的时间片,我们只需要在时钟中断中轮询一遍timers[],任务状态就一目了然了。

任务号越小,优先级越高,假设LED1,LED2两个闪灯条件都满足,哪个先闪,任务号小哪个先闪。

主程序很简单:


  1. void main(void)
  2. {
  3.     System_Init();      
  4.     Time_Init();      
  5.     LED_Init();         

  6.     InitTasks();      
  7.     while(1)
  8.     {
  9.         RunTaskA(flash_led1, Task0_ID);
  10.         RunTaskA(flash_led2, Task1_ID);
  11.     }   
  12. }
复制代码


 楼主 | 2018-3-31 16:46 | 显示全部楼层
本帖最后由 holts 于 2018-4-1 09:00 编辑

假如LED1每秒闪一次,慢闪, LED2每秒闪五次,快闪, 而且要求,LED2只在LED1不亮时闪,怎么做呢?

实现的方法是多样的,从简单直观来讲,或从操作系统的角度来看这个问题,我们引入一个信号的说法

先定义一个亮灯标志
SEM  LightFlag  ;


  1. unsigned char  flash_led2(){
  2. _SS
  3.   while(1){
  4.         if (GPIO_ReadInputPin(GPIOT0,LED1)) SendSem(LightFlag);
  5.           WaitSem(LightFlag);
  6.           WaitX(100); LED2=!LED2;
  7.    }
  8. _EE
  9. }
复制代码


程序其它地方都不用改,是不是很简单

 楼主 | 2018-4-1 09:09 | 显示全部楼层
本帖最后由 holts 于 2018-4-1 09:11 编辑

娴话少说,实战调程序,编释通过

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
| 2018-4-1 10:24 | 显示全部楼层
holts 发表于 2018-4-1 09:09
娴话少说,实战调程序,编释通过

可以把程序大包发上来吗?
 楼主 | 2018-4-1 18:27 | 显示全部楼层
经过一下午,终于把程序调通,接下来画板搞点复杂的,准备驱动TFT


本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
 楼主 | 2018-4-4 08:11 | 显示全部楼层


到货,等我找点资料再来写程序

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
| 2018-4-4 08:25 | 显示全部楼层
holts 发表于 2018-3-31 16:46
假如LED1每秒闪一次,慢闪, LED2每秒闪五次,快闪, 而且要求,LED2只在LED1不亮时闪, ...

看到这里我就笑了,应该是靠Switch切换任务的,_SS和_EE里面定义了很多东西,10年前就很多前辈讨论这种方式,最终还是缺陷大于优势,所以逐渐没人用了,楼主还藏着当宝。
| 2018-4-4 08:35 | 显示全部楼层
上传当年用AVR的时候,玩的程序。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
 楼主 | 2018-4-4 18:51 | 显示全部楼层
fox8769 发表于 2018-4-4 08:35
上传当年用AVR的时候,玩的程序。

不错,哪个task0为什么要放中断中? 有什么好处 ?
 楼主 | 2018-4-7 12:44 | 显示全部楼层


找到TFT屏资料,要花点时间研究下

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?注册

x
| 2018-4-8 17:33 | 显示全部楼层
自己设计的电路板?点赞
| 2018-4-11 16:49 | 显示全部楼层
****小小调度器开始**********************************************/
#ifndef _Task_H_
#define _Task_H_

#define MAXTASKS 3


//volatile
extern volatile unsigned char timers[MAXTASKS];
#define _SS static unsigned char _lc=0; switch(_lc){default:
#define _EE ;}; _lc=0; return 255;
#define WaitX(tickets) do {_lc=(__LINE__%255)+1; return tickets ;} while(0); case (__LINE__%255)+1:
#define RunTask(TaskName,TaskID) do { if (timers[TaskID]==0) timers[TaskID]=TaskName(); } while(0);
#define RunTaskA(TaskName,TaskID) { if (timers[TaskID]==0) {timers[TaskID]=TaskName(); continue;} } //前面的任务优先保证执行
#define CallSub(SubTaskName) do {unsigned char currdt; _lc=(__LINE__%255)+1; return 0; case (__LINE__%255)+1: currdt=SubTaskName();if(currdt!=255) return currdt;} while(0); //和上面是同一行,因为一行显示不下了,里面有 return 0,在执行子函数前释放了一下 CPU
#define InitTasks() {unsigned char i; for(i=MAXTASKS;i>0 ;i--) timers[i-1]=0; }
#define UpdateTimers() {unsigned char i; for(i=MAXTASKS;i>0 ;i--){if((timers[i-1]!=0)&&(timers[i-1]!=255)) timers[i-1]--;}}
#define SEM unsigned char
//初始化信号量
#define InitSem(sem) sem=0;
//等待信号量
#define WaitSem(sem) do{ sem=1; WaitX(0); if (sem>0) return 1;} while(0); //里面有WaitX(0),执行的时候释放了一下 CPU
//等待信号量或定时器溢出, 定时器 tickets 最大为 0xFFFE
#define WaitSemX(sem,tickets) do { sem=tickets+1; WaitX(0); if(sem>1){ sem--; return 1;} } while(0); //里面有 WaitX(0),执行的时候释放了一下 CPU
//发送信号量
#define SendSem(sem) do {sem=0;} while(0);
/*****小小调度器结束**********************************************************************/



#endif
调度程序
| 2018-4-26 23:01 | 显示全部楼层
牛逼,8051都玩任务调度,学习下楼主的程序。
 楼主 | 2018-4-27 14:13 | 显示全部楼层
Bruing 发表于 2018-4-26 23:01
牛逼,8051都玩任务调度,学习下楼主的程序。

现在的客户需求变动的非常快,不上操作系统,还裸奔根本跟不上客户要求。
| 2018-4-27 21:31 | 显示全部楼层
mark...
| 2018-4-27 22:51 | 显示全部楼层
holts 发表于 2018-4-27 14:13
现在的客户需求变动的非常快,不上操作系统,还裸奔根本跟不上客户要求。 ...

加了这些 256 + 256的ram够用不啊。。。
 楼主 | 2018-4-28 13:47 | 显示全部楼层
47857872 发表于 2018-4-27 22:51
加了这些 256 + 256的ram够用不啊。。。

加了这些只不过额外增加了几个字节,为什么不够用 ?
扫描二维码,随时随地手机跟帖
您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复

您需要登录后才可以回帖
登录 | 注册
高级模式
我要创建版块 申请成为版主

论坛热帖

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