OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 29003|回复: 80

我的雕刻机 MSOS+FATFS+SD+NRF24L01+GUI

  [复制链接]

7

主题

64

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
3148
金钱
3148
注册时间
2013-12-8
在线时间
431 小时
发表于 2017-4-7 15:18:33 | 显示全部楼层 |阅读模式
本帖最后由 zgp0518 于 2017-4-11 13:43 编辑

我原来在16年初的时候,用光驱的步进电机DIY了1个简易的雕刻机,有人提到用雕刻机做PCB板,但光驱步进电机功率太小,所以重新设计了这个雕刻机。
主要分成2部分,一部分为控制器,一部分为主机。
控制器:主芯片:STM32F103ZET6;显示 TFT 3.2寸带触摸,FSMC控制;NRF24L01通讯;SD卡
主机:控制采用STM32F103C8T6最小系统版;NRF24L01通讯;37步进电机*3,A4988步进电机控制器*3,XD211槽型光电模块*2 X轴,Y轴限位用,755高速电机,JTO0.3-4钻夹头;直径8mm,行程1mm丝杆;滑块,不锈钢外壳。
不锈钢外壳我先通过三维SolidWorks把外形设计出来后用1.5mm不锈钢激光切割折弯而成。
dk.png
刚开始的时候,只用了1个单片机直接控制,但发现安装和操作不方便,所以使用了2个单片机,一个C8T6直接控制雕刻机,一个ZET6带TFT触摸屏进行控制和设置。2个单片机之间用NRF24L01进行通信,减少相互间联线。
这些是雕刻机的照片,我用双色板来模拟的PCB板,雕刻机主机下面的主板就是用这个程序雕刻出来的。
现在这个程序我USB和串口控制还没有加入。现在是SD卡文件控制。
这个是雕刻机的程序: 雕刻机程序.rar (3.29 MB, 下载次数: 13405)
正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

6

主题

24

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
250
金钱
250
注册时间
2017-2-20
在线时间
65 小时
发表于 2017-4-7 15:54:26 | 显示全部楼层
回复 支持 反对

使用道具 举报

7

主题

64

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
407
金钱
407
注册时间
2016-7-5
在线时间
64 小时
发表于 2017-4-7 15:58:21 | 显示全部楼层
火前刘明,膜拜大神
回复 支持 反对

使用道具 举报

29

主题

486

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
3080
金钱
3080
注册时间
2014-7-19
在线时间
413 小时
发表于 2017-4-7 16:23:23 | 显示全部楼层
厉害! 我也想试试了!
电子人生!
回复 支持 反对

使用道具 举报

22

主题

255

帖子

0

精华

高级会员

Rank: 4

积分
770
金钱
770
注册时间
2017-2-19
在线时间
132 小时
发表于 2017-4-7 16:37:59 | 显示全部楼层
赞赞
回复 支持 反对

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164867
金钱
164867
注册时间
2010-12-1
在线时间
2099 小时
发表于 2017-4-7 18:10:01 | 显示全部楼层
cool,楼主动手能力一级棒啊。。。
回复 支持 反对

使用道具 举报

558

主题

11万

帖子

34

精华

管理员

Rank: 12Rank: 12Rank: 12

积分
164867
金钱
164867
注册时间
2010-12-1
在线时间
2099 小时
发表于 2017-4-7 18:13:04 | 显示全部楼层
论坛急需楼主这样的DIY爱好者啊
我是开源电子网www.openedv.com站长,有关站务问题请与我联系。
正点原子STM32开发板购买店铺http://openedv.taobao.com
正点原子官方微信公众平台,点击这里关注“正点原子”
回复 支持 反对

使用道具 举报

33

主题

481

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
5075
金钱
5075
注册时间
2013-10-4
在线时间
654 小时
发表于 2017-4-7 18:22:57 | 显示全部楼层
厉害了,顶起
回复 支持 反对

使用道具 举报

2

主题

130

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1850
金钱
1850
注册时间
2011-9-16
在线时间
414 小时
发表于 2017-4-7 21:48:30 | 显示全部楼层
厉害
回复 支持 反对

使用道具 举报

1

主题

78

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
211
金钱
211
注册时间
2015-12-10
在线时间
49 小时
发表于 2017-4-7 21:55:43 | 显示全部楼层
动手能力超强  
逆水行舟不进则退
回复 支持 反对

使用道具 举报

50

主题

1804

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
6632
金钱
6632
注册时间
2016-5-29
在线时间
906 小时
发表于 2017-4-7 22:29:25 | 显示全部楼层
桌面雕刻机做电路板还有一个问题.就是过孔怎么办?很精细的电路板明显不太好做.做其实的东西可能更加实用.电路板还是用传统的办法来实现比较方便.
回复 支持 反对

使用道具 举报

7

主题

64

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
3148
金钱
3148
注册时间
2013-12-8
在线时间
431 小时
 楼主| 发表于 2017-4-7 22:33:00 | 显示全部楼层
本帖最后由 zgp0518 于 2017-4-7 22:39 编辑
操作系统 发表于 2017-4-7 22:29
桌面雕刻机做电路板还有一个问题.就是过孔怎么办?很精细的电路板明显不太好做.做其实的东西可能更加实用.电 ...

这个肯定不能做太精细的PCB板,过孔直接换钻头由雕刻机进行钻孔,要不手动要打很久,而且还不准,一般用这个做PCB就普通的DIY用,精细的就直接打板吧
这张照片是我雕刻机自己使用的PCB,先把过孔通过雕刻机钻好了
IMG_2034.JPG
回复 支持 反对

使用道具 举报

24

主题

695

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1665
金钱
1665
注册时间
2016-4-29
在线时间
266 小时
发表于 2017-4-7 23:00:04 | 显示全部楼层
哇靠,牛……………………
回复 支持 反对

使用道具 举报

42

主题

145

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
352
金钱
352
注册时间
2016-7-17
在线时间
58 小时
发表于 2017-4-7 23:09:42 来自手机 | 显示全部楼层
我的天,我很少崇拜别人,但是看了你一个人做的机器,我崇拜你了,我给你个建议,其实这个雕刻机可以做精细的电路板的,你把控制电机走动的控制器换一下,用TMS320F28335,这样你每次打孔时的偏差就会非常非常小了。步进电机用好点的,然后你的雕刻机就完美了
回复 支持 反对

使用道具 举报

7

主题

64

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
3148
金钱
3148
注册时间
2013-12-8
在线时间
431 小时
 楼主| 发表于 2017-4-8 07:23:23 来自手机 | 显示全部楼层
一起走过的日子 发表于 2017-4-7 23:09
我的天,我很少崇拜别人,但是看了你一个人做的机器,我崇拜你了,我给你个建议,其实这个雕刻机可以做精细 ...

我现在做的精度是0.01mm。对应的脉冲数量为64个。计算的精度还可以提高。直线插补用的是中值逐点插补法。圆弧是常规的逐点插补法。所有计算为整数运算。51单片机都可以使用。现在要提高精度的话需要加强外壳和更换高精度丝杆和滑块。这个成本就厉害了。
回复 支持 反对

使用道具 举报

22

主题

103

帖子

0

精华

高级会员

Rank: 4

积分
950
金钱
950
注册时间
2017-2-23
在线时间
205 小时
发表于 2017-4-8 10:47:30 | 显示全部楼层
牛B,大神
回复 支持 反对

使用道具 举报

42

主题

145

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
352
金钱
352
注册时间
2016-7-17
在线时间
58 小时
发表于 2017-4-8 13:36:22 来自手机 | 显示全部楼层
zgp0518 发表于 2017-4-8 07:23
我现在做的精度是0.01mm。对应的脉冲数量为64个。计算的精度还可以提高。直线插补用的是中值逐点插补法。 ...

这个精度,差不多了,哈哈,可以的,大神
回复 支持 反对

使用道具 举报

4

主题

118

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1632
金钱
1632
注册时间
2013-9-2
在线时间
703 小时
发表于 2017-4-9 21:13:06 | 显示全部楼层
佩服,佩服
回复 支持 反对

使用道具 举报

21

主题

1406

帖子

0

精华

论坛元老

Rank: 8Rank: 8

积分
5845
金钱
5845
注册时间
2015-8-25
在线时间
958 小时
发表于 2017-4-9 23:35:50 | 显示全部楼层
厉害啊,这个机械结构也是自己设计的吗?用啥设计的啊,也想学学
He who fights with monsters should look to it that he himself does not become a monster, when you gaze long into the abyss, the abyss also gazes into you.
过于执着就会陷入其中,迷失自己,困住自己。
回复 支持 反对

使用道具 举报

7

主题

64

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
3148
金钱
3148
注册时间
2013-12-8
在线时间
431 小时
 楼主| 发表于 2017-4-10 10:39:07 | 显示全部楼层
本帖最后由 zgp0518 于 2017-4-11 13:07 编辑

MSOS基本介绍:
回复 支持 反对

使用道具 举报

7

主题

64

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
3148
金钱
3148
注册时间
2013-12-8
在线时间
431 小时
 楼主| 发表于 2017-4-10 10:39:15 | 显示全部楼层
本帖最后由 zgp0518 于 2017-4-10 10:41 编辑

一、MSOS基本介绍:
MSOS的基本配置在OS_conf.h中配置
主要是配置任务数量和堆栈的长度,由于MSOS是简化版的uCOS的简化版,因此不提供任务的删除,所有任务需在开始的时候就创建。
[mw_shl_code=applescript,true]        /*TaskSum必须要跟实际配套,实际的任务数必须小于TaskSum,否则出现异常
        如果TaskSum 大于实际任务数,则大于部分的RAM是浪费的,但程序可以运行
        为了简化定义,每个任务的任务栈深度和消息队列深度改为相同的大小
        */
        #define TaskStackSumIsSame                0                                                /*是否使用相同数量的堆栈*/
        #define TaskSum                                             5           /*任务总数4个用户任务加1个系统任务,最大为8个,其中用户任务7个,系统任务1个*/
        #define QueueStackSum                                        20                                        //消息队列深度        
               
        #if TaskStackSumIsSame == 0        
                #define TaskStackSum0                   200         //任务栈深度
                #define TaskStackSum1                   200         //任务栈深度
                #define TaskStackSum2                   200         //任务栈深度
                #define TaskStackSum3                   200         //任务栈深度
                #define TaskStackSum4                   64         //任务栈深度
                #define TaskStackSum5                   64         //任务栈深度
                #define TaskStackSum6                   64         //任务栈深度
                #define TaskStackSum7                   64         //任务栈深度
        #endif
        #if TaskStackSumIsSame == 1        
                #define TaskStackSum                   200         //任务栈深度
        #endif
[/mw_shl_code]
在创建任务前需要创建任务的指针,同时也是任务的优先级。

[mw_shl_code=applescript,true]
u8 NRFTaskPriority ;  /*NRF解析任务*/
u8 MotoTaskPriority ;  /*MOTO解析任务*/
u8 CtrlTaskPriority;
/*******************************************************************************
* 函数名        : main
* 描述            : msOS整个程序入口
*           : InitializeData:初始化存储数据区
* 输入参数  : 无
* 返回参数  : int
********************************************************************************
*版本     作者            日期            说明
*V0.1    Wangsw        2013/09/03       初始版本
*******************************************************************************/
int main(void)
{
        NVIC_Config();
                 
        InitializeOs();                              //初始化OS      
        InitializeApp();                                                    //初始化应用层
        NRFTaskPriority = OS.CreateTask(NRFTask); //创建任务,优先级0   NRFTask为NRF通讯任务的主函数      NRFTaskPriority  为NRFTask的优先级同时也是指针
        CtrlTaskPriority = OS.CreateTask(CtrlTask);
        MotoTaskPriority = OS.CreateTask(MotoTask);

  OS.Start();                                                  //MSOS启动
}
[/mw_shl_code]
任务的优先级是按照创建的顺序来实现的,最早创建的任务优先级最高。系统任务也就是空闲任务在OS.Start()中自动创建。

用户任务必须为死循环,在死循环中必须有OS.PendMessageQueue函数或OS.DelayTimeTick函数把系统的控制权交还给OS来切换任务。

[mw_shl_code=applescript,true]/*******************************************************************************
* 函数名        : PendMessageQueue
* 描述            : 等待消息队列,当消息队列为空时,所在任务挂起
* 输入参数  : eventPointer 队列事件块指针,timeout 等待时间,1mS为单位
* 返回参数  :
********************************************************************************
*版本     作者            日期            说明
*V0.2                        ZZ                                         2013/10/08                                修改了不能同时post2个不是最高优先级的BUG
*V0.1    Wangsw        2013/09/11       初始版本
*******************************************************************************/
static void * PendMessageQueue (uint32_t timeout)
{
    void * messagePointer;
    Queue * queuePointer;

//    Assert(InterruptNesting == 0);

    EnterCritical();
               
    queuePointer = OsCurrentTaskPointer->QueuePointer;                     /* Point at queue control block                       */
    if (queuePointer->Entries > 0)                                                                 /* See if any messages in the queue                   */
    {                    
                                messagePointer = PopQueue(queuePointer);
               ExitCritical();
               return (messagePointer);                                                    /* Return message received                            */
    }
    OsCurrentTaskPointer->State |= TaskStatusMessageQueue;                        /* Task will have to pend for a message to be posted  */
    OsCurrentTaskPointer->Delay   = timeout;                                          /* Load timeout into TCB                  */
    PriorityReadyTable &= ~(1 <<  OsCurrentPriority);               
    ExitCritical();
    Schedule();              /*任务切换节点,如果当前没有message的话就切换任务,当有message的时候就从这里切换回来       */
    EnterCritical();
                queuePointer = OsCurrentTaskPointer->QueuePointer;
                messagePointer = PopQueue(queuePointer);
    if ((messagePointer != OS_null) || (timeout == 0))                   /*Wangsw add || (timeout == 0) for direct DataQueue */
    {                                                                                                          /* Did we get a message?                              */
        OsCurrentTaskPointer->MessagePointer      = OS_null;                         /* Extract message from TCB (Put there by QPost)      */
        OsCurrentTaskPointer->State     = TaskStatusReady;
        ExitCritical();
        return (messagePointer);                                                /* Return message received                            */
    }            
    OsCurrentTaskPointer->State = TaskStatusReady;
    ExitCritical();
    return (OS_null);                                                                      /* No message received                                */
}
/*******************************************************************************
* 函数名 : DelayTimeTick
* 描述     : 任务挂起等待时间,单位为1mS
* 输入参数  : timeTick,挂起时间,1mS为单位,比如1000,就是1秒钟
* 返回参数  : 无
********************************************************************************
*版本     作者            日期            说明
*V0.1    Wangsw        2013/09/11       初始版本
*******************************************************************************/
static void DelayTimeTick (uint32_t timeTick)
{
    if (timeTick > 0)
    {                                                   
        EnterCritical();
        PriorityReadyTable &= ~(1 << OsCurrentPriority);
        OsCurrentTaskPointer->Delay = timeTick;
        ExitCritical();
        Schedule();
    }
}
[/mw_shl_code]

PendMessageQueue 主要是在运行到这个函数的时候进行消息获取,但如果消息列队中为空的时候进行任务切换。
DelayTimeTick  是当需要延时时切换出任务,当延时时间到后切换回来,时间单位为1ms
如果一个任务是纯粹的死循环,不需要其他任务发送给消息的时候可以不用PendMessageQueue 函数,只用DelayTimeTick  短时延时并交出控制权。
对应于PendMessageQueue 函数PostMessageQueue 为消息发送函数
[mw_shl_code=applescript,true]/*******************************************************************************
* 函数名        : PostMessageQueue
* 描述            : 发送一个消息到消息队列中,处于等待的任务会自动运行
* 输入参数  : eventPointer 队列事件块指针,messagePointer发送消息指针
* 返回参数  : 无
********************************************************************************
*版本     作者            日期            说明
*V0.2                        ZZ                                         2013/10/08                                修改了不能同时post2个不是最高优先级的BUG
*V0.1    Wangsw        2013/09/11       初始版本
*******************************************************************************/
static uint8_t PostMessageQueue (uint8_t Priority, void *messagePointer)
{
    Queue * queuePointer;
                Task *taskPointer;        
//          Assert(TaskCounter >= Priority);        
    EnterCritical();
          taskPointer = &TaskBlock[Priority];
    queuePointer = taskPointer->QueuePointer;                 /* Point to queue control block                  */        
    if (queuePointer->Entries == 0x00)                                             /* 当前的队列里消息长度为0            */
    {                 
                                PushQueue(queuePointer,messagePointer);                                                                //将消息压入队列
        taskPointer->Delay = 0;
        taskPointer->MessagePointer = messagePointer;
        taskPointer->State &= ~TaskStatusMessageQueue;                        
        if (taskPointer->State == TaskStatusReady)                //如果是ready状态
        {
                                        PriorityReadyTable |=  1 << taskPointer->Priority;  
                                        ExitCritical();
                                        Schedule();                                                 /* Find highest priority task ready to run       */
                                        return (OS_true);         
        }
                                PushQueue(queuePointer,messagePointer);               
        ExitCritical();
        return (OS_true);
    }
    if (queuePointer->Entries >= queuePointer->Size)
    {                                                               /* Make sure queue is not full                   */
        ExitCritical();
        return (OS_false);
    }
                PushQueue(queuePointer,messagePointer);
    ExitCritical();
    return (OS_true);
}[/mw_shl_code]
PostMessageQueue  为发送一个消息到指定任务的消息列队,消息发送完成后如果接收消息的任务优先级高于本身的优先级的话,MSOS为自动切换任务到接收消息的任务中去。
消息为32位的4字节的数据,可以是指针,函数或者你自己定义的消息,具体区分靠最高为字节来定义数据类型,具体类型在OS_CONF.H中定义(0xF0-0XFF,0X20和0X08),或者在APP中定义,但不能是0X20和0X08。

正常的一个任务的主函数如下:
通过选择获取的message 的内容来知道获取的是什么类型的数据,并进行处理。
[mw_shl_code=applescript,true]void MotoTask(void)
{
        u32 message;
        InitPlanQueue();
  while(1)
        {                     
                message = (u32)OS.PendMessageQueue(5000);  
         switch(GetMessageType(message))
                {
                        case UseMessage_PlanRun:                                                            //Plan运行消息
                                PlanRun();
                                break;
                        case FuntionMessageType:                    //函数指针消息,直接以0x20或0x08来判断
                                Function(message);
                                break;
          default:                                
                                break;
                }

        }
}[/mw_shl_code]

MSOS提供了2种软定时器,一种由MSOS自动分配定时器的编号,时间间隔为1ms,定时器在计时过程中无法取消;另一种为指定定时器编号,定时时间为0.1ms,指定定时器编号的可以在计时过程中取消定时。
[mw_shl_code=applescript,true]/*******************************************************************************
* 函数名        : Start
* 描述            : 软件定时器
* 输入参数  : handleMode: 两种处理方式,一种直接在节拍中断中处理,适合费用低的,
*                         另一种在消息中处理,适合处理费用高的。
*           : delay:延时节拍数,以系统节拍为单位
*           : registerFunction: 注册回调执行函数,延时超时后,执行此函数。
*             data:消息值,MessageTimer类型为32bit地址,其他类型下都是24bit数据
* 返回参数  : uint8_t类型,返回ID号,从0开始,若失败则返回invalid(-1)
*******************************************************************************/
static uint8_t Start(TimerhandleModeEnum handleMode, uint32_t delay, OS_func registerFunction)
{
    uint8_t i;

    EnterCritical();
        
    for(i = 0; i < Timer1msSum; i++)
    {
        if(!OS_GetBit(State_1ms, i))
        {                        
            TimerBlock_1ms.Delay = delay;/*延时时间*/
            TimerBlock_1ms.RegisterFunction = registerFunction;/*回调函数*/
            if(handleMode)
            {
                OS_SetBit(Mode_1ms, i);
                                                                TimerBlock_1ms.Priority = OsCurrentPriority;
            }
            else
            {
                OS_ResetBit(Mode_1ms, i);
            }
                        
            OS_SetBit(State_1ms, i);
            ExitCritical();
            return(i);
        }
    }
    ExitCritical();
    return(OS_invalid);
}
/*******************************************************************************
* 函数名        : Start
* 描述            : 软件定时器
* 输入参数  : handleMode: 两种处理方式,一种直接在节拍中断中处理,适合费用低的,
*                         另一种在消息中处理,适合处理费用高的。
*           : delay:延时节拍数,以系统节拍为单位
*           : registerFunction: 注册回调执行函数,延时超时后,执行此函数。
*             data:消息值,MessageTimer类型为32bit地址,其他类型下都是24bit数据
* 返回参数  : uint8_t类型,返回ID号,从0开始,若失败则返回invalid(-1)
*******************************************************************************/
void StartAt(uint8_t id, uint32_t delay, OS_func registerFunction)
{
        EnterCritical();        
        TimerBlock_100us[id].Delay = delay;/*延时时间*/
        TimerBlock_100us[id].RegisterFunction = registerFunction;/*回调函数*/
        TimerBlock_100us[id].Priority = OsCurrentPriority;
        OS_SetBit(State_100us, id);
        ExitCritical();
}

/*******************************************************************************
* 函数名        : Stop
* 描述            : 停止某一路的软件定时器
* 输入参数  : id为0、1、2...
* 返回参数  : 无
********************************************************************************
*版本     作者            日期            说明
*V0.1    Wangsw        2013/09/10       初始版本
*******************************************************************************/
static void StopAt(uint8_t id)
{
//    Assert(id < Timer1msSum);
   
    EnterCritical();
        
    OS_ResetBit(State_100us, id);
        
    ExitCritical();
}[/mw_shl_code]

同时MSOS提供了8个互斥量,编号为0-7,在使用的时候需要自己指定使用那个互斥量编号,由于我使用到的互斥量只是在GUI的TFT读写函数中用到,所以没有做自动分配
[mw_shl_code=applescript,true]/*******************************************************************************
* 函数名        : OSMutexLock
* 描述            : 互斥量闭锁
* 输入参数  : OSMutexNum:互斥量编号        
* 返回参数  : 0:成功;非0:不成功
********************************************************************************
*版本     作者            日期            说明
*V2.0                        ZZ                                         2014/10/24                                
*******************************************************************************/
uint8_t OSMutexLock (uint8_t OSMutexNum)
{   //不得在中断中调用该函数   
//        Assert(OSMutexNum <8);
        if(OSMutexNum > 7) return 1;                        //mutex编号大于7的话出错        0-7 对应8个通道
        EnterCritical();                        //关闭中断     
        
        if (!OS_IsUseMutex(OSMutexNum))                                //互斥标志为0,信号量可用  
        {
                OsCurrentTaskPointer->State |=(0x100<<OSMutexNum);     //设置互斥标志
                ExitCritical();                                //开启中断
                return 0;
        }
        //互斥标志信号已经有任务置位,说明被占用
                OsCurrentTaskPointer->State |=(0x100<<OSMutexNum);     //设置互斥标志
        PriorityReadyTable &= ~(1 <<  OsCurrentPriority);                                                //清除优先级就绪
        ExitCritical();                                                //开启中断        
        Schedule();              /*任务切换节点,如果当前没有message的话就切换任务,当有message的时候就从这里切换回来       */
        return 0;
}

/*******************************************************************************
* 函数名        : OSMutexUnLock
* 描述            : 互斥量解锁
* 输入参数  : OSMutexNum:互斥量编号        
* 返回参数  : 无
********************************************************************************
*版本     作者            日期            说明
*V2.0                        ZZ                                         2014/10/24                                
*******************************************************************************/
void  OSMutexUnLock (uint8_t OSMutexNum)
{   //不得在中断中调用该函数
        uint8_t MutexTaskNum;
        uint8_t MutexHighPriority;  
        Task *taskPointer;
//        Assert(OSMutexNum < 8);        
        EnterCritical();                        //关闭中断   
        OsCurrentTaskPointer->State &=~(0x100<<OSMutexNum);     //取消互斥标志
        MutexTaskNum = OS_IsUseMutex(OSMutexNum);
        if (!MutexTaskNum)                                //没有任务有互斥信号  
        {
                ExitCritical();                                //开启中断
                return ;
        }
        //还有任务有互斥信号
        MutexHighPriority = MutexTaskNum-1;                        //查询的时候为了判断,预先加1了.
                taskPointer = &TaskBlock[MutexHighPriority];                                                //获取最高优先级的TASK的指针
//                taskPointer->State =TaskStatusReady;     //清除互斥标志,设为就绪状态
                taskPointer->Delay = 0;                                
//                taskPointer->EventPointer = (Event *)0;
                taskPointer->MessagePointer = (void*)0;
                PriorityReadyTable |=  1 << MutexHighPriority;                          
                ExitCritical();
                Schedule();                                                                                                                                                 //任务切换
                return ;                                
}[/mw_shl_code]
回复 支持 反对

使用道具 举报

7

主题

64

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
3148
金钱
3148
注册时间
2013-12-8
在线时间
431 小时
 楼主| 发表于 2017-4-10 10:39:32 | 显示全部楼层
二、 G_Code代码解析:

1. G_Code文件的获取

我现在的G_Code是存储在SD卡上的,在GUI的FileList控件中通过触摸屏选择需要文件的路径,在FATFS中打开文件

[mw_shl_code=applescript,true]/********************************************
函数:FileLoad
功能:文件读取
参数:message:需要读取的文件信息
返回:无
********************************************/
void FileLoad(u32 message)
{
        u8* Path;
        GUI_FileListStruct *FL_T;
        u8 chgchar[2]={0X5C,0X00};//转义符 等效"\"
GCodeStruct GCode_One;
        FL_T = (GUI_FileListStruct *)((message&0xffffff)|RAM_ADDRESS); //获取文件名信息
//需增加文件类型判断

Path = mymalloc(sizeof(FL_T->path)+sizeof(FL_T->fname));
        strcpy((char*)Path,(const char*)FL_T->path); //拷贝path到pname里面
strcat((char*)Path,(const char*)chgchar); //添加转义符
strcat((char*)Path,(const char*)FL_T->fname); //添加新增的名字
f_res = f_open(filescr, (const TCHAR*)Path, FA_OPEN_DEFAULT); //打开文件
myfree(Path); //释放内存
if(f_res == FR_OK) //读取文件正常
{
DK.File_B_P->Open =1; //文件打开标志
DK.File_B_P->NeedRead = 1; //文件需要继续读取
DK.File_B_P->NotEnd = 1; //设置文件没有结束标志
FileRead = 0; //
        FileBuffNum = 0;
        Page_DK_File();
        ReadFile(); //读取数据到缓存中
DK.File_B_P->ReadStart = 1; //文件开始读取标志

while(DK.File_B_P->NotEnd)
        {
        if(ReadOneLine()==0)
        {
        TransformGCode(OneLine,&GCode_One);
        ShowRun(&GCode_One,Green);
        }
        }
        f_res = f_lseek(filescr, 0);
        FileRead = 0;
        FileBuffNum = 0;
        DK.File_B_P->Open =1; //文件打开标志
DK.File_B_P->NeedRead = 1; //需要读取文件
DK.File_B_P->NotEnd = 1;

        ReadFile(); //读取数据到缓存中
DK.File_B_P->ReadStart = 1; //文件开始读取标志
DK.Ctrl = CTRL_PAUSE; //停止
OS.Timer.Start(TimerMessageHandle,10,DK_File_Start);

        }
        else
        {
        DK.Ctrl = CTRL_STOP; //停止
}
}[/mw_shl_code]

在FileLoad中先进行一遍G代码的解析(TransformGCode(OneLine,&GCode_One);)和在TFT显示绿色的刀路(ShowRun(&GCode_One,Green); ),在开始雕刻前方便知道选择的CNC文件是否正确。

然后跳转到DK_File_Start中等待开始雕刻的命令。

[mw_shl_code=applescript,true]/********************************************
函数:DK_File_Start
功能:雕刻机脱机SD卡文件开始雕刻
参数:无
返回:无
********************************************/
void DK_File_Start(void)
{
// NRF_GCode_Struct *NRF_GCode_P;

DK.N = 0;
        while(DK.File_B_P->NotEnd)
        {
        if(DK.Ctrl == CTRL_STOP)        return; //停止
if(DK.Alarm) return; //有故障退出自动
if(NRF.Send == 0) //等待上次发送完成
{
if(DK.NRF.queue < 5) //从机的队列小于5个
{
if(!ReadOneLine()) //正常读到数据
{
if(!TransformGCode(OneLine,&G_Code)) //数据解析正常
{       
ShowRun(&G_Code,Red); //红色显示倒路
Send_GCode(&G_Code);
        }
        }
        else
        {
        //sd卡数据读取错误
}
        }
else
        {
        Send_DK_Action(NRF_CX_STATE); //查询DK状态;
        }
        }
else
        {
        OS.DelayTimeTick(5);
        }
        }
}[/mw_shl_code]

当触摸屏开始按钮按下后,开始正式的G代码解析和雕刻。

2. G_Code的解析

由于我自己的雕刻机最大的雕刻尺寸为15mm*15mm,将坐标位置放大100倍对能够满足雕刻机0.01mm的精度,所以没有使用浮点运算,直接通过对浮动数放大100倍来用整型运算,小于0.01的坐标直接放弃,这样所有运算为整型计算,提高运算速度。直线插补和圆弧插补使用逐点插补运算,没有使用复杂的浮点运算全部为整型计算。
坐标位置X 1.01 Y 2.02 对应的值为整型 X 101 Y 202 。
这样对移植可能有点问题,但对于我自己的机器精度完全足够

这个是G_Code的结构体,我只取我需要使用的坐标代码。

[mw_shl_code=applescript,true]typedef struct
{
        u8 Action;   //功能码
        u16 ValueWords; //位标志,对应X,Y等有数据坐标的对应位置1
        s32 X;
        s32 Y;
        s32 I;
        s32 J;
        s32 Z;
        u32 N;
}GCodeStruct;[/mw_shl_code]

TransformGCode是将line中的一条ASC码存储的G代码转换成自己需要的控制代码。

a) 流程是先判断是否为注释,如果是注释则直接返回。

b) 对控制码G和M全部遍历一遍,应为在G代码中会出现如G00 G90 G94 G71 G40 G54 G80这样的代码,对于设置的代码则直接处理掉。

c) 对坐标代码遍历一遍,并对数据的对应标志位置1(SetBit(Queue->ValueWords,word_bit); ),因为在简化的G代码控制中有时候坐标是缺省的,如G01 X62.65 Y57.71;G01 X59.44第二个G代码中Y坐标就省略了。这样在插补的时候知道那个坐标需要使用原来的而不是0.

当G代码转换完成后且没有出错就开始通过NRF发送到主机进行运行。(函数:Send_GCode(&G_Code);)



[mw_shl_code=applescript,true]u8 TransformGCode(u8 *line,GCodeStruct * Queue)//parser_state_t *G_Code)
{
        u8 char_counter = 1;
        u8 letter;
        s32 value_f;
        s32 value_i;
        u8 word_bit = 0; //跟踪变量的位值
DK.GC_Status = 0;
        mymemset(Queue,0,sizeof(GCodeStruct));
if (line[1] == ';'
        || line[1] == '('
        || line[1] == '%'
        )
        return GCSTATUS_OK; // comments
        DK.N++;
        // Pass 1: Commands
        while (next_statement(&letter, &value_f,&value_i, line, &char_counter))
        {
        switch (letter)
        {
        case 'N':
        break;
        case 'G':
        SetBit(Queue->ValueWords,WORD_GM);
        switch (value_i)
        {
        case 0: Queue->Action = GC_SEEK_G0; break; //快速移动
case 1: Queue->Action = GC_LINEAR_G1; break;        //直线移动
case 2: Queue->Action = GC_CW_ARC; break;
        case 3: Queue->Action = GC_CCW_ARC; break;
        case 4: Queue->Action = GC_DWELL_G4; break; //暂停
case 20: DK.State_B_P->Inches_Mode = true; break;
case 21: DK.State_B_P->Inches_Mode = false; break;
        case 28: Queue->Action = GC_GO_HOME_G28; break;//移动到原点
case 90: DK.State_B_P->Absolute_Mode = true; break;
        case 91: DK.State_B_P->Absolute_Mode = false;break;
        case 92: Queue->Action = GC_RESET_XYZ_G92; break;
        //G92:定义当前位置 例如:G92X10允许编程的绝对零点,通过重置当前位置为指定的值。这将设置机器的X坐标为10;若没有指定坐标的G92将重置所有轴为零。
case 30:
        case 64:
        case 40:
        case 17: // G17 蔓犷?疣犷麇?镫铖觐耱?X-Y
        case 94: // Feedrate per minute
        case 98: // Feedrate per minute (group type A)
        case 97: // Constant spindle speed M T Takes an S address integer, which is interpreted as rev/min (rpm). The default speed mode per system parameter if no mode is programmed.
        case 49: // Tool length offset compensation cancel
        case 80: // Cancel canned cycle
        break;
        default: FAIL(GCSTATUS_UNSUPPORTED_STATEMENT);
        }
        break;
        case 'M':
        SetBit(Queue->ValueWords,WORD_GM);
        switch (value_i)
        {
        case 112: // Emergency Stop
        case 0:
        case 1:
        case 2:
        case 30:
        case 60:
        Queue->Action = GC_STOP;
        break;
        case 3: Queue->Action = GC_M03; break;
        // case 4: G_Code->spindle_direction = -1; break;
        case 5: Queue->Action = GC_M05; break;
        case 23: // Thread gradual pullout ON
        case 24: // Thread gradual pullout OFF
        case 52: // Unload Last tool from spindle
        case 49: // Feedrate override NOT allowed
        case 48: // Feedrate override allowed
        case 8: // Coolant on
        case 9: // Coolant off
        case 105: // M105: Get Extruder Temperature Example: M105 Request the temperature of the current extruder and the build base in degrees Celsius. The temperatures are returned to the host computer. For example, the line sent to the host in response to this command looks like
        case 106: // M106: Fan On Example: M106 S127 Turn on the cooling fan at half speed. Optional parameter 'S' declares the PWM value (0-255)
        case 107: // Fan Off
        case 108: // M108: Set Extruder Speed Sets speed of extruder motor. (Deprecated in current firmware, see M113)
        case 110: // Set Current Line Number
        case 113: // Set Extruder PWM
        case 140: // Bed Temperature (Fast) Example: M140 S55 Set the temperature of the build bed to 55oC
        case 141: //Chamber Temperature (Fast) Example: M141 S30 Set the temperature of the chamber to 30oC
        case 142: // Holding Pressure Example: M142 S1 Set the holding pressure of the bed to 1 bar.
        case 6:
        return DK.GC_Status;
        default: FAIL(GCSTATUS_UNSUPPORTED_STATEMENT);
        }
        break;
        }
        if (DK.GC_Status)
        return (DK.GC_Status);
        }
        if (DK.GC_Status)
        return(DK.GC_Status);

        char_counter = 1;
        while (next_statement(&letter, &value_f, &value_i,line, &char_counter))
        {
        double unit_millimeters_value = to_millimeters(value_f);
        switch (letter)
        {
        case 'X':
        word_bit = WORD_X;
        if (DK.State_B_P->Absolute_Mode)
        Queue->X = unit_millimeters_value;
        else
        Queue->X += unit_millimeters_value;
        break;
        case 'Y':
        word_bit = WORD_Y;
        if (DK.State_B_P->Absolute_Mode)
        Queue->Y = unit_millimeters_value;
        else
        Queue->Y += unit_millimeters_value;
        break;
        case 'Z':
        word_bit = WORD_Z;
        if (DK.State_B_P->Absolute_Mode)
        Queue->Z = unit_millimeters_value;
        else
        Queue->Z += unit_millimeters_value;
        break;
        case 'I':
        word_bit = WORD_I;
        Queue->I = unit_millimeters_value;
        break;
        case 'J':
        word_bit = WORD_J;
        Queue->J = unit_millimeters_value;
        break;
        case 'K':
        break;
        case 'E':
        break;
        case 'F':
        break;
        case 'P':
        case 'S':
        break;
        case 'R':
        case 'G':
        case 'N':
        case 'M':
        break;
        default:
        FAIL(GCSTATUS_UNSUPPORTED_PARAM);
        }
        if(word_bit)
        {
        if (GetBit(Queue->ValueWords,word_bit)) { FAIL(FAIL_WORD_REPEATED); } // [Word repeated]
        SetBit(Queue->ValueWords,word_bit);
        }

        }

        return(DK.GC_Status);
}[/mw_shl_code]
回复 支持 反对

使用道具 举报

0

主题

6

帖子

0

精华

初级会员

Rank: 2

积分
193
金钱
193
注册时间
2015-4-8
在线时间
50 小时
发表于 2017-4-11 10:08:52 | 显示全部楼层
非常好,学习下楼主的步进电机速度处理
回复 支持 反对

使用道具 举报

70

主题

6670

帖子

0

精华

论坛大神

Rank: 7Rank: 7Rank: 7

积分
12178
金钱
12178
注册时间
2012-11-26
在线时间
3638 小时
发表于 2017-4-11 10:41:22 | 显示全部楼层
厉害
回复 支持 反对

使用道具 举报

7

主题

64

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
3148
金钱
3148
注册时间
2013-12-8
在线时间
431 小时
 楼主| 发表于 2017-4-11 13:42:07 | 显示全部楼层
三、NRF通讯

我的NRF通讯分主从机结构,控制端为ZET6主机,雕刻机C8T6为从机。主机发送命令后从机响应返回数据。
NRF通讯是由NRFTASK任务来完成的,通讯的命令为Buff中的第一个字来区分这组Buff是什么内容。
[mw_shl_code=applescript,true]/*****通讯控制码****/
#define End_D                                0x5A                        //结束码
#define NRF_CX                                                        0x10                                //通讯协议:查询
#define NRF_CX_STATE                                0x11                                //通讯协议:查询
#define NRF_CX_SYS                                        0x12        
#define NRF_KZ                                                        0x20                                //通讯协议:控制
#define NRF_KZ_GCODE                                0x21
#define NRF_KZ_DW                                                0x22
#define NRF_KZ_RESET                                0x23
#define NRF_KZ_MAINMOTO_ON        0x24
#define NRF_KZ_MAINMOTO_OFF        0x25
#define NRF_KZ_MOTO_ON                        0x26
#define NRF_KZ_MOTO_OFF                        0x27
#define NRF_KZ_GOTOZERO                        0x28
#define NRF_SZ                                                0x30                                //通讯协议:设置
#define NRF_SZ_SYS                                0x30        
#define NRF_REPEAT                        0x80                //数据重发标志[/mw_shl_code]
从机根据控制字来跳转到对应的控制中去。
NRF的通讯过程:
在NRF主机的NRFTASK接收到开始发送命令的时候,进入发送模式。
首先设置NRF的状态是OK的(NRF.Ctrl = NRF_M_OK;),然后进行数据发送,如果发送失败,则加重发标志后再次发送,直到正确发送。

[mw_shl_code=applescript,true]void NRF_M_CRTL(u8 *TX_Buf)                                 //M 主站控制
{
        NRF.Ctrl = NRF_M_OK;  //设置NRF的状态为 NRF_M_OK
        while(NRF.Send)                //NRF本次发送的是否完成,NRF.Send = 0 发送已经完成,NRF.Send = 1 发送还在进行中
        {
                if(NRF.Ctrl != NRF_M_OK)  //NRF发送发生错误,没有成功发送
         {//通讯错误
                        DK.ErrN ++;//错误计数加1
                        Tx_Buf[0] |= NRF_REPEAT;  //设置重发标志
                        OS.DelayTimeTick(10);  //延时10ms  
         }                        
                NRF_M_Send(TX_Buf);          //发送数据        
        }
}[/mw_shl_code]
[mw_shl_code=applescript,true]void NRF_M_Send(u8 *TX_Buf)
{
        NRF.Time_Out=0;                                        //清除通讯超时                                                                                 
        TX_Mode();//发送模式
        OS.DelayTimeTick(1);//等待1ms
        if(NRF24L01_TxPacket(TX_Buf)==TX_OK)                         //发送成功
        {
                RX_Mode();                                                                                                                  //设置接收模式                                
                while(NRF24L01_RxPacket(RX_BUF)!=0)                //等待接收的数据
                {
                        OS.DelayTimeTick(2); //延时2ms
                        NRF.Time_Out++;         //timeout计数               
                        if(NRF.Time_Out >=NRF_TIME_OUT) //timeout计数值大于设定值,说明从机没有发送数据,本次发送失败
                        {
                                NRF.Ctrl = NRF_ERR_TIMEOUT;        //超时等待次数超过了设置值  
                                NRF.Time_Out++; //
                                return;
                        }
                }
                NRFTask_RX(RX_BUF,Tx_Buf);                                                //数据解析处理                        
        }
        else                        //发送失败 等待下次重发
        {                                                                                          
                NRF.Ctrl = NRF_ERR_SEND;
                NRF.ERR_SEND++;
        }               
}[/mw_shl_code]
当接收到正确的数据后对数据进行解析,根据发送时的控制命令跳转到对应的解析函数中去
[mw_shl_code=applescript,true]
void NRFTask_RX(u8 *RX_Buf,u8 *TX_Buf)                //控制代码选择
{
                        switch (RX_Buf[0])
      {
              case NRF_CX_STATE:
              case NRF_KZ_GCODE:
              case NRF_KZ_DW:                                       
              case NRF_KZ_RESET:
              case NRF_KZ_MAINMOTO_ON:               
              case NRF_KZ_MOTO_ON:                                       
              case NRF_KZ_MOTO_OFF:        
              case NRF_KZ_MAINMOTO_OFF:               
              case NRF_KZ_GOTOZERO:                                                        
                                        Save_CX_Buf(RX_Buf);                        
                                        NRF.Ctrl=NRF_M_OK;
                                        NRF.Send = 0;
                      break;        
              case NRF_SZ_SYS:        
                                        Save_CX_Buf(RX_Buf);               
                                        NRF.Ctrl=NRF_M_OK;
                                        NRF.Send = 0;
                      break;
              case NRF_CX_SYS:                                
                                        Save_SYS_Buf(RX_Buf);
                                        NRF.Ctrl=NRF_M_OK;
                                        NRF.Send = 0;
                                        break;                                
                                case 0xFF:
                                                NRF.Ctrl = NRF_ERR_S;
                                                NRF.ERR_S++;
                                        break;                                                               
              default:
                                                NRF.Ctrl = NRF_ERR_FC;        
                                                NRF.ERR_FC++;
                      break;
      }

}[/mw_shl_code]
这样NRF主机的一次通讯就完成了。
NRF从机的话我现在没有使用中断进行判断,而是通过每2ms查询一次NRF24L01中是否有数据来判断是否接受到了数据。
当NRF从机接收到数据后需要对接收的数据判断是否为重发的数据,如果是重发的数据并且和上次接收的数据相同,那么需要把本次接收的数据丢弃,并返回雕刻机的运行状态数据。
由于需要判断与上次接收的数据是否相同,应此接收的BUFF采用了双队列,
[mw_shl_code=applescript,true]typedef struct
{
        u8 Buf[2][32];
        u8 New;               
        u8 Old;
}BUF_Struct;
#define RX_BUF_O Rx_Buf.Buf[Rx_Buf.Old]
#define RX_BUF_N Rx_Buf.Buf[Rx_Buf.New]
void Switch_Buf(void)
{
if(Rx_Buf.Old == 0)
{
  Rx_Buf.Old = 1;
  Rx_Buf.New = 0;
}
else
{
  Rx_Buf.Old = 0;
  Rx_Buf.New = 1;  
}
}
[/mw_shl_code]
通过下标的转换来切换当前使用那组BUFF

回复 支持 反对

使用道具 举报

2

主题

9

帖子

0

精华

初级会员

Rank: 2

积分
61
金钱
61
注册时间
2016-10-9
在线时间
8 小时
发表于 2017-4-11 23:26:57 | 显示全部楼层
厉害了我的哥
回复 支持 反对

使用道具 举报

20

主题

50

帖子

0

精华

高级会员

Rank: 4

积分
511
金钱
511
注册时间
2016-2-1
在线时间
49 小时
发表于 2017-4-14 21:01:14 | 显示全部楼层
楼主,能否共享原理图供大家学习学习?
回复 支持 反对

使用道具 举报

0

主题

125

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2728
金钱
2728
注册时间
2015-10-29
在线时间
428 小时
发表于 2017-4-15 11:20:58 | 显示全部楼层
厉害了牛哇
回复 支持 反对

使用道具 举报

32

主题

230

帖子

0

精华

高级会员

Rank: 4

积分
506
金钱
506
注册时间
2016-4-14
在线时间
113 小时
发表于 2017-4-16 16:03:22 | 显示全部楼层
题主能加个好友聊聊吗。雕刻机程序部分基本上我的也行了,但是现在我想加上这个GUI的部分。想跟您交流交流。谢谢了。QQ1454014228
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
18
金钱
18
注册时间
2016-11-23
在线时间
4 小时
发表于 2017-5-6 10:33:43 | 显示全部楼层
正点原子 发表于 2017-4-7 18:13
论坛急需楼主这样的DIY爱好者啊

原子哥你好,我在做一个写字机械手,功能类似于雕刻机,是根据你的源码改的,名字叫GRBL,基于stm32F4,是给数控机床用的。我用上位机GRBL controller与下位机通信时,打开端口会出现 No data from COM port after connect. Expecting Grbl version string,无法连接端口, 请问你们当时遇到过吗。
回复 支持 反对

使用道具 举报

14

主题

36

帖子

0

精华

初级会员

Rank: 2

积分
108
金钱
108
注册时间
2016-1-27
在线时间
21 小时
发表于 2017-5-6 10:46:07 | 显示全部楼层
膜拜膜拜
回复 支持 反对

使用道具 举报

7

主题

64

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
3148
金钱
3148
注册时间
2013-12-8
在线时间
431 小时
 楼主| 发表于 2017-5-6 21:33:46 | 显示全部楼层
jssf1020 发表于 2017-5-6 10:33
原子哥你好,我在做一个写字机械手,功能类似于雕刻机,是根据你的源码改的,名字叫GRBL,基于stm32F4, ...

grbl 与上位机有通讯协议的。开始的时候单片机需要发送GRBL的版本号给上位机建立通讯。其他的协议具体的我没有去深入分析。
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
18
金钱
18
注册时间
2016-11-23
在线时间
4 小时
发表于 2017-5-7 15:25:57 | 显示全部楼层
zgp0518 发表于 2017-5-6 21:33
grbl 与上位机有通讯协议的。开始的时候单片机需要发送GRBL的版本号给上位机建立通讯。其他的协议具体的 ...

我使用了串口,上位机是返回了版本号的,我搜索了,有人说这是maga2560连接时会出现,stm32的grbl是从2560移植过来的,应该是一样的什么原因,但是grbl controller的作者说这是软件bug,版本更新后也没解决,我也不懂了。
回复 支持 反对

使用道具 举报

0

主题

2

帖子

0

精华

新手入门

积分
10
金钱
10
注册时间
2017-5-10
在线时间
1 小时
发表于 2017-5-10 13:34:24 | 显示全部楼层
支持支持,动手能力很强
回复 支持 反对

使用道具 举报

2

主题

9

帖子

0

精华

初级会员

Rank: 2

积分
161
金钱
161
注册时间
2017-7-2
在线时间
56 小时
发表于 2017-7-3 23:42:34 来自手机 | 显示全部楼层
膜拜大神,问下大神,为什么我精英版下载了大神的程序,2.8的屏白屏无显示。
回复 支持 反对

使用道具 举报

12

主题

76

帖子

0

精华

高级会员

Rank: 4

积分
946
金钱
946
注册时间
2017-4-3
在线时间
116 小时
发表于 2017-7-4 09:08:57 | 显示全部楼层
膜拜大神Ing。。。。。。
回复 支持 反对

使用道具 举报

4

主题

44

帖子

0

精华

初级会员

Rank: 2

积分
185
金钱
185
注册时间
2013-8-11
在线时间
25 小时
发表于 2017-7-12 21:56:57 | 显示全部楼层
膜拜大神
回复 支持 反对

使用道具 举报

17

主题

330

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2525
金钱
2525
注册时间
2016-3-6
在线时间
391 小时
发表于 2017-7-14 08:09:05 | 显示全部楼层
厉害厉害
路漫漫其修远兮,吾将上下而求索。
回复 支持 反对

使用道具 举报

1

主题

13

帖子

0

精华

初级会员

Rank: 2

积分
90
金钱
90
注册时间
2017-6-15
在线时间
19 小时
发表于 2017-10-24 21:52:04 | 显示全部楼层
正点原子的开发板不支持
回复 支持 反对

使用道具 举报

4

主题

349

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1046
金钱
1046
注册时间
2017-5-19
在线时间
335 小时
发表于 2017-10-25 09:15:08 | 显示全部楼层
惊呆了,学习学习
回复 支持 反对

使用道具 举报

0

主题

1

帖子

0

精华

新手上路

积分
23
金钱
23
注册时间
2016-5-13
在线时间
2 小时
发表于 2017-11-9 00:17:53 | 显示全部楼层
做的是非常好,有时间学习仿制一下。
回复 支持 反对

使用道具 举报

64

主题

446

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1031
金钱
1031
注册时间
2017-7-26
在线时间
275 小时
发表于 2017-11-9 13:48:05 | 显示全部楼层
这个就有点强悍了。、、、、、、
回复 支持 反对

使用道具 举报

16

主题

190

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
1296
金钱
1296
注册时间
2013-8-27
在线时间
503 小时
发表于 2017-11-22 13:40:01 | 显示全部楼层
膜拜大神
回复 支持 反对

使用道具 举报

22

主题

333

帖子

0

精华

金牌会员

Rank: 6Rank: 6

积分
2224
金钱
2224
注册时间
2017-7-6
在线时间
271 小时
发表于 2017-11-27 15:09:23 | 显示全部楼层
看到此贴,楼主的理论和实践结合的能力令人瑟瑟发抖,不过你用MSOS有什么特别的考量吗
?感觉这个OS不是很主流
回复 支持 反对

使用道具 举报

7

主题

64

帖子

2

精华

论坛元老

Rank: 8Rank: 8

积分
3148
金钱
3148
注册时间
2013-12-8
在线时间
431 小时
 楼主| 发表于 2017-11-27 16:21:20 | 显示全部楼层
1547674987 发表于 2017-11-27 15:09
看到此贴,楼主的理论和实践结合的能力令人瑟瑟发抖,不过你用MSOS有什么特别的考量吗
?感觉这个OS不 ...

MSOS是在UCOS的基础上简化来的,比较简单但功能满足我的要求,能够完全看懂他的切换原理和机制。遇见程序BUG调试的时候能够从最基本的层面进行DEBUG跟踪。不像ucos宏定义太多,看懂整个UCOS很困难。程序出错跟踪有时候很抓瞎。有时候适合自己的就是最好的。毕竟我是业余玩单片机的。
回复 支持 反对

使用道具 举报

5

主题

26

帖子

0

精华

高级会员

Rank: 4

积分
777
金钱
777
注册时间
2015-3-14
在线时间
74 小时
发表于 2017-11-28 16:27:44 | 显示全部楼层
有看到大牛,膜拜+学习~
回复 支持 反对

使用道具 举报

4

主题

38

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
232
金钱
232
注册时间
2011-3-22
在线时间
59 小时
发表于 2017-11-29 11:48:47 | 显示全部楼层
大牛,膜拜+学习~
回复 支持 反对

使用道具 举报

19

主题

490

帖子

4

精华

论坛元老

Rank: 8Rank: 8

积分
5160
金钱
5160
注册时间
2016-7-21
在线时间
1145 小时
发表于 2018-1-14 22:39:28 | 显示全部楼层
摩拜大神
回复 支持 反对

使用道具 举报

1

主题

10

帖子

0

精华

初级会员

Rank: 2

积分
54
金钱
54
注册时间
2015-12-15
在线时间
5 小时
发表于 2018-1-28 19:32:46 | 显示全部楼层
大神啊,顶
回复 支持 反对

使用道具 举报

5

主题

66

帖子

0

精华

中级会员

Rank: 3Rank: 3

积分
232
金钱
232
注册时间
2016-10-20
在线时间
42 小时
发表于 2018-3-16 17:45:52 | 显示全部楼层
谢谢分享
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2024-5-15 14:32

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

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