[MM32生态] 基于MM32的手持终端设备(国产芯片替代方案)

2023-10-30 16:50

本文主要是介绍[MM32生态] 基于MM32的手持终端设备(国产芯片替代方案),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

芯片在我们生活中无处不在,常用的手机、汽车、家用电器、智能家居、消费电子等等都离不开芯片,以及上班途中骑行的共享单车,也都有芯片身影的存在。在当前市场大环境下,尤其是之前使用国外芯片(ST、NXP等)方案设计的项目产品,缺芯的现象尤为严重,对于量小的客户而言,高价芯片、一芯难求的现象比比皆是;而对于国产芯片的发力,芯片国产化、推动国产芯片完成实际项目落地的需求,也成了当前国内设计项目之初作为芯片选型的主要依据;而对于已经完成的项目,在缺芯的因素下使用国产芯片替换原有芯片的需求量也变得日益增多;国产芯片不但解决了芯片短缺的问题,同时在价格成本上具有很大的优势,随着国产芯片技术逐步趋向稳定、成熟,并且在以飞快的速度创新着,在做好项目前期功能验证的情况下,国产芯片完全可以做为选型首选品牌!

本文主要是分享使用国产芯片MM32F3273G8P去替换STM32F103VET6在手持式终端设备上的产品,主要是依据产品已经设计好的硬件原理图,去比较这两款单片机电气特性和引脚功能上的兼容性;对于软件代码来说,只需要上层的应用控制逻辑不需要改动,底层驱动根据芯片选型做相应的更新就可以了。当然也有遇到过说是可以做到跟ST做到芯片PIN TO PIN,软件不需要改动的国产芯片,但使用ST的软件代码库运行在非ST的芯片上,终究不是原配,出了问题是芯片的问题,还是软件库的问题,也无法确认;做项目还是稳妥些好,不要随便的拿来主义,要经过充分验证和测试,才能保证产品的稳定性不是吗?

因为是实际的产品项目,不可能直接分享实际的功能实现,但会结合硬件设计来做一个相近的软件功能出来展示,来体现这两款芯片的硬件和软件的兼容性。这款手持终端设备在硬件设计上使用STM32F103VET6作为主控芯片、锂电池供电,并带有充电管理电路、具有一键开关机和一键复位功能、带有2.8寸的TFT彩色LCD液晶显示屏,并伴有背光控制电路、带有USB接口电路,一方面给整机充电,另一方面可以与PC通过USB接口进行数据通讯、带有外置的RTC芯片,实现精确时间管理功能、支持2片SPI FLASH,可提供大容量的离线数据存储和管理、通过TM1668芯片驱动实现一个外置的4*5按键矩阵检测功能、带有1路蜂鸣器,供操作时当作提示音、此外还有FM17522实现的非接触式IC卡控制电路和FM11NC08的NFC TAG电路,以及LTK4201移动支付加密芯片电路等。


 

芯片规格

MM32F3273G8P芯片与STM32F103VET6芯片相比,在SRAM大小、UART数量、GPIO口个数、以及CPU频率和工作电压范围上有明显的优势,但在ADC单元数上有些欠缺;当然这只是规格比较的结果,还需要结合实际的硬件设计电路来看;就比如ADC欠缺的部分,在这个手持终端设备上就只用了2路,完全满足硬件需求,芯片就可以直接替换使用了。

芯片引脚定义

MM32F3272G8P和STM32F103VET6都是LQFP100的封装结合,在引脚定义上是PIN TO PIN兼容的,在引脚功能上也是相匹配的,具体可以参考这两款芯片数据手册的相关章节;如下图所示:

基于MM32F3273G8P的软件功能实现

基于手持设备终端,通过按键、显示、非接触式IC卡的读写操作,模拟完成客户在消费时的消费、充值、查询、设置等功能,结合硬件设计,驱动部分的功能如下所示:

  • 通过FSMC接口驱动显示LCD显示,通过GPIOLCD背光进行控制;
  • 通过硬件I2C接口驱动PCF8563 RTC芯片,进行精确时钟管理,并在LCD液晶屏上显示当前的日期和时间信息;
  • 通过GPIO口模拟TM1668的控制时序,实现对4*5按键矩阵的检测和识别,对检测到的按键在不同状态任务下做相应的功能处理;
  • 通过硬件SPI接口驱动2SPI FLASH,实现离线数据存储和管理的功能,其中SPI FLASH1部分空间用作中文汉字字库点阵数据的存储空间使用;
  • 通过ADC检测当前锂电池电量和供电电压;
  • 通过TIM定时器的PWM功能,实现对蜂鸣器的驱动,完成操作提示音的功能;
  • 通过硬件SPI接口驱动FM17522芯片驱动,实现对非接触式IC卡的读写操作;
  • 通过Xmodem串行文件传输协议实现中文汉字字库点阵数据烧录到SPI FLASH的功能;

软件分层/模块设计

整体软件分为MM32库、SHELL调试、SYS系统调度、NFC层、HAL层、BSP层、以及APP层;其中MM32库是灵动官方提供的库程序;SHELL调试是基于UART实现的,用于项目设计过程中的调试等功能分析;SYS系统调度包含了消息队列及TASK任务的注册、轮询和调用;NFC层实现了FM11NC08和FM17550的驱动及应用功能;HAL层实现了FSM有限状态机的控制、HMI的界面显示、KEY的按键处理、NVM的存储控制、以及RTC的应用;BSP层则是基于硬件原理图设计,实现了基于底层硬件的驱动程序,如ADC、BEEP、LCD、LKT4201、PCF8563、TIM1668、W25Q64等等;APP层是整体框架和Xmodem功能;具体的实现和代码可以参加附件中的软件工程源代码。

部分程序源码

LCDFSMC初始化及配置

void LCD_Init(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    FSMC_InitTypeDef FSMC_InitStructure;

    FSMC_NORSRAM_Bank_InitTypeDef FSMC_BankInitStructure;

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOC, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_8 | GPIO_Pin_9;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

    GPIO_Init(GPIOC, &GPIO_InitStructure);

    LCD_BL_L(); /* Turn OFF LCD Backlight */

    RCC_AHBPeriphClockCmd( RCC_AHBENR_GPIOD,   ENABLE);

    RCC_AHBPeriphClockCmd( RCC_AHBENR_GPIOE,   ENABLE);

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_SYSCFG, ENABLE);

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource4,  GPIO_AF_12);  /* FSMC_NOE  */

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource5,  GPIO_AF_12);  /* FSMC_NWE  */

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource7,  GPIO_AF_12);  /* FSMC_NE1  */

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource11, GPIO_AF_12);  /* FSMC_A16  */

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource14, GPIO_AF_12);  /* FSMC_DA0  */

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource15, GPIO_AF_12);  /* FSMC_DA1  */

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource0,  GPIO_AF_12);  /* FSMC_DA2  */

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource1,  GPIO_AF_12);  /* FSMC_DA3  */

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource7,  GPIO_AF_12);  /* FSMC_DA4  */

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource8,  GPIO_AF_12);  /* FSMC_DA5  */

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource9,  GPIO_AF_12);  /* FSMC_DA6  */

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource10, GPIO_AF_12);  /* FSMC_DA7  */

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource11, GPIO_AF_12);  /* FSMC_DA8  */

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource12, GPIO_AF_12);  /* FSMC_DA9  */

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource13, GPIO_AF_12);  /* FSMC_DA10 */

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource14, GPIO_AF_12);  /* FSMC_DA11 */

    GPIO_PinAFConfig(GPIOE, GPIO_PinSource15, GPIO_AF_12);  /* FSMC_DA12 */

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource8,  GPIO_AF_12);  /* FSMC_DA13 */

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource9,  GPIO_AF_12);  /* FSMC_DA14 */

    GPIO_PinAFConfig(GPIOD, GPIO_PinSource10, GPIO_AF_12);  /* FSMC_DA15 */

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_4 | GPIO_Pin_5  | GPIO_Pin_7  |

                                    GPIO_Pin_11;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOD, &GPIO_InitStructure);

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_0 | GPIO_Pin_1  | GPIO_Pin_8  |

                                    GPIO_Pin_9 | GPIO_Pin_10 | GPIO_Pin_14 |

                                    GPIO_Pin_15;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOD, &GPIO_InitStructure);

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_7  | GPIO_Pin_8  | GPIO_Pin_9  |

                                    GPIO_Pin_10 | GPIO_Pin_11 | GPIO_Pin_12 |

                                    GPIO_Pin_13 | GPIO_Pin_14 | GPIO_Pin_15;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOE, &GPIO_InitStructure);

    RCC_AHB3PeriphClockCmd(RCC_AHB3ENR_FSMC, ENABLE);

    FSMC_NORSRAM_BankStructInit(&FSMC_BankInitStructure);

    FSMC_BankInitStructure.FSMC_SMReadPipe    = 0;

    FSMC_BankInitStructure.FSMC_ReadyMode     = 0;

    FSMC_BankInitStructure.FSMC_WritePeriod   = 0x2;

    FSMC_BankInitStructure.FSMC_WriteHoldTime = 2;

    FSMC_BankInitStructure.FSMC_AddrSetTime   = 3;

    FSMC_BankInitStructure.FSMC_ReadPeriod    = 0x1;

    FSMC_BankInitStructure.FSMC_DataWidth     = FSMC_DataWidth_16bits;

    FSMC_NORSRAM_Bank_Init(&FSMC_BankInitStructure, FSMC_NORSRAM_BANK0);

    FSMC_NORSRAMStructInit(&FSMC_InitStructure);

    FSMC_InitStructure.FSMC_Mode            = FSMC_Mode_8080;

    FSMC_InitStructure.FSMC_TimingRegSelect = FSMC_TimingRegSelect_0;

    FSMC_InitStructure.FSMC_MemSize         = FSMC_MemSize_64MB;

    FSMC_InitStructure.FSMC_MemType         = FSMC_MemType_NorSRAM;

    FSMC_InitStructure.FSMC_AddrDataMode    = FSMC_AddrDataMUX;

    FSMC_NORSRAMInit(&FSMC_InitStructure);

    LCD_RST_H(); SysTick_DelayMS(100);

    LCD_RST_L(); SysTick_DelayMS(100);

    LCD_RST_H(); SysTick_DelayMS(200);

    LCD_REG_Configure();

}

PCF8563 RTC芯片驱动程序

void PCF8563_Read(uint8_t Address, uint8_t *Buffer, uint8_t Length)

{

    uint8_t i = 0, Flag = 0, Count = 0;

    I2C_SendData(I2C1, Address);

    while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));

    for(i = 0; i < Length; i++)

    {

        while(1)

        {

            if((I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFNF)) && (Flag == 0))

            {

                I2C_ReadCmd(I2C1);   Count++;

                if(Count == Length) Flag = 1;

            }

            if(I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_RFNE))

            {

                Buffer[i] = I2C_ReceiveData(I2C1);     break;

            }

        }

    }

    I2C_GenerateSTOP(I2C1, ENABLE);

    while(!I2C_GetFlagStatus(I2C1, I2C_FLAG_STOP_DET));

}

void PCF8563_Write(uint8_t Address, uint8_t *Buffer, uint8_t Length)

{

    uint8_t i = 0;

    I2C_SendData(I2C1, Address);

    while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));

    for(i = 0; i < Length; i++)

    {

        I2C_SendData(I2C1, Buffer[i]);

        while(!I2C_GetFlagStatus(I2C1, I2C_STATUS_FLAG_TFE));

    }

    I2C_GenerateSTOP(I2C1, ENABLE);

    while(!I2C_GetFlagStatus(I2C1, I2C_FLAG_STOP_DET));

}

void PCF8563_Init(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    I2C_InitTypeDef  I2C_InitStructure;

    uint8_t wBuffer[7] = {0x00, 0x56, 0x12, 0x29, 0x03, 0x12, 0x21};

    uint8_t rBuffer[7] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

    RCC_APB1PeriphClockCmd(RCC_APB1ENR_I2C1, ENABLE);

    I2C_StructInit(&I2C_InitStructure);

    I2C_InitStructure.I2C_Mode       = I2C_Mode_MASTER;

    I2C_InitStructure.I2C_OwnAddress = 0;

    I2C_InitStructure.I2C_Speed      = I2C_Speed_STANDARD;

    I2C_InitStructure.I2C_ClockSpeed = 100000;

    I2C_Init(I2C1, &I2C_InitStructure);

    I2C_Send7bitAddress(I2C1, PCF8563_I2C_ADDRESS, I2C_Direction_Transmitter);

    I2C_Cmd(I2C1, ENABLE);

    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOB, ENABLE);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource8, GPIO_AF_4);

    GPIO_PinAFConfig(GPIOB, GPIO_PinSource9, GPIO_AF_4);

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_8 | GPIO_Pin_9;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_OD;

    GPIO_Init(GPIOB, &GPIO_InitStructure);

    PCF8563_Read(0x02, rBuffer, sizeof(rBuffer));

    if(rBuffer[6] != 0x21)

    {

        printf("\r\nRTC Factory Reset!!!\r\n");

        PCF8563_Write(0x02, wBuffer, sizeof(wBuffer));

    }

}

TM1668按键识别驱动程序

uint8_t TM1668_RD(void)

{

    uint8_t Data = 0;

    TM1668_InitDIO(0);  TM1668_DIO_L();

    TM1668_InitDIO(1);

    TM1668_CLK_L();     TM1688_Delay();

    for(uint8_t i = 0; i < 8; i++)

    {

        Data >>=  1;

        TM1668_CLK_H(); TM1688_Delay();

        if(GPIO_ReadInputDataBit(TM1668_DIO_GPIO, TM1668_DIO_PIN) == Bit_SET)

        {

            Data |= 0x80;

        }

        TM1668_CLK_L(); TM1688_Delay();

    }

    return Data;

}

void TM1668_WR(uint8_t Data)

{

    TM1668_InitDIO(0);  TM1668_DIO_L();

    for(uint8_t i = 0; i < 8; i++)

    {

        TM1668_CLK_L(); TM1688_Delay();

        if(Data & (0x01 << i)) TM1668_DIO_H();

        else                   TM1668_DIO_L();

        TM1668_CLK_H(); TM1688_Delay();

    }

}

void TM1668_Init(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    RCC_AHBPeriphClockCmd(TM1668_DIO_RCC, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = TM1668_DIO_PIN;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

    GPIO_Init(TM1668_DIO_GPIO, &GPIO_InitStructure);

    RCC_AHBPeriphClockCmd(TM1668_CLK_RCC, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = TM1668_CLK_PIN;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

    GPIO_Init(TM1668_CLK_GPIO, &GPIO_InitStructure);

    RCC_AHBPeriphClockCmd(TM1668_STB_RCC, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = TM1668_STB_PIN;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_Out_PP;

    GPIO_Init(TM1668_STB_GPIO, &GPIO_InitStructure);

    TM1668_STB_H();

    TM1668_CLK_H();

    TM1668_DIO_L();

}

void TM1668_ReadInput(uint16_t *Buffer)

{

    TM1668_STB_L();

    TM1668_WR(0x42); TM1688_Delay();

    for(uint16_t i = 0; i < 5; i++)

    {

        Buffer[i] = TM1668_RD() + (0x0100 * i);

    }

    TM1668_STB_H();

}

FM17522非接触式IC卡操作

uint8_t FM17550_TypeA_Handler(void)

{

    FM17550_SoftwareReset();

    ReaderA_Init();

    FM17550_SetCW(3);

    if(ReaderA_CardActivate() != FM175XX_SUCCESS)

    {

        FM17550_SetCW(0);       return 0;

    }

    printf("\r\n-> ATQA = ");

    for(uint8_t i = 0; i < 2; i++)

    {

        printf("0x%02x ", PICC_A.ATQA[i]);

    }

    printf("\r\n-> UID  = ");

    for(uint8_t i = 0; i < 4; i++)

    {

        printf("0x%02x ", PICC_A.UID[i]);

    }

    printf("\r\n-> SAK  = 0x%02x", PICC_A.SAK);

    if(Mifare_Auth(KEY_A_AUTH, 0, KEY_A[0], PICC_A.UID) != FM175XX_SUCCESS)

    {

        printf("\r\n-> AUTH ERROR!\r\n");               

        FM17550_SetCW(0);            return 0;

    }

    printf("\r\n-> AUTH SUCCESS!");        

#if 0

    memcpy(BLOCK_VAULE,"\x00\x00\x00\x08",4);

               

    if(Mifare_SetBlock(1, BLOCK_VAULE) != FM175XX_SUCCESS)

    {

        printf("-> SET BLOCK VALUE ERROR!\r\n");               

        FM17550_SetCW(0);       return 0;

    }

    printf("-> SET BLOCK VALUE SUCCESS!\r\n");

#endif

    if(Mifare_ReadBlock(1, BLOCK_DATA) != FM175XX_SUCCESS)

    {

        printf("\r\n-> READ BLOCK ERROR!\r\n");               

        FM17550_SetCW(0);             return 0;

    }

    printf("\r\n-> READ BLOCK = ");

    for(uint8_t i = 0; i < 16; i++)

    {

        printf("0x%02x ", BLOCK_DATA[i]);

    }

    RemainingAmount  = BLOCK_DATA[0]; RemainingAmount *= 256;

    RemainingAmount += BLOCK_DATA[1]; RemainingAmount *= 256;

    RemainingAmount += BLOCK_DATA[2]; RemainingAmount *= 256;

    RemainingAmount += BLOCK_DATA[3];

    printf("\r\nRemainingAmount : %d", RemainingAmount);

    switch(AmountOperation)

    {

        case 1: /* 消费 */

            printf("\r\nConsumptionAmount : %d", ConsumptionAmount);

            if(RemainingAmount >= ConsumptionAmount)

            {

                BLOCK_VAULE[3] = (uint8_t)(((RemainingAmount - ConsumptionAmount) >> 0x00) & 0xFF);

                BLOCK_VAULE[2] = (uint8_t)(((RemainingAmount - ConsumptionAmount) >> 0x08) & 0xFF);

                BLOCK_VAULE[1] = (uint8_t)(((RemainingAmount - ConsumptionAmount) >> 0x10) & 0xFF);

                BLOCK_VAULE[0] = (uint8_t)(((RemainingAmount - ConsumptionAmount) >> 0x18) & 0xFF);

                if(Mifare_SetBlock(1, BLOCK_VAULE) != FM175XX_SUCCESS)

                {

                    printf("\r\n-> SET BLOCK VALUE ERROR!\r\n");               

                    FM17550_SetCW(0);                  return 0;

                }

                printf("\r\n-> SET BLOCK VALUE SUCCESS!");

            }

            else

            {

                printf("-> Amount Not Enough!\r\n");               

                FM17550_SetCW(0);          return 2;

            }

            break;

        case 2: /* 充值 */

            printf("\r\nRechargeAmount : %d", RechargeAmount);

            BLOCK_VAULE[3] = (uint8_t)(((RemainingAmount + RechargeAmount) >> 0x00) & 0xFF);

            BLOCK_VAULE[2] = (uint8_t)(((RemainingAmount + RechargeAmount) >> 0x08) & 0xFF);

            BLOCK_VAULE[1] = (uint8_t)(((RemainingAmount + RechargeAmount) >> 0x10) & 0xFF);

            BLOCK_VAULE[0] = (uint8_t)(((RemainingAmount + RechargeAmount) >> 0x18) & 0xFF);

            if(Mifare_SetBlock(1, BLOCK_VAULE) != FM175XX_SUCCESS)

            {

                printf("\r\n-> SET BLOCK VALUE ERROR!\r\n");               

                FM17550_SetCW(0);                  return 0;

            }

            printf("\r\n-> SET BLOCK VALUE SUCCESS!");

            break;

        case 3: /* 查询 */

            break;

        case 4: /* 清空 */

            memset(BLOCK_VAULE, 0, sizeof(BLOCK_VAULE));

            if(Mifare_SetBlock(1, BLOCK_VAULE) != FM175XX_SUCCESS)

            {

                printf("\r\n-> SET BLOCK VALUE ERROR!\r\n");               

                FM17550_SetCW(0);                  return 0;

            }

            printf("\r\n-> SET BLOCK VALUE SUCCESS!");

            break;

        default:

            break;

    }

    FM17550_SetCW(0);           return 1;

}

实际显示效果

主界面

消费操作界面

充值操作界面

查询操作界面

设置操作界面

关机界面

附件

演示程序源代码:  Project.zip (841.5 KB)


---------------------
作者:xld0932
链接:https://bbs.21ic.com/icview-3226582-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

这篇关于[MM32生态] 基于MM32的手持终端设备(国产芯片替代方案)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/309280

相关文章

无人叉车3d激光slam多房间建图定位异常处理方案-墙体画线地图切分方案

墙体画线地图切分方案 针对问题:墙体两侧特征混淆误匹配,导致建图和定位偏差,表现为过门跳变、外月台走歪等 ·解决思路:预期的根治方案IGICP需要较长时间完成上线,先使用切分地图的工程化方案,即墙体两侧切分为不同地图,在某一侧只使用该侧地图进行定位 方案思路 切分原理:切分地图基于关键帧位置,而非点云。 理论基础:光照是直线的,一帧点云必定只能照射到墙的一侧,无法同时照到两侧实践考虑:关

从去中心化到智能化:Web3如何与AI共同塑造数字生态

在数字时代的演进中,Web3和人工智能(AI)正成为塑造未来互联网的两大核心力量。Web3的去中心化理念与AI的智能化技术,正相互交织,共同推动数字生态的变革。本文将探讨Web3与AI的融合如何改变数字世界,并展望这一新兴组合如何重塑我们的在线体验。 Web3的去中心化愿景 Web3代表了互联网的第三代发展,它基于去中心化的区块链技术,旨在创建一个开放、透明且用户主导的数字生态。不同于传统

高效+灵活,万博智云全球发布AWS无代理跨云容灾方案!

摘要 近日,万博智云推出了基于AWS的无代理跨云容灾解决方案,并与拉丁美洲,中东,亚洲的合作伙伴面向全球开展了联合发布。这一方案以AWS应用环境为基础,将HyperBDR平台的高效、灵活和成本效益优势与无代理功能相结合,为全球企业带来实现了更便捷、经济的数据保护。 一、全球联合发布 9月2日,万博智云CEO Michael Wong在线上平台发布AWS无代理跨云容灾解决方案的阐述视频,介绍了

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

国产游戏崛起:技术革新与文化自信的双重推动

近年来,国产游戏行业发展迅猛,技术水平和作品质量均得到了显著提升。特别是以《黑神话:悟空》为代表的一系列优秀作品,成功打破了过去中国游戏市场以手游和网游为主的局限,向全球玩家展示了中国在单机游戏领域的实力与潜力。随着中国开发者在画面渲染、物理引擎、AI 技术和服务器架构等方面取得了显著进展,国产游戏正逐步赢得国际市场的认可。然而,面对全球游戏行业的激烈竞争,国产游戏技术依然面临诸多挑战,未来的

JavaFX应用更新检测功能(在线自动更新方案)

JavaFX开发的桌面应用属于C端,一般来说需要版本检测和自动更新功能,这里记录一下一种版本检测和自动更新的方法。 1. 整体方案 JavaFX.应用版本检测、自动更新主要涉及一下步骤: 读取本地应用版本拉取远程版本并比较两个版本如果需要升级,那么拉取更新历史弹出升级控制窗口用户选择升级时,拉取升级包解压,重启应用用户选择忽略时,本地版本标志为忽略版本用户选择取消时,隐藏升级控制窗口 2.

如何选择SDR无线图传方案

在开源软件定义无线电(SDR)领域,有几个项目提供了无线图传的解决方案。以下是一些开源SDR无线图传方案: 1. **OpenHD**:这是一个远程高清数字图像传输的开源解决方案,它使用SDR技术来实现高清视频的无线传输。OpenHD项目提供了一个完整的工具链,包括发射器和接收器的硬件设计以及相应的软件。 2. **USRP(Universal Software Radio Periphera

MyBatis 切换不同的类型数据库方案

下属案例例当前结合SpringBoot 配置进行讲解。 背景: 实现一个工程里面在部署阶段支持切换不同类型数据库支持。 方案一 数据源配置 关键代码(是什么数据库,该怎么配就怎么配) spring:datasource:name: test# 使用druid数据源type: com.alibaba.druid.pool.DruidDataSource# @需要修改 数据库连接及驱动u

国产游戏行业的崛起与挑战:技术创新引领未来

国产游戏行业的崛起与挑战:技术创新引领未来 近年来,国产游戏行业蓬勃发展,技术水平不断提升,许多优秀作品在国际市场上崭露头角。从画面渲染到物理引擎,从AI技术到服务器架构,国产游戏已实现质的飞跃。然而,面对全球游戏市场的激烈竞争,国产游戏技术仍然面临诸多挑战。本文将探讨这些挑战,并展望未来的机遇,深入分析IT技术的创新将如何推动行业发展。 国产游戏技术现状 国产游戏在画面渲染、物理引擎、AI

一种改进的red5集群方案的应用、基于Red5服务器集群负载均衡调度算法研究

转自: 一种改进的red5集群方案的应用: http://wenku.baidu.com/link?url=jYQ1wNwHVBqJ-5XCYq0PRligp6Y5q6BYXyISUsF56My8DP8dc9CZ4pZvpPz1abxJn8fojMrL0IyfmMHStpvkotqC1RWlRMGnzVL1X4IPOa_  基于Red5服务器集群负载均衡调度算法研究 http://ww