发新帖本帖赏金 100.00元(功能说明)我要提问
返回列表

[MM32生态] 带你一起玩转WPI基于灵动MM32F0140 Arduino平台方案开发板

[复制链接]
2261|12
手机看帖
扫描二维码
随时随地手机跟帖
xld0932|  楼主 | 2022-6-7 09:55 | 显示全部楼层 |阅读模式
#申请原创#   @21小跑堂


相遇
疫情期间,无意中在大大通平台上看到了大联大世平集团推出的基于灵动微MM32F0140 MCU制作的Arduino平台方案,在直播中了解到MM32F0410系列MCU功能特性,以及WPI详细介绍了开发的设计、功能和特点,对网友的很多提问也都进行了答复。活动结束后有幸获得了一块开发板,通过这几天的研究和把玩,今天来带大家一起玩转这块开发板,在程序设计的过程中有不少的注意点和优化设计分享给大家哦。有兴趣的小伙伴可以去观看直播回放哈:https://www.wpgdadatong.com/webinar/243499494

MM32F0140主要特点
  • Arm Cortex-M0内核,运行频率高达72MHz
  • 最高64KB FLASH和8KB SRAM
  • 内置5个DMA通道
  • 内置32-bit硬件除法单元
  • 3组UART、2组SPI、2组I2C、1组I2C
  • 1组FlexCAN,支持CAN2.0协议
  • 1组高级定时器,可输出4通道带互补的PWM波形,支持死区和刹车功能
  • 1组32-bit定时器、4组16-bit定时器
  • 1组12-bit SAR ADC,最高支持1Msps采样率和13路采样通道
  • 1组高速模拟比较单元
  • 2.0V~5.5V宽工作电压

MM32F0140资源图
MM32F0140框图.png

开发板功能示意图
系统框图.png

开发板实物展示图
正面.png 背面.png

开发板功能介绍
大联大世平集团推出的基于灵动微MM32F0140系列MCU制作的Arduino平台方案开发板,搭配了众多的传感器件以及接口IC,例如ams TMF8821多点ToF、Novosense的NSHT35温湿传感器和NCA1042 CAN收发器、Vishay的TFDU4300红外收发器等等,另外还有onsemi的CAT24C03 EEPROM、Winbond的W25Q16JV SPI FLASH、LCD接口、按键、RGB LED等等……另外还兼容Arduino接口,方便更多的扩展与应用。通过这块开发板,我们可以实现如下功能:
  • 实现ADC采样
  • 实现按键检测
  • 实现FlexCAN通讯
  • 实现EEPROM数据存取功能
  • 实现DAC音频播放功能
  • 实现IrDA红外通讯功能
  • 实现LCD TFT液晶屏幕显示
  • 实现NSHT35温湿检测功能
  • 实现RGB LED控制功能
  • 实现SWITCH开关状态检测功能
  • 实现ToF高精度测距功能
  • 实现SPI FLASH数据存取功能
  • 实现Xmodem下载文件/数据到SPI FLASH的应用功能

需要说明的是在收到的开发上NSHT35温湿度传感器属于选焊器件,没有焊接在板子上;另外ToF高精度测距模块使用的TFM8821,对于它的操作需要原厂的技术支持并提供相应的参数文件;所以这两块功能作为保留,暂时无法进行操作和实现;而我们会将开发板上其余所有功能都一一实现,并进行演示和测试。

大大通上提供部分技术文档的下载:https://www.wpgdadatong.com/solution/detail?PID=5481,其中包含原理图、项目计划表、硬件BOM、以及硬件PCB图;但没有提供开发板的软件工程源代码,以及其它的一些手册资料也都没有开放出来,这让很多小伙伴在拿到这个开发板后,都只能看看原理图、看看开发板……要怎么去调试、演示成了最大的问题。下面我们一边分析硬件原理图,一边演示开发板功能,来实现上面提到的实现功能,分享给大家。

原理图设计
2.png 3.png 4.png 5.png

原理图的解读和建议
整个开发板由一个Micro USB接口供电,这个Micro USB仅为供电接口,不带有任何通讯调试功能哈;所以个人建议在下次改板时,USB接口可以加一个CH340,作为调试用,会很实用,也会很方便!

开发板上MM32F0140芯片的程序下载接口为1.27mm间距的2*5双排针,可能是为了节省PCB的空间吧,才使用了这们的接口;同样是通过SWD接口下载程序,我更喜欢4PIN(VCC、GND、SWDIO、SWDCLK)或者5PIN(VCC、GND、SWDIO、SWDCLK、NRST)这样的2.54间距排针,这样可以直接插上杜邦线就可以下载程序了,但现在板子上这样的接口,我还需要另外配一个2.54mm间距的2*10 接口转1.27mm间距的2*5接口的转接板,不是很方便。

整个硬件系统可以工作在5V电压下,也可以工作在3.3V电压下,可以通过JP1接口进行跳帽选择;但需要注意的是开发上的SPI FLASH和LCD都只能是3.3V供电的,所以在5V供电时,需要将这些元器件的连接断开。

官网上下载下来的原理图中关于RGB LED设计部分有错误,原理图中设计通过JP3、JP4、JP5这3个跳帽将RGB LED的3个控制引脚分为与PA2、PA1、PA3进行连接,但在实际收到的开发板上,这3个跳帽连接的是PB1、PA1、PB0这3个端口引脚,应该是为了避免端口功能重复使用,更新的原理图没有同步过来,这个在程序设计的时候,需要注意一下。

CAN通讯的收发芯片使用的是NCA1042,这个芯片的工作电压是5V,所以开发板在测试CAN功能时,一定要有5V电源输入;另外需要注意的是原理图中使用了PA8这个引脚来控制NCA1042的工作状态,需要将PA8置为低电平时,NCA1042才能够正常工作,如果PA8为高电平,那NCA1042则为待机状态,此时CAN收发器是不工作的,更没有数据传输,更有甚者在CAN上位机软件发送数据时,还会提示发送失败,这些需要注意。

在测试IrDA通讯时,需要通过JP8将TFDU4300的SD与GND相连接,这样IrDA才会处于正常工作的状态;另外就是TFDU4300收发数据是半双工的,即同一时刻只能发送数据或者接收数据,更达不到自发自收的功能,所以需要借助我们之前做的IrDA开发板来搭配测试。

开发平台环境
硬件包含:MM32F0140 Arduino开发板、ARM仿真器、USB转TTL调试工具、JTAG 20PIN转10PIN转换板、LCD TFT液晶显示屏、以及用于播放音乐用的音箱。
软件包含:Keil MDK集成开发环境、SecureCRT调试终端、Image2Lcd图片数据提取软件等。
准备硬件.jpg


RGB LED控制
使用GPIO的控制方式,循环让3个LED点亮,如果需要实现RGB呼吸灯效果,可以参考之前【MM32+模块】专题帖,实现代码和测试效果如下:
void RGBLED_Toggle(void)
{
    static uint8_t Step = 0;

    switch(Step)
    {
        case 0:
            GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_RESET);
            GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
            GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_SET);
            break;

        case 1:
            GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_SET);
            GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_RESET);
            GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_SET);
            break;

        case 2:
            GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_SET);
            GPIO_WriteBit(GPIOA, GPIO_Pin_1, Bit_SET);
            GPIO_WriteBit(GPIOB, GPIO_Pin_0, Bit_RESET);
            break;

        default:
            break;
    }

    Step = (Step + 1) % 3;
}
RGB LED.GIF

独立按键检测
通过时间片轮转调度的方式,软件延时去抖处理,实现了4个按键的独立检测,实现代码和测试效果如下:
void BUTTON_SubScan(uint8_t *State, uint8_t *Count, uint8_t Value, char *Name)
{
    if(*State == 0)
    {
        if(Value == Bit_RESET) *Count += 1;
        else                   *Count  = 0;

        if(*Count > 5)
        {
            *Count = 0; *State = 1;
            printf("\r\n%s Pressed", Name);
        }
    }
    else
    {
        if(Value != Bit_RESET) *Count += 1;
        else                   *Count  = 0;

        if(*Count > 5)
        {
            *Count = 0; *State = 0;
            printf("\r\n%s Release", Name);
        }
    }
}

void BUTTON_Scan(void)
{
    static uint8_t State[4] = {0, 0, 0, 0};
    static uint8_t Count[4] = {0, 0, 0, 0};

    BUTTON_SubScan(&State[0], &Count[0], GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0), "SW2");
    BUTTON_SubScan(&State[1], &Count[1], GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_2), "SW3");
    BUTTON_SubScan(&State[2], &Count[2], GPIO_ReadInputDataBit(GPIOD, GPIO_Pin_3), "SW4");
    BUTTON_SubScan(&State[3], &Count[3], GPIO_ReadInputDataBit(GPIOB, GPIO_Pin_2), "SW5");
}
BUTTON.png

SWITCH开关状态检测
SWITCH是一个4位的拨码开关,通过GPIO端口引脚的状态位来判断拨码开关所处的状态,实现代码和测试效果如下:
void SWITCH_SubScan(uint8_t *State, uint8_t *Count, uint8_t Value, char *Name)
{
    if(*State == 0)
    {
        if(Value != Bit_RESET) *Count += 1;
        else                   *Count  = 0;

        if(*Count > 5)
        {
            *Count = 0; *State = 1;
            printf("\r\n%s -> OFF", Name);
        }
    }
    else
    {
        if(Value == Bit_RESET) *Count += 1;
        else                   *Count  = 0;

        if(*Count > 5)
        {
            *Count = 0; *State = 0;
            printf("\r\n%s -> ON ", Name);
        }
    }
}

void SWITCH_Scan(void)
{
    static uint8_t State[4] = {0, 0, 0, 0};
    static uint8_t Count[4] = {0, 0, 0, 0};

    SWITCH_SubScan(&State[0], &Count[0], GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_15), "SW1-1");
    SWITCH_SubScan(&State[1], &Count[1], GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_15), "SW1-2");
    SWITCH_SubScan(&State[2], &Count[2], GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_14), "SW1-3");
    SWITCH_SubScan(&State[3], &Count[3], GPIO_ReadInputDataBit(GPIOC, GPIO_Pin_13), "SW1-4");
}
SWITCH.png

ADC采样
ADC包含了实现掉电压检测功能和可调电位器的电压检测这两个功能,采用的单通道独立查询采样的实现方式,实现代码和测试效果如下:
void ADC_Handler(void)
{
    uint16_t Result  = 0;
    float    Voltage = 0.0;

    Result = ADC_Convert(ADC_Channel_10);

    Voltage = (float)Result / 4096 * 3.3;

    printf("\r\n");
    printf("\r\nPB3 <-> ADC1_CH10 : %0.3fV", Voltage);

    Result = ADC_Convert(ADC_Channel_12);

    Voltage = (float)Result / 4096 * 3.3;

    printf("\r\nPB7 <-> ADC1_CH12 : %0.3fV", Voltage);
    printf("\r\n");
}
ADC.png

FlexCAN通讯
FlexCAN默认配置为500kbps的通讯速率,每间隔3秒中向CAN总线中发送一个报文;配置了一个接收邮箱,采用中断的方式,在接收到数据后通过调试终端软件打印显示出来,实现代码和测试效果如下:
void FLEX_CAN_IRQHandler(void)
{
    flexcan_frame_t flexcan_frame_rx;

    if(0U != FLEXCAN_GetMbStatusFlags(FLEX_CAN1, 1 << RX_MESSAGE_BUFFER_NUM))
    {
        FLEXCAN_ClearMbStatusFlags(FLEX_CAN1, 1 << RX_MESSAGE_BUFFER_NUM);

        FLEXCAN_ReadRxMb(FLEX_CAN1, RX_MESSAGE_BUFFER_NUM, &flexcan_frame_rx);

        printf("\r\nReceived message from MB%d", RX_MESSAGE_BUFFER_NUM);

        printf("\r\n");
        printf("\r\nrx form  = 0x%x", (flexcan_frame_rx.id & CAN_ID_STD_MASK) >> CAN_ID_STD_SHIFT);
        printf("\r\nrx word0 = 0x%x", flexcan_frame_rx.dataWord0);
        printf("\r\nrx word1 = 0x%x", flexcan_frame_rx.dataWord1);
        printf("\r\n");
    }

    __DSB();
}

void CAN_Handler(void)
{
    flexcan_frame_t flexcan_frame_tx;

    flexcan_frame_tx.length = 0x08;
    flexcan_frame_tx.type   = (uint8_t)Enum_Flexcan_FrameTypeData;
    flexcan_frame_tx.format = (uint8_t)Enum_Flexcan_FrameFormatStandard;
    flexcan_frame_tx.id     = FLEXCAN_ID_STD(0x123);

    flexcan_frame_tx.dataWord0 = CAN_WORD0_DATA_BYTE_0(0x11) |
                                 CAN_WORD0_DATA_BYTE_1(0x22) |
                                 CAN_WORD0_DATA_BYTE_2(0x33) |
                                 CAN_WORD0_DATA_BYTE_3(0x44);

    flexcan_frame_tx.dataWord1 = CAN_WORD1_DATA_BYTE_4(0x55) |
                                 CAN_WORD1_DATA_BYTE_5(0x66) |
                                 CAN_WORD1_DATA_BYTE_6(0x77) |
                                 CAN_WORD1_DATA_BYTE_7(0x88);

    FLEXCAN_TransferSendBlocking(FLEX_CAN1, TX_MESSAGE_BUFFER_NUM, &flexcan_frame_tx);
}
CAN1.png CAN2.png CAN3.png

IrDA红外通讯
在测试IrDA功能的时候需要搭配另外一块IrDA功能板进行测试,在上电的时候会自动向外发送一串数据,然后以中断方式接收数据并通过调试终端软件打印显示出来;实现代码和测试效果如下:
void UART2_IRQHandler(void)
{
    if(UART_GetITStatus(UART2, UART_IT_RXIEN) != RESET)
    {
        printf("%d ", UART_ReceiveData(UART2));
        UART_ClearITPendingBit(UART2, UART_IT_RXIEN);
    }
}

void IrDA_SendData(uint8_t Data)
{
    UART_SendData(UART2, Data);
    while(UART_GetFlagStatus(UART2, UART_IT_TXIEN) == RESET);
}

void IrDA_Test(void)
{
    printf("\r\nIrDA TX : ");

    for(uint8_t i = 0; i < 10; i++)
    {
        IrDA_SendData(i);
        printf("%d ", i);
    }

    printf("\r\n");
}
IrDA2.jpg IrDA1.png

EEPROM数据存取
测试函数向EEPROM中先写入100字节数据,再读取到缓存数组,通过调试终端软件打印显示出来需要注意的是EEPROM是按PAGE进行写入,每个PAGE的写入操作需要一段时间的间隔,这个代码实现的时候需要特别注意;实现代码和测试效果如下:
void EEPROM_Test(void)
{
    uint8_t rBuffer[100], wBuffer[100];
    uint8_t Address = 0,  Length = 100;

    printf("\r\n");

    for(uint8_t i = 0; i < Length; i++)
    {
        rBuffer[i] = 0;
        wBuffer[i] = i + 100;
    }

    printf("\r\nEEPROM Write......");

    EEPROM_WriteData(Address, wBuffer, Length);

    printf("\r\n");

    printf("\r\nEEPROM Read :");

    EEPROM_ReadData(Address, rBuffer, Length);

    for(uint8_t i = 0; i < Length; i++)
    {
        if((i % 16) == 0) printf("\r\n");

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

    printf("\r\n");
}
EEPROM.png

W25Q16数据存取
W25Q16用来保存WAV音频文件和字库文件,为了实现WAV播放音乐时流畅的效果,在读取数据时应尽可能的迅速,所以W25Q16的SPI接口使用DMA的通讯操作方式;在测试W25Q16时先对全片的所有SECTOR进行擦除操作,然后验证写数据和读数据的操作,实现代码和测试效果如下:
void W25Q16_Test(void)
{
    uint8_t Buffer[128];

    W25Q16_ReadDeviceID();

    W25Q16_ReadJEDEC_ID();

    for(uint16_t i = 0; i < 512; i++)
    {
        printf("\r\nW25Q16 Erase Sector[%03d]......", i);
        W25Q16_SectorErase(i);
    }

    printf("\r\n");

    printf("\r\nW25Q16 Erase Sector[0]......");
    W25Q16_SectorErase(0);

    printf("\r\nW25Q16 Read :");
    W25Q16_FastRead(0, Buffer, sizeof(Buffer));

    for(uint8_t i = 0; i < sizeof(Buffer); i++)
    {
        if((i % 16) == 0) printf("\r\n");

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

    printf("\r\n");

    printf("\r\nW25Q16 Write......");

    memset(Buffer, 0, sizeof(Buffer));

    for(uint8_t i = 0; i < sizeof(Buffer); i++)
    {
        Buffer[i] = i;
    }

    W25Q16_PageProgram(0, Buffer, sizeof(Buffer));

    printf("\r\nW25Q16 Read :");

    W25Q16_FastRead(0, Buffer, sizeof(Buffer));

    for(uint8_t i = 0; i < sizeof(Buffer); i++)
    {
        if((i % 16) == 0) printf("\r\n");

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

    printf("\r\n");
}
W25Q16.png

Xmodem下载文件/数据
Xmodem功能通过UART1接口,使用SecureCRT软件将音频文件和字库文件下载到W25Q16存储芯片中;实现代码可以参考附件中的程序,也可以参考之前的Xmodem专题帖,测试效果如下:
Xmodem.png

LCD液晶显示
LCD的控制接口与W25Q16共用了SPI1的SCK和MOSI这两个引脚,但W25Q16需要发送数据和接收数据双向功能,但LCD只需要发送数据即可;所以为了保证W25Q16数据读写正常,在不操作W25Q16时需要将SPI1接收数据的功能关闭!!!LCD实现了图片显示和中英文字符显示,英文字符的字库存放在程序空间,而中文字库则存放在W25Q16存储芯片,实现代码和测试效果如下所示:
void LCD_ShowLOG(uint8_t StartX, uint8_t StartY, const char *str)
{
    while(*str != '\0')
    {
        if(*str < 0x7F)
        {
            if(StartX > (128 - 8))
            {
                StartX = 0; StartY += 16;
            }

            if(StartY > (128 - 16))
            {
                StartX = 0; StartY = 0;

                LCD_ClearScreen(BACKCOLOR);
            }

            LCD_ShowEN(StartX, StartY, *str);

            StartX += 0x08;
            str    += 0x01;
        }
        else
        {
            if(StartX > (128 - 16))
            {
                StartX = 0; StartY += 16;
            }

            if(StartY > (128 - 16))
            {
                StartX = 0; StartY = 0;

                LCD_ClearScreen(BACKCOLOR);
            }

            LCD_ShowCN(StartX, StartY,  str);

            StartX += 0x10;
            str    += 0x02;
        }
    }
}

void LCD_DrawImage(void)
{
    uint16_t Color = 0;
    uint32_t Index = 0;

    for(uint32_t i = 0; i < 47; i++)
    {
        for(uint32_t j = 0; j < 128; j++)
        {
            Color   = gImage_**[Index++];
            Color <<= 8;
            Color  |= gImage_**[Index++];

            LCD_DrawPoint(j, i, Color);
        }
    }
}
LCD.jpg

WAV播放音乐
通过读取W25Q16存储芯片中的WAV音乐文件,进行解析,通过I2C接口将数据发送到HT5010功放芯片进行音乐播放;I2S使用了双缓存空间与DMA的数据传输方式,结合W25Q16存储芯片DMA读取数据的操作方式,加快数据读取速度,使音乐播放效果更流畅;实现代码和测试效果如下所示:
void WAV_PlaySong(void)
{
    WAV_TypeDef WaveFile;

    if(WAV_PlayState == 0)
    {
        if(WAV_DecodeFile(&WaveFile, 0x00000000) == 0)
        {
            if((WaveFile.BitsPerSample == 16) && (WaveFile.nChannels == 2) &&
               (WaveFile.SampleRate  > 44000) && (WaveFile.SampleRate < 48100))
            {
                WAV_NextIndex = 0;
                WAV_PlayEnded = 0;
                WAV_TxLength  = 0;
                WAV_PlayState = 1;

                printf("\r\n");
                printf("\r\nWAV Data Size : %d, Data Start : 0x%08x", WAV_DataSize, WAV_Offset);
                printf("\r\n");

                WAV_PrepareData();
                WAV_PlayHandler();
            }
            else
            {
                printf("\r\nWAV File Format Error!\r\n"); return;
            }
        }
        else
        {
            printf("\r\nNo WAV File!\r\n"); return;
        }
    }
    else
    {
        printf("\r\nThe Song Is Not Over Yet!\r\n");
    }
}
WAV.png

附件
软件工程源代码:
Template_DMA.zip (802.16 KB)

使用特权

评论回复

打赏榜单

21小跑堂 打赏了 100.00 元 2022-06-13
理由:恭喜通过原创文章审核,请多多加油哦

评论
21小跑堂 2022-6-13 10:30 回复TA
详尽介绍了开发板硬件资源,同时利用自己的知识突破官方没有demo的限制。打通开发板试用限制。同时提供丰富的参考完档,便于玩转开发板。 所有的硬件资料均为厂商提供,代码WPI未提供,但是在灵动官网是极易获取的。鉴于文章结构完整,内容丰富,给予100元奖励 
www5911839| | 2022-6-7 13:42 | 显示全部楼层
感谢大佬分享

使用特权

评论回复
xld0932|  楼主 | 2022-6-7 13:51 | 显示全部楼层

使用特权

评论回复
6552918| | 2022-6-8 10:10 | 显示全部楼层
只是硬件接口兼容,软件生态不兼容,和普通开发板一样,意义不大。

使用特权

评论回复
xld0932|  楼主 | 2022-6-8 15:42 | 显示全部楼层
6552918 发表于 2022-6-8 10:10
只是硬件接口兼容,软件生态不兼容,和普通开发板一样,意义不大。

嗯,WPI连代码程序都没给出,别说其它的了;上次直播的时候有问过,硬件上只是兼容了Arduino接口,为的是方便扩展其它Arduino接口的模块;对于Arduino生态的支持,WPI不支持。所以对于你提的这个问题可能还需要原厂或者是有兴趣的小伙伴去实现了……

使用特权

评论回复
daichaodai| | 2022-6-9 08:31 | 显示全部楼层
用arduino开发确实方便,不需要关心底层配置。

使用特权

评论回复
robertesth| | 2022-6-20 15:27 | 显示全部楼层
MM32F0140 怎么搭建环境?

使用特权

评论回复
232321122| | 2022-6-20 16:04 | 显示全部楼层
Arduino平台如何使用?

使用特权

评论回复
1988020566| | 2022-6-20 16:51 | 显示全部楼层
WPI是什么板子?

使用特权

评论回复
gouguoccc| | 2022-6-20 19:32 | 显示全部楼层
这个板子不错,谢谢分享。

使用特权

评论回复
guijial511| | 2022-6-21 08:25 | 显示全部楼层
下载看看,谢谢楼主分享经验。

使用特权

评论回复
onlycook| | 2022-6-27 15:08 | 显示全部楼层
挺不错的的板子,感谢楼主分享这么好的经验

使用特权

评论回复
发新帖 本帖赏金 100.00元(功能说明)我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则