打印
[资源共享]

简单直观的写程序

[复制链接]
4821|56
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
holts|  楼主 | 2018-3-25 15:23 | 只看该作者 |只看大图 回帖奖励 |倒序浏览 |阅读模式
本帖最后由 芯圣电子官方QQ 于 2023-7-20 11:14 编辑

芯圣的板子收到好久了,今天抽点空分享点东西给大家,共同进步

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

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

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




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

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

        //定时1ms
        //反推初值         = 65536 - ((1/1000) / (1/(Fosc / Timer分频系数)))
        //                           = 65536 - ((1/1000) / (1/(16000000 / 12)))
        //                        = 65536 - 1333
        //                        = 0xFACB
        TIM0_TimerCnt(0xFACB);                                                        //T0定时时间1ms
        TIM0_ITCmd(ENABLE);                                                                //T0中断使能
        TIM0_Cmd(ENABLE);                                                                //T0使能
        EA_Enable();                                                                        //使能总中断
}

沙发
holts|  楼主 | 2018-3-26 13:12 | 只看该作者
接下来,需要定义两个LED, 顺便学习下IO的设置知识



sbit LED1 = P0^0;
sbit LED2 = P1^0;


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





使用特权

评论回复
板凳
holts|  楼主 | 2018-3-27 21:18 | 只看该作者
本帖最后由 holts 于 2018-3-27 21:34 编辑

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




unsigned char  flash_led1(){

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

}

unsigned char  flash_led2(){

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

}



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

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



void Delay_Ms(u16 Time_count)
{
        u16 j;

        for(;Time_count > 0;Time_count --)
        for(j = 1596;j > 0;j --);
}

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

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


使用特权

评论回复
地板
holts|  楼主 | 2018-3-28 21:29 | 只看该作者
本帖最后由 holts 于 2018-3-28 21:54 编辑

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


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

#define MAXTASKS      8

volatile unsigned char timers[MAXTASKS];


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

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

主程序很简单:


void main(void)
{
    System_Init();      
    Time_Init();      
    LED_Init();         

    InitTasks();      
    while(1)
    {
        RunTaskA(flash_led1, Task0_ID);
        RunTaskA(flash_led2, Task1_ID);
    }   
}


使用特权

评论回复
5
holts|  楼主 | 2018-3-31 16:46 | 只看该作者
本帖最后由 holts 于 2018-4-1 09:00 编辑

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

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

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


unsigned char  flash_led2(){
_SS
  while(1){
        if (GPIO_ReadInputPin(GPIOT0,LED1)) SendSem(LightFlag);
          WaitSem(LightFlag);
          WaitX(100); LED2=!LED2;
   }
_EE
}


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

使用特权

评论回复
6
holts|  楼主 | 2018-4-1 09:09 | 只看该作者
本帖最后由 holts 于 2018-4-1 09:11 编辑

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

使用特权

评论回复
7
snowdpq| | 2018-4-1 10:24 | 只看该作者
holts 发表于 2018-4-1 09:09
娴话少说,实战调程序,编释通过

可以把程序大包发上来吗?

使用特权

评论回复
8
holts|  楼主 | 2018-4-1 18:27 | 只看该作者
经过一下午,终于把程序调通,接下来画板搞点复杂的,准备驱动TFT


使用特权

评论回复
9
holts|  楼主 | 2018-4-4 08:11 | 只看该作者


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

使用特权

评论回复
10
fox8769| | 2018-4-4 08:25 | 只看该作者
holts 发表于 2018-3-31 16:46
假如LED1每秒闪一次,慢闪, LED2每秒闪五次,快闪, 而且要求,LED2只在LED1不亮时闪, ...

看到这里我就笑了,应该是靠Switch切换任务的,_SS和_EE里面定义了很多东西,10年前就很多前辈讨论这种方式,最终还是缺陷大于优势,所以逐渐没人用了,楼主还藏着当宝。

使用特权

评论回复
11
fox8769| | 2018-4-4 08:35 | 只看该作者
上传当年用AVR的时候,玩的程序。

Feng_OS.rar

63.66 KB

使用特权

评论回复
12
holts|  楼主 | 2018-4-4 18:51 | 只看该作者
fox8769 发表于 2018-4-4 08:35
上传当年用AVR的时候,玩的程序。

不错,哪个task0为什么要放中断中? 有什么好处 ?

使用特权

评论回复
13
holts|  楼主 | 2018-4-7 12:44 | 只看该作者
ILI9220_V03.pdf (1.31 MB)

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

使用特权

评论回复
14
tlled| | 2018-4-8 17:33 | 只看该作者
自己设计的电路板?点赞

使用特权

评论回复
15
xad74| | 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
调度程序

使用特权

评论回复
16
Bruing| | 2018-4-26 23:01 | 只看该作者
牛X,8051都玩任务调度,学习下楼主的程序。

使用特权

评论回复
17
holts|  楼主 | 2018-4-27 14:13 | 只看该作者
Bruing 发表于 2018-4-26 23:01
牛X,8051都玩任务调度,学习下楼主的程序。

现在的客户需求变动的非常快,不上操作系统,还裸奔根本跟不上客户要求。

使用特权

评论回复
18
pmw_56| | 2018-4-27 21:31 | 只看该作者
mark...

使用特权

评论回复
19
47857872| | 2018-4-27 22:51 | 只看该作者
holts 发表于 2018-4-27 14:13
现在的客户需求变动的非常快,不上操作系统,还裸奔根本跟不上客户要求。 ...

加了这些 256 + 256的ram够用不啊。。。

使用特权

评论回复
20
holts|  楼主 | 2018-4-28 13:47 | 只看该作者
47857872 发表于 2018-4-27 22:51
加了这些 256 + 256的ram够用不啊。。。

加了这些只不过额外增加了几个字节,为什么不够用 ?

使用特权

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

本版积分规则

个人签名:QQ854887889  出几块TFT屏(2.2寸,有资料,送转接板),现在15RMB到付

44

主题

765

帖子

4

粉丝