本帖最后由 xld0932 于 2022-3-20 08:43 编辑
#申请原创# @21小跑堂
前面分享了几篇基于MM32的应用得到了21ic及网友们的鼓励及支持,后续还有更多的分享,但由于都是基于实物设计方案的分享,需要设计原理图、进行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所有芯片哦,真正做到了全兼容!
附件 MM32F0140用户手册:附件太大,到官网下载吧
工程源代码:
Template.zip
(709.53 KB)
|