起因

会拿这个冷门板子开发的起因源自于好友的一个请求,帮助他去英国留学的对象完成一个课程设计作业。虽然料到会有一定难度,但没想到居然是卡在如何抢救一个变砖的板子上😤,代码部分倒是一帆风顺。

项目相关资料

下面是英国老师提供的几份资料,可供大家查阅。

购置开发板及相关资源

FRDM-KL46Z 面向Kinetis MKL46Z256VLL4 KL3 MCU Freedom开发平台

在淘宝上找到了卖这个板子的商家,可以说国内基本没有这个板子的货源,仅有的几家这个板子也非常贵,居然要350大洋,太夸张了,难道是板子上的硬件资源有什么特别的嘛,当时的我并没有多想,毕竟不是我付板子的钱(bushi😂),这里是一个巨大的伏笔,折磨了我2天。

接下来自然是购置杜邦彩色排线和矩阵键盘模块啦,鉴于这种资源淘宝就非常多了,就不放链接了。

其次由于当时暂时还住在自己家里,大量工具都在学校,家里并没有准备焊枪、焊锡等常用工具,只能等好友去买点基础装备过来了。

冻手冻手,我都说了冻手啊!

发生甚么事了?

在解释原因之前请允许我先聊下我自己的故事,在当时我下载完驱动并第一次接上板子时,一切都那么的正常,正常的绿灯闪烁,正常的预烧录程序执行,唯一的问题就是连接不上Mbed Studio,我以为是连接不稳定?于是我进行了一次插拔,这下乐了,板子完全不亮灯,此时,我还没意识到问题的严重性。也许是板子的boot程序问题?(对了但不完全对)我到飞思卡尔官网查阅文档后下载了最新的boot程序,害,多半是国内这种板子货源少,厂家没更新bootloader,多大点事。结果这就是噩梦的开始,无论我按照操作手册安装多少次固件,板子永远在一次插拔后变砖块。由于国内几乎没有学校用这块板子教学,更没有丰富的社区资源,我一开始完全找不到相关问题解决方案。实话说,花了2个晚上寻求解决方案无果后,我都打算和好友说可能板子有问题打算放弃了。

直到第二天我再次尝试去国外论坛寻求答案时才找到一些眉目,这里有一篇遇到非常类似更新飞思卡尔板子固件的博客文章。顺着这个线索,我完整的下载了OpenSDA_Bootloader,P&E drivers,MSD Debug等一系列驱动(什么报菜名?😂),但由于资料年代久远,我按照要求尝试多次仍然未能抢救这块砖头。又是半天的国外全网搜索,终于明白,我的电脑windows版本过高了,想要安全更新固件,必须降级windows版本至win7,我真是chao了微软の🐎了。

需要windows7好说,装个虚拟机很快就完事了,果然在win7下安装以上固件完全没问题,等更新固件完成后再次连接win10平台的Mbed Studio就能正常识别了,DAPLink本身也没任何问题,很好,也就浪费了我2天半时间,nmmd🫠。

等待好友帮忙完成焊接

板子能正常连接后,我们一起焊接完键盘,杜邦线等外设。只能说我们两个的焊接技术纯纯依托,勉强能保证两块东西连在一起罢了,我严重怀疑之后的键盘大量出现抖动、失灵问题和我们粗暴的焊接技术有关😂。只能说我确实用不来松香+焊锡这玩意,要么太多变成一个球,要么太少焊不住,焊枪头tm的又贼粗,完全在和泥吧啊喂。

正式开始编程

这部分当然是全部交给我了(不然找你干嘛🥲),其实也没有花太多时间,第一版代码也就爆肝到凌晨4点而已,主要是我个人一旦进入心流状态,很难停的下来,根本睡不着。而且这个状态效率也高,说实话强行断了非常可惜。至于后续的第二版第三版代码也是每次做一些功能上的调整,自然就没那么辛苦了。

起步最关键的当然是定义外设资源:

//定义外设
DigitalOut greenLED(LED1);  // LED灯-绿
DigitalOut redLED(LED2);    // LED灯-红
SLCD        sLCD;           //SLCD屏幕
TSISensor   tsi;            //触摸板
InterruptIn button1(SW1);   //开关SW1
InterruptIn button3(SW3);   //开关SW2
//Keypad mykey(PTD3, PTA2, PTA1, PTC8, PTA5, PTA4, PTA12);  //老师设置的默认端口
Keypad mykey(PTD3, PTA1, PTA4, PTA2, PTC8, PTA5, PTA12);     //键盘端口设置
FlashIAP fd;

简单列下主函数逻辑,至于每个功能函数实现细节就不列出来了,太长了,基本只涉及简单的逻辑:

/** 主函数
    @note 这个键盘稀碎,记得输密码的时候一个一个键按,慢点来,不然可能出错
*/
int main()
{

    yellowLED = !yellowLED;
    redLED = !redLED;
    fd.init();
    flash_password_init();
    while (true)
    {
        char* password = (char *)calloc((MAX_PASSWORD_LENTH+1),sizeof(char)); //Allocate memory for the input password string and initialize all of it to 0

        if(button1.read()==1) //When switch 1 is not pressed, open the door procedure
        {
            password=get_password_hardware(password);
            check_password_hardware(password);
        }
        else //Press switch 1 to enter password change mode, and the yellow light is on
        {
            flip_yellowled();
            password=get_password_hardware(password);
            change_password_hardware(password);
            flip_yellowled();
        }
        free(password); //Free memory
        thread_sleep_for(WAIT_TIME_MS);
    }
}

这里为了程序结构更加美观(其实是他们老师要求),写了个自己的辅助功能库,myutils.cpp:

/** 显示滚动信息
    @param message 输入想要显示的信息文本,最长60个字符
    @param scrool_speed 单位ms,即每多少ms滚动一个字符
    @param sLCD 外设对象,取对象地址
    @note  最多显示60个字符
*/
void scroll_message(char message[60],int scroll_speed,SLCD &sLCD);

/** LED闪烁
    @param led led外设
    @todo 暂时不能用!
*/
void LEDflip(DigitalOut &led);

/** 等待密码期间让屏幕闪烁
    @param sLCD 外设对象
    @note 以 0.0.0.0 闪烁
*/
void waiting_password(bool mode,SLCD &sLCD);

/** 比对两个字符串
    @param str1 字符串1指针
    @param str2 字符串2指针
    @return 返回两个字符串大小区别,0表示字符串相同,1表示str1长于str2
    @note 不要输入空字符串
*/
int mystrcmp(const char *str1,const char *str2);

/** 复制字符串
    @param strDest 目标字符串数组指针
    @param strSrc 复制原字符串数组指针
    @return 返回指向目标字符串数组的指针
    @note 不要输入空字符串,返回的是指针
*/
char* mystrcpy(char *strDest, const char* strSrc);

这里面的函数其实都非常简单,唯一值得说道的就是如何在SLCD上显示滚动消息,再书写前需要做一下简单的计算:

void scroll_message(char message[60],int scroll_speed,SLCD &sLCD)
{
    for (int start = 0; start < strlen(message)-3; start++) //-4 -> -3
    {
        for (int digit = 0; digit < 4; digit++)
            sLCD.putc(message[start + digit]);
        thread_sleep_for(scroll_speed);
    }
}

main函数中也有一个函数实现时候需要仔细思考下,即如何在只能显示4位数字的SLCD上用户能够录入超过4位数字的密码?当屏幕上已经显示4位数字后,每输入一个数字要让前面的数字往左边推进一位,才能让用户正常看到自己键入的新数字。依旧在书写前可能需要实现做些计算,想清楚边界是多少:

/** get password
    @param password input password(pionter)
    @return 返回char*指针,指向用户输入的密码
    @note  Press the # key to finish typing
*/
char* get_password_hardware(char* password)
{
    char key;
    char POUND[] = "#";
    int count=0;
    bool wait=true;
    waiting_password(wait,sLCD);
    while (count<MAX_PASSWORD_LENTH)
    {
        if (mykey.KeyReady()) 
        {
            if(wait)
            {
                wait=false;
                waiting_password(wait,sLCD);
            }
            key = mykey.ReadKey();
            if (key == POUND[0])
            {
                if(count<4)
                {
                    sLCD.Home();
                    return 0;
                }
                else
                {
                    sLCD.Home();
                    return password;
                }
            }
            else
            {
                *(password+count)=key;
                if(count<4)
                {
                    sLCD.putc(key);
                }
                else    //Ensure that the last four digits are input
                {
                    for (int start = 0; start < (count + 1) - 4; start++)
                    {
                        for (int digit = 0; digit < 4; digit++)
                            sLCD.putc(*(password+start + digit + 1));   
                    } 
                }
                count++;
            }
        }
    }
    sLCD.Home();
    return 0;
}

大部分需要自己敲得代码已经放出来了,其他需要引用的外设库文件已经放在文章开头就不赘述了。

题外话

写完代码实现完功能,当然就是交给我好友对象让她理解再做些修改啦,后续主要也是卡在如何录入密码的逻辑那,我还专门画图讲了下如何算出的边界。。。很可惜没有让她理解🫠。不过倒也不重要,据我所知,咸鱼上做这种毕设项目能有3k的收入,好友惨痛被骗3k后时间来不及了才来找到我,结果几天就给他做完了😂。当然好友送了一大袋薯片和一大箱果汁,非常NICE!(吃到上火嘴巴起泡)苹果汁真的好好喝啊!!!🤤

不过实话说,再看自己的代码还是纯纯三脚猫,当时第一版Debug就耗了大量时间才搞到凌晨,只能说还需努力!