xld0932 发表于 2022-5-25 10:40

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

本帖最后由 xld0932 于 2022-5-25 10:52 编辑

#申请原创#   @21小跑堂

芯片在我们生活中无处不在,常用的手机、汽车、家用电器、智能家居、消费电子等等都离不开芯片,以及上班途中骑行的共享单车,也都有芯片身影的存在。在当前市场大环境下,尤其是之前使用国外芯片(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路,完全满足硬件需求,芯片就可以直接替换使用了。
PeripheralsSTM32F103VET6MM32F3273G8P
FLASH512KB512KB
SRAM64KB128KB
FSMCYESYES
定时器通用44
基本22
高级22
通讯接口UART58
I2C22
SPI33
I2S22
CAN11
SDIO11
USB-OTG FS11
GPIO端口数8084
12位ADC单元数32
通道数1616
12位DAC22
RTCYESYES
工作电压2.0V~3.6V2.0V~5.5V
CPU频率72MHz120MHz
芯片封装LPFQ100LQFP100


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

基于MM32F3273G8P的软件功能实现基于手持设备终端,通过按键、显示、非接触式IC卡的读写操作,模拟完成客户在消费时的消费、充值、查询、设置等功能,结合硬件设计,驱动部分的功能如下所示:
[*]通过FSMC接口驱动显示LCD显示,通过GPIO对LCD背光进行控制;
[*]通过硬件I2C接口驱动PCF8563 RTC芯片,进行精确时钟管理,并在LCD液晶屏上显示当前的日期和时间信息;
[*]通过GPIO口模拟TM1668的控制时序,实现对4*5按键矩阵的检测和识别,对检测到的按键在不同状态任务下做相应的功能处理;
[*]通过硬件SPI接口驱动2个SPI 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功能;具体的实现和代码可以参加附件中的软件工程源代码。

部分程序源码LCD的FSMC初始化及配置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 = 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);
      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_InitTypeDefI2C_InitStructure;

    uint8_t wBuffer = {0x00, 0x56, 0x12, 0x29, 0x03, 0x12, 0x21};
    uint8_t rBuffer = {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 != 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 = 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);
    }

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

    for(uint8_t i = 0; i < 4; i++)
    {
      printf("0x%02x ", PICC_A.UID);
    }

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

    if(Mifare_Auth(KEY_A_AUTH, 0, KEY_A, 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);
    }

    RemainingAmount= BLOCK_DATA; RemainingAmount *= 256;
    RemainingAmount += BLOCK_DATA; RemainingAmount *= 256;
    RemainingAmount += BLOCK_DATA; RemainingAmount *= 256;
    RemainingAmount += BLOCK_DATA;

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

    switch(AmountOperation)
    {
      case 1: /* 消费 */
            printf("\r\nConsumptionAmount : %d", ConsumptionAmount);

            if(RemainingAmount >= ConsumptionAmount)
            {
                BLOCK_VAULE = (uint8_t)(((RemainingAmount - ConsumptionAmount) >> 0x00) & 0xFF);
                BLOCK_VAULE = (uint8_t)(((RemainingAmount - ConsumptionAmount) >> 0x08) & 0xFF);
                BLOCK_VAULE = (uint8_t)(((RemainingAmount - ConsumptionAmount) >> 0x10) & 0xFF);
                BLOCK_VAULE = (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 = (uint8_t)(((RemainingAmount + RechargeAmount) >> 0x00) & 0xFF);
            BLOCK_VAULE = (uint8_t)(((RemainingAmount + RechargeAmount) >> 0x08) & 0xFF);
            BLOCK_VAULE = (uint8_t)(((RemainingAmount + RechargeAmount) >> 0x10) & 0xFF);
            BLOCK_VAULE = (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;
}

实际显示效果主界面
消费操作界面
充值操作界面
查询操作界面设置操作界面
关机界面

附件演示程序源代码:

演示视频
[*]基于MM32的手持终端设备(国产芯片替代方案):https://www.bilibili.com/video/BV1Mr4y147Ly/


www5911839 发表于 2022-5-25 19:44

点赞 + 10086

xld0932 发表于 2022-5-26 09:01

www5911839 发表于 2022-5-25 19:44
点赞 + 10086

vikey_zhu 发表于 2022-5-26 09:05

现在国产的芯片越来越多好,质量也慢慢跟上去了,只是对于学生,或新手来说,教程,学习资料都还不是全面。还是有很长的路要走

www5911839 发表于 2022-5-26 10:08

vikey_zhu 发表于 2022-5-26 09:05
现在国产的芯片越来越多好,质量也慢慢跟上去了,只是对于学生,或新手来说,教程,学习资料都还不是全面。 ...

楼主这开源的一系列 MM32 的资料,质量很高,资料还是很全面的。

xld0932 发表于 2022-5-26 14:01

www5911839 发表于 2022-5-26 10:08
楼主这开源的一系列 MM32 的资料,质量很高,资料还是很全面的。

说实话,有好几次我都不想写了但看到很多网友的支持……还是再坚持坚持吧……谢谢{:handshake:}

www5911839 发表于 2022-5-26 19:25

xld0932 发表于 2022-5-26 14:01
说实话,有好几次我都不想写了但看到很多网友的支持……还是再坚持坚持吧……谢谢 ...

在21ic投入产出比太低,不行开个公众号和B站写系列**呢,质量这么高的技术**,长此以往肯定有识货的人支持。

安富莱的 eric2013 白工让人非常敬佩,就拿周报连说,连续 266 期没有一期落下。安富莱的 STM32 系列入门门槛是很高,但是不管是教程和示例水准也高。 大侠你这一系列的 MM32 水准也很高,贴近实战,和安富莱不相上下。


xld0932 发表于 2022-5-26 21:41

www5911839 发表于 2022-5-26 19:25
在21ic投入产出比太低,不行开个公众号和B站写系列**呢,质量这么高的技术**,长此以往肯定有识货的人支 ...

那是真正的技术大佬,向他学习

小峰i 发表于 2022-6-1 10:46

大佬牛X

xld0932 发表于 2022-6-1 13:16

小峰i 发表于 2022-6-1 10:46
大佬牛X

onlycook 发表于 2022-6-27 15:24

大佬好棒棒哟

freedy 发表于 2022-10-24 15:53

同样是用一款芯片,感觉楼主做的产品功能和质量很ok啊,比我小菜**好多了
页: [1]
查看完整版本: 基于MM32的手持终端设备(国产芯片替代方案)