emWin(ucGUI)在PC机上模拟的按键响应多次解决办法

起因

菜单程序在 emWin(ucGUI) 的 PC 模拟器上调试时出现怪现象:按一次 ENTER,菜单连跳两级——本该从一级进到二级,结果直接落在三级菜单。

需求

  • 按一次按键只响应一次,一级一级进入菜单
  • “响应 ENTER 键消息后切换菜单”的既有架构不动,只在按键判定上找原因

技术实现原理

根因:模拟器“按下、松开”各发一条消息

emWin(ucgui) 在PC端的模拟器,默认的按键机制是"按抬都发送Msg",当在按下键盘时,会收到一个key值-1,在按键没有离开时一直维持,当按键松开时还发送一个key值-0的标记。所以在你手速多快的情况下都会有一个 key,1和key,0两个操作。程序中没有对按键的状态做判定,所以在PC上不管是按下,还是按下后离开都会进行响应(两次响应)。

STM32读取旋钮编码器

起因

设备上要用旋钮做交互输入(调参数、选菜单):能左旋、右旋还能按,一个旋钮顶三个按键。旋钮里面是增量式编码器,读它的核心问题只有一个——怎么判方向。这篇把软件、硬件两种解法都记下来。

Modbus TCP和Modbus Rtu协议的区别

起因

工控和嵌入式项目里选通信协议,十有八九绕不开 Modbus:下位机一边可能是 RS485 总线上的仪表/变频器/PLC,另一边可能是走以太网的运动控制卡或上位机软件,同一个项目里 RTU 和 TCP 混着用是常态。要正确选型、写对接代码,就得先把两者的关系和差别讲清楚。

自定义的小型调试器,类似于printf(基于emwin),通过屏幕显示

自定义的小型调试器,类似于printf(基于emwin),通过屏幕显示

可供外部使用的函数有

1
2
3
Hide_Debug()
Show_Debug()
GUI_Printf(const char*,)

程序代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
#include <stdarg.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include "GUI.h"
#include "WM.h"
//需要中文字库的支持才可以打印中文信息
//仅需要在需要调试信息的部分调用GUI_Printf(const char* fmt,...)进行显示
//与printf的用法一致
//通过调用Show_debug()创建调试窗口
//通过调用Hide_debug()删除窗口
#define MAX_BUF         1024*4
#define ID_FRAMEWIN_0 (GUI_ID_USER+0)
#define ID_MULTIEDIT_0 (GUI_ID_USER+1)
static WM_HWIN error_hwin;
static WM_HTIMER hTimer;
static char debug_infomation[MAX_BUF];
struct __Debug_Ctrl
{
    const uint32_t DEBUG_MAX_BUF;
    char* const Debug_Buffer;
    bool fresh_flag;
}Debug_Ctrl;
struct __Debug_Ctrl Debug_Ctrl=
{
    .DEBUG_MAX_BUF=MAX_BUF,
    .Debug_Buffer=debug_infomation,
    .fresh_flag=false
};
static const GUI_WIDGET_CREATE_INFO _aDialogCreate[] = {
  { FRAMEWIN_CreateIndirect, "Framewin", ID_FRAMEWIN_0, 0, 300, 300, 300, 0, 0x0, 0 },
  { MULTIEDIT_CreateIndirect, "Multipage", ID_MULTIEDIT_0, 0, 0, 290, 280, 0, 0x0, 0 },
  // USER START (Optionally insert additional widgets)
  // USER END
};
static void _cbDialog(WM_MESSAGE * pMsg) 
{
    WM_HWIN hItem;
    switch (pMsg->MsgId) 
    {
        case WM_PRE_PAINT:
            GUI_MULTIBUF_Begin();
            break;
        case WM_POST_PAINT:
            GUI_MULTIBUF_End();
            break;
        case WM_INIT_DIALOG:
            //
            // Initialization of 'Framewin'
            //
            hItem = pMsg->hWin;
            FRAMEWIN_SetFont(hItem, &GUI_FontHZ16);
            FRAMEWIN_SetText(hItem,"调试信息");
            //
            // Initialization of 'Multipage'
            //
            hItem = WM_GetDialogItem(pMsg->hWin, ID_MULTIEDIT_0);
            MULTIEDIT_SetFont(hItem, &GUI_FontHZ16);
            MULTIEDIT_SetAutoScrollV(hItem,1);
            MULTIEDIT_SetAutoScrollH(hItem,1);
            MULTIEDIT_SetMaxNumChars(hItem,Debug_Ctrl.DEBUG_MAX_BUF);
            MULTIEDIT_SetBufferSize(hItem,Debug_Ctrl.DEBUG_MAX_BUF);
            MULTIEDIT_SetText(hItem,Debug_Ctrl.Debug_Buffer);
            MULTIEDIT_SetFocussable(hItem,0);
            break;
        case WM_NOTIFY_PARENT:
            break;
        case WM_TIMER:
            if(Debug_Ctrl.fresh_flag==true)
            {
                hItem = WM_GetDialogItem(pMsg->hWin, ID_MULTIEDIT_0);
                MULTIEDIT_SetText(hItem,Debug_Ctrl.Debug_Buffer);
                Debug_Ctrl.fresh_flag=false;
            }
            WM_RestartTimer(hTimer,200);
            break;
        default:
            WM_DefaultProc(pMsg);
            break;
    }
}
void Hide_debug(void)
{
    WM_DeleteWindow(error_hwin);
}
void Show_debug(void)
{
    WM_HWIN hwin;
    error_hwin=GUI_CreateDialogBox(_aDialogCreate, GUI_COUNTOF(_aDialogCreate), _cbDialog, WM_HBKWIN, 0, 0);

    WM_SetStayOnTop(error_hwin,1);
    hwin=WM_GetClientWindow(error_hwin);
    hTimer=WM_CreateTimer(hwin,0,200,0);
}
void GUI_Printf(const char* fmt,...)
{
    static uint16_t Debug_num=0;
    va_list args;
    if(Debug_Ctrl.DEBUG_MAX_BUF-Debug_num<64)//预留出64字节的缓冲区,因此单次使用GUI_Printf不能超过64个字节
    {
        memset(Debug_Ctrl.Debug_Buffer,'\0',Debug_Ctrl.DEBUG_MAX_BUF);
        Debug_num=0;
    }
    va_start(args,fmt);                                 //取出可变参数
    Debug_num+=vsprintf(&Debug_Ctrl.Debug_Buffer[Debug_num],fmt,args);
    va_end(args);                                       //恢复堆栈的数据
    Debug_Ctrl.Debug_Buffer[Debug_num]='\n';          //向末尾添加换行符
    Debug_num++;
    Debug_Ctrl.fresh_flag=true;
}

CC65 开发FC/NES 游戏(一)环境搭建

红白机(又叫FC,美国叫NES)
开发语言,早期的码农们用的是6502的CPU上的汇编语言ASM
cc65 是 65(C)02 系统的完整交叉开发包,包括功能强大的宏汇编程序、C 编译器、链接器、库管理员和其他几个工具。