[MM32生态]【MM32+模块】系列:01、开篇(软硬件准备)

由于都是基于实物设计方案的分享,需要设计原理图、进行PCB布线、打板焊接调试,以及功能程序设计等步骤,更新进度相对会慢一些;所以准备了MM32 + 模块这个系列的分享,可以通过MM32的核心板结合众多的硬件功能模块来实现一个简单的单一功能,后期也能通过这种搭积木的方式,来完成一个复杂的项目。所以在开始这个系列主题帖之前,我们需要先准备一些资料、硬件环境、软件环境,以及调试工具、基础工程等;这些我们在接下来的内容会一一讲到:

芯片资料

我们这个系列主题会以灵动微电子的MM32F014CD6P这个芯片作为主控芯片,虽然它是一颗Arm Cortex-M0内核的32位MCU,但它最高工作频率可以达到72MHz,与STM32F103系列的工作频率处于同一水平了;存储部分带有了64KB的FLASH和8KB的SRAM,满足了大多数应用场景下对于应用功能程序空间大小的要求;另外其本身还具备了丰富的外设功能,包含了1个12位的ADC、1个比较器、1个16位高级定时器、1个16位和1个32位通用定时器、3个16位基本定时器,还包含了标准的通信接口:1个I2C接口、2个SPI或I2S接口、3个UART接口和FlexCAN接口;工作电压为2.0~5.5V,这样可以更好的兼容外部扩展模式的供电特性,无需要再额外的进行电压兼容匹配。

在灵动官网上提供了MM32F0140系列芯片的资料手册(数据手册、用户编程手册、勘误手册、以及丰富的应用笔记)、库函数及示例程序,可以到如下链接进行下载:https://www.mindmotion.com.cn/products/mm32mcu/mm32f/mm32f_value_line/mm32f0140/

硬件环境

基于MM32F0140数据手册中引脚分布图,我们绘制了一块核心板,将芯片所有的功能引脚通过双排针的形式,都引出来了,方便扩展其它功能模块进行连线。

硬件环境:原理图

硬件环境:PCB

 

硬件环境:实物图

软件环境

统一使用KEIL MDK集成开发环境,具体的下载、安装过程我们在这边就不一一详述了,可以参考应用笔记的《AN0001 MDK5.18安装指南(中文版)》。如果之前没有开使用过MM32芯片,在第一次进行MM32开发之前,我们还需要安装一下KEIL MDK环境下的MM32芯片支持包,同样可以到官网上下载到芯片支持包和对应的《AN0012 MM32 Series KEIL Pack的安装(中文版)》进行指导操作。

调试工具

我们选用创芯工坊的PWLINK2这个调试下载器,支持市面上多家MCU的调试烧录;另外PWLINK2的接口同时支持5V、3.3V这两个供电电压,这样方便了后面扩展模块的供电需求;另外还带有1路TLL串口,方便我们程序的调试。

 

基础工程

基于官方提供的函数库程序,我们使用KEIL MDK建立一个全新的工程作为基础工程;在这个基础工程中,我们使用到了SysTick作为系统嘀嗒时钟,进行任务轮询调度和精确延时功能的实现、使用到了UART1功能,重载并实现了printf函数的调用,并且基于UART1移植了Letter-shell_2.x开源软件,方便在后面程序中的调试、最后就是实现自己编写的TASK任务创建及调度函数,这个在后面的每一应用功能中都会使用到,所以在提供的函数上都有具体的函数说明及注释,所以看明白这个,才会懂程序是如何运行的;这个只是一个简单的基于时间片轮转的调度实现,但很可靠,已经应用在多个实际项目当中了,有兴趣的小伙伴可以详细看一下。

基础工程:SysTick部分

/*******************************************************************************

 * SysTick初始化配置 

*******************************************************************************/

void SysTick_Init(void)

{

    RCC_ClocksTypeDef  RCC_Clocks;

    RCC_GetClocksFreq(&RCC_Clocks);



    if(SysTick_Config(RCC_Clocks.HCLK_Frequency / 1000) != 0)

    {

        while(1);

    }



    NVIC_SetPriority(SysTick_IRQn, 0);

}





/*******************************************************************************

 * SysTick中断函数

*******************************************************************************/

void SysTick_Handler(void)

{

    SysTick_Tick++;

    TASK_TimeSlice(SysTick_Tick);

}





/*******************************************************************************

 * SysTick精确毫秒延时函数

*******************************************************************************/

void SysTick_DelayMS(uint32_t Tick)

{

    uint32_t Start = SysTick_Tick;



    if((UINT32_MAX - Start) >= Tick)

    {

        while((SysTick_Tick - Start) != Tick);

    }

    else

    {

        while((SysTick_Tick + (UINT32_MAX - Start)) != Tick);

    }

}

基础工程:UART1 & Letter-shell部分
复制
/*******************************************************************************

 * SHELL底层打印函数

*******************************************************************************/

void shellPortWrite(const char ch)

{

    UART_SendData(UART1, (uint8_t)ch);

    while(UART_GetFlagStatus(UART1, UART_IT_TXIEN) == RESET);

}





/*******************************************************************************

 * UART1初始化及SHELL

*******************************************************************************/

void shellPortInit(void)

{

    GPIO_InitTypeDef GPIO_InitStructure;

    NVIC_InitTypeDef NVIC_InitStructure;

    UART_InitTypeDef UART_InitStructure;



    RCC_AHBPeriphClockCmd(RCC_AHBENR_GPIOA,   ENABLE);

    RCC_APB2PeriphClockCmd(RCC_APB2ENR_UART1, ENABLE);



    NVIC_InitStructure.NVIC_IRQChannel = UART1_IRQn;

    NVIC_InitStructure.NVIC_IRQChannelPriority = 1;

    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;

    NVIC_Init(&NVIC_InitStructure);



    UART_StructInit(&UART_InitStructure);

    UART_InitStructure.UART_BaudRate            = 115200;

    UART_InitStructure.UART_WordLength          = UART_WordLength_8b;

    UART_InitStructure.UART_StopBits            = UART_StopBits_1;

    UART_InitStructure.UART_Parity              = UART_Parity_No;

    UART_InitStructure.UART_HardwareFlowControl = UART_HardwareFlowControl_None;

    UART_InitStructure.UART_Mode                = UART_Mode_Rx | UART_Mode_Tx;

    UART_Init(UART1, &UART_InitStructure);



    UART_ITConfig(UART1, UART_IT_RXIEN, ENABLE);



    UART_Cmd(UART1, ENABLE);



    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9,  GPIO_AF_1);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_1);



    GPIO_StructInit(&GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_9;

    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_AF_PP;

    GPIO_Init(GPIOA, &GPIO_InitStructure);



    GPIO_InitStructure.GPIO_Pin   = GPIO_Pin_10;

    GPIO_InitStructure.GPIO_Mode  = GPIO_Mode_IN_FLOATING;

    GPIO_Init(GPIOA, &GPIO_InitStructure);



    shell.write = shellPortWrite;

    shellInit(&shell);

}





/*******************************************************************************

 * UART1中断函数

*******************************************************************************/

void UART1_IRQHandler(void)

{

    if(UART_GetITStatus(UART1, UART_IT_RXIEN) != RESET)

    {

        shellHandler(&shell, UART_ReceiveData(UART1));

        UART_ClearITPendingBit(UART1,  UART_IT_RXIEN);

    }

}





/*******************************************************************************

 * printf打印函数

*******************************************************************************/

int fputc(int ch, FILE *f)

{

    UART_SendData(UART1, (uint8_t)ch);

    while(UART_GetFlagStatus(UART1, UART_IT_TXIEN) == RESET);



    return ch;

}
基础工程:TASK任务调度部分
/*******************************************************************************

 * 添加节点

*******************************************************************************/

void LinkedList_AppendNode(TASK_InfoTypeDef info)

{

    LinkedList_TypeDef *node = head;



    /* 如果当前链表为空 */

    if(head == NULL)

    {

        /* 申请一个内存空间 */

        head = (LinkedList_TypeDef *)malloc(sizeof(LinkedList_TypeDef));



        if(head == NULL)

        {

            free(head);         /* 申请不成功, 对其释放 */

        }

        else

        {

            /* 将新申请到的内容空间作为头节点, 对数据信息进行赋值 */

            memcpy(&head->info, &info, sizeof(TASK_InfoTypeDef));



            head->next = NULL;  /* 设置指向的下一个节点为空 */

        }

    }

    else

    {

        /* 找到最后一个节点(最后一个节点的下一个指向为空) */

        while(node->next != NULL)

        {

            node = node->next;

        }



        /* 给最后一个节点的下一个指向申请一个内存空间 */

        node->next = (LinkedList_TypeDef *)malloc(sizeof(LinkedList_TypeDef));



        /* 如果申请成功 */

        if(node->next != NULL)

        {

            node = node->next;  /* 切换到最后一个节点位置 */



            /* 对新节点的数据信息进行赋值 */

            memcpy(&node->info, &info, sizeof(TASK_InfoTypeDef));



            node->next = NULL;  /* 设置指向的下一个节点为空 */

        }

        else

        {

            free(node->next);   /* 申请不成功, 对其释放 */

        }

    }

}





/*******************************************************************************

 * 查找节点

*******************************************************************************/

uint8_t LinkedList_SearchNode(uint8_t index)

{

    LinkedList_TypeDef *node = head;



    /* 如果节点不为空 */

    while(node != NULL)

    {

        /* 比较当前节点的下标号值, 节点下标号数值唯一性 */

        if(node->info.index == index)

        {

            return 1;       /* 存在下标号相同的节点 */

        }



        node = node->next;  /* 指向下一个节点 */

    }



    return 0;               /* 不存在下标号相同的节点 */

}





/*******************************************************************************

 * 添加任务

*******************************************************************************/

void TASK_Append(uint8_t index, Task_Handler handler, uint32_t tick)

{

    /* 定义一个任务信息结构 */

    TASK_InfoTypeDef info;



    /* 根据参数对其进行赋值 */

    info.index   = index;

    info.ready   = 0;

    info.tick    = tick;

    info.handler = handler;



    /* 判断当前链表中有没有重复的节点*/

    if(LinkedList_SearchNode(index) == 0)

    {

        /* 没有重复的节点, 则添加 */

        LinkedList_AppendNode(info);

    }

    else

    {

        /* 存在重复的节点, 打印提示信息 */

        printf("\r\nDuplicate Task Index!!!");

    }

}





/*******************************************************************************

 * 任务时间片处理

*******************************************************************************/

void TASK_TimeSlice(uint32_t tick)

{

    LinkedList_TypeDef *node = head;



    /* 如果节点不为空 */

    while(node != NULL)

    {

        /* 根据当前的TICK与节点配置的TICK进行比较, 如果相余为0, 置位节点的READY标志 */

        if((tick % node->info.tick) == 0)

        {

            node->info.ready = 1;

        }

        

        node = node->next;  /* 指向下一个节点 */

    }

}





/*******************************************************************************

 * 任务调度

*******************************************************************************/

void TASK_Scheduling(void)

{

    LinkedList_TypeDef *node = head;



    /* 如果节点不为空, 存在需要被调度的任务 */

    while(node != NULL)

    {

        /* 如果节点当前处于READY状态 */

        if(node->info.ready)

        {

            node->info.ready = 0;

            node->info.handler();   /* 调用节点的处理函数 */

        }



        node = node->next;          /* 指向下一个节点 */

    }

}

基础工程:实际运行效果

在附件中提供了PCB的Gerber产生文件,可以直接使用这个去制作PCB,跟着一起来熟悉MM32及其应用,这个PCB板支持LQFP48封装的MM32所有芯片哦,真正做到了全兼容!
---------------------
作者:xld0932
链接:https://bbs.21ic.com/icview-3206376-1-1.html
来源:21ic.com
此文章已获得原创/原创奖标签,著作权归21ic所有,任何人未经允许禁止转载。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值