如下图所示,评估板长这个样子:
TKM32F499深圳市好钜润科技有限公司发行的一款高性能单片机,以上图片是基于TKM32F499的一款评估板,可以看到评估板上的资源那是相当的丰富了,而且还是白菜价,到手价仅需88元,不得不说实在是香,香在哪呢?咱们看看特点就知道了。
1、特点
1、IPS全视角液晶屏,阳光下可视度高;
2、高分辨率800*480;
3、超大存储空间:16MB的FLASH及8MB的RAM,可以运行复杂的界面程序;
4、采用高性能TK499芯片,240MHz,带FPU,支持硬件浮点运算;
5、USB方式下载,可以用USB更新程序,图标及字库;
6、带WIFI模块,支持无线显示,利于接入IoT物联网
7、提供丰富外设:串口、SPI、GPIO、USB,SDIO、五向按键、3路LED灯;
8、性价比高,88元包含了高性能M4芯片,16M FLASH与8M RAM及高分辨率TFT屏, 可以直接片上编程,比串口屏灵活,速度更快;
这么强大的一个评估板,直接套在我们的产品上简直就是天然的优势,但不知道开发起来难度大不大?
根据官方对评估板的介绍,用户不需要关心环境设置以及路径设置,所有的一切,他们都帮我们配置好了,所以我们只需要拿到程序的工程文件,直接就可以开始编写代码,其它一些特殊的说明直接查看资料包即可。
2、开发平台
那这款芯片用什么平台来进行开发呢?
这是我们熟悉的Keil5,编程风格和STM32是几乎是一样的。
想要让芯片工作起来,去驱动连接的外设,首先我们得了解芯片的一些规格,比如系统框图、引脚排列和引脚说明、存储器映射表,了解了这些以后,我们再去结合芯片的Datasheet去查询相应的寄存器以及注意事项,最后完成我们产品功能的软件编写。
3、TKM32F499芯片架构
以下是TKM32F499芯片系统框图,系统框图能让我们快速了解这款芯片包含哪些模块单元,以便于我们开发产品时进行选型。
可以看到,芯片基于ARM-ContexM4 FPU架构,我们来简单了解下:
ARM Cortex-M4处理器是由ARM专门开发的最新嵌入式处理器,在M3的基础上强化了运算能力,新加了浮点、DSP、并行计算等。
ARM-ContexM4 FPU处理器则是在此架构基础上单精度浮点单元(FPU),能够高效率处理较为复杂的浮点运算,如电机闭环控制、PID算法、快速傅里叶变换等。
TKM32F499支持的功能还是非常多的,除了常规的STM32上有的功能,还加了一个特色模块,比如TK80.
4、TKM32F499芯片管脚及排列说明
5、TKM32F499存储器映射表
6、TKM32F499测评程序编写步骤及程序解读
玩任何一个板子,先点个灯,后面就好办了,官方资料包里第一个例程GPIO_LED是让评估板上的LED以固定的频率进行闪烁,我们来看下原理图:
由于例程里没有使用PWM去驱动LED,所以直接高低电平就可以驱动它进行工作了。管脚接在PD8这个位置。
既然和STM32的编程方式一样,那么肯定是以下的流程:
1、初始化并使能RCC时钟
2、GPIO模式配置及初始化
3、使用GPIO
TKM32F499,官方已经写好相应的开发库了,我们拿来即用即可,编写这个例程,一共需要用到库文件里四个API,分别是:
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState); void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct); void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint32_t GPIO_Pin); void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint32_t GPIO_Pin);
6.1 RCC_AHBPeriphClockCmd
RCC_AHBPeriphClockCmd用于配置系统的RCC时钟,只有时钟起来了,外设才能工作。打HAL_rcc.h可以看到,官方根据Datasheet已经写好了相应的地址。
这里我们要控制的外设是GPIOD,根据存储器映射图,我们可以看到GPIOD挂在AHB1总线下:
对应的,接下来看Datasheet关于RCC相关的章节关于这部分的描述:
我们需要把这个寄存器的第三位给使能了,那么GPIOD功能就可以正常使用了。
所以非常简单,我们只需要把这一位和RCC的AHB1外设时钟使能寄存器做一个或操作即可,来看看程序里是怎么写的:
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);
追进该函数的RCC_AHBPeriphClockCmd源码实现:
void RCC_AHBPeriphClockCmd(uint32_t RCC_AHBPeriph, FunctionalState NewState) { /* Check the parameters */ assert_param(IS_RCC_AHB_PERIPH(RCC_AHBPeriph)); assert_param(IS_FUNCTIONAL_STATE(NewState)); if (NewState != DISABLE) { RCC->AHB1ENR |= RCC_AHBPeriph; } else { RCC->AHB1ENR &= ~RCC_AHBPeriph; } }
这里首先使用断言函数assert_param检查参数是否有效,主要是检查是否为非法地址。
接下来判断NewState参数,如果是ENABLE ,则与RCC->AHB1ENR置位,否则清除该位,NewState参数其实是一个枚举。
typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
那么RCC->AHB1ENR是啥呢?追进定义看看:
#define PERIPH_BASE ((uint32_t)0x40000000) /*!< SRAM base address in the bit-band region */ #define AHB1PERIPH_BASE (PERIPH_BASE + 0x00020000) #define RCC_BASE (AHB1PERIPH_BASE + 0x3800) #define RCC ((RCC_TypeDef *) RCC_BASE)
这是将一个地址强制转换成一个结构体,STM32里也是这么做的,我们看看对应的结构体:
typedef struct { __IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */ __IO uint32_t PLLCFGR; /*!< RCC clock configuration register, Address offset: 0x04 */ __IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x08 */ __IO uint32_t CIR; /*!< RCC clock interrupt register, Address offset: 0x0C */ __IO uint32_t AHB1RSTR; /*!< RCC AHB peripheral clock register, Address offset: 0x10 */ __IO uint32_t AHB2RSTR; /*!< RCC AHB peripheral clock register, Address offset: 0x14 */ __IO uint32_t APB1RSTR; /*!< RCC AHB peripheral clock register, Address offset: 0x18 */ __IO uint32_t APB2RSTR; /*!< RCC AHB peripheral clock register, Address offset: 0x1C */ __IO uint32_t AHB1ENR; /*!< RCC AHB peripheral clock register, Address offset: 0x20 */ __IO uint32_t AHB2ENR; /*!< RCC AHB peripheral clock register, Address offset: 0x24 */ __IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x28 */ __IO uint32_t APB2ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x2C */ __IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x30 */ __IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x34 */ __IO uint32_t PLLLCDCFGR; /*!< RCC clock configuration register 2, Address offset: 0x38 */ __IO uint32_t PLLDCKCFGR; /*!< RCC clock configuration register 3, Address offset: 0x3C */ } RCC_TypeDef;
对应结构体的成员即为RCC的地址,如果想详细了解它的配置,可以查看手册时钟章节。
RCC_AHBPeriph_GPIOD是一个宏
对应的值是0x0000008,也就是(1 << 3),所以得出结论,把这个宏作为参数传递进RCC_AHBPeriphClockCmd这个函数,在第二个函数写上ENABLE即可完成GPIOD使能RCC时钟的初始化,真香!所以很容易可以写出以下代码完成GPIOD使能时钟的配置:
RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);
6.2 GPIO_Init
/** * @brief Initializes the GPIOx peripheral according to the specified * parameters in the GPIO_InitStruct. * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. * @param GPIO_InitStruct: pointer to a GPIO_InitTypeDef structure that * contains the configuration information for the specified GPIO * peripheral. * @retval : None */ void GPIO_Init(GPIO_TypeDef* GPIOx, GPIO_InitTypeDef* GPIO_InitStruct)
这个函数用户GPIO(通用功能I/O)功能的初始化,与RCC一样,程序里也是定义了操作GPIO相关的结构体GPIO_TypeDef,以下是对应的一些寄存器:
typedef struct { __IO uint32_t CRL; __IO uint32_t CRH; __IO uint32_t IDR; __IO uint32_t ODR; __IO uint32_t BSRR; __IO uint32_t BRR; __IO uint32_t LCKR; uint32_t RESERVED; __IO uint32_t AFRL; __IO uint32_t AFRH; __IO uint32_t CRH_EXT; __IO uint32_t BSRR_EXT; __IO uint32_t AFRH_EXT; } GPIO_TypeDef;
详细可以看GPIO章节手册里对寄存器的描述:
对IO的管脚、速率、模式的设置,用的是GPIO_InitTypeDef这个结构体:
typedef struct { uint32_t GPIO_Pin; GPIOSpeed_TypeDef GPIO_Speed; GPIOMode_TypeDef GPIO_Mode; }GPIO_InitTypeDef;
GPIO_Pin用于设置管脚,对应的也是相应的宏:
#define GPIO_Pin_0 ((uint16_t)0x0001) /* Pin 0 selected */ #define GPIO_Pin_1 ((uint16_t)0x0002) /* Pin 1 selected */ #define GPIO_Pin_2 ((uint16_t)0x0004) /* Pin 2 selected */ #define GPIO_Pin_3 ((uint16_t)0x0008) /* Pin 3 selected */ #define GPIO_Pin_4 ((uint16_t)0x0010) /* Pin 4 selected */ #define GPIO_Pin_5 ((uint16_t)0x0020) /* Pin 5 selected */ #define GPIO_Pin_6 ((uint16_t)0x0040) /* Pin 6 selected */ #define GPIO_Pin_7 ((uint16_t)0x0080) /* Pin 7 selected */ #define GPIO_Pin_8 ((uint16_t)0x0100) /* Pin 8 selected */ #define GPIO_Pin_9 ((uint16_t)0x0200) /* Pin 9 selected */ #define GPIO_Pin_10 ((uint16_t)0x0400) /* Pin 10 selected */ #define GPIO_Pin_11 ((uint16_t)0x0800) /* Pin 11 selected */ #define GPIO_Pin_12 ((uint16_t)0x1000) /* Pin 12 selected */ #define GPIO_Pin_13 ((uint16_t)0x2000) /* Pin 13 selected */ #define GPIO_Pin_14 ((uint16_t)0x4000) /* Pin 14 selected */ #define GPIO_Pin_15 ((uint16_t)0x8000) /* Pin 15 selected */ #define GPIO_Pin_16 ((uint32_t)0x010000) /* Pin 16 selected */ #define GPIO_Pin_17 ((uint32_t)0x020000) /* Pin 17 selected */ #define GPIO_Pin_18 ((uint32_t)0x040000) /* Pin 18 selected */ #define GPIO_Pin_19 ((uint32_t)0x080000) /* Pin 19 selected */ #define GPIO_Pin_20 ((uint32_t)0x100000) /* Pin 20 selected */ #define GPIO_Pin_21 ((uint32_t)0x200000) /* Pin 21 selected */ #define GPIO_Pin_22 ((uint32_t)0x400000) /* Pin 22 selected */ #define GPIO_Pin_23 ((uint32_t)0x800000) /* Pin 23 selected */ #define GPIO_Pin_All ((uint32_t)0xFFFFFF) /* All pins selected */
GPIO_Speed是用于IO速率的配置,对应的是一个枚举:
typedef enum { GPIO_Speed_10MHz = 1, GPIO_Speed_2MHz, GPIO_Speed_50MHz }GPIOSpeed_TypeDef;
GPIO_Mode是针对IO的模式进行配置,对应的是一个枚举
typedef enum { GPIO_Mode_AIN = 0x0, //模拟输入 GPIO_Mode_IN_FLOATING = 0x04, //浮空输入 GPIO_Mode_IPD = 0x28, //下拉输入 GPIO_Mode_IPU = 0x48, //上拉输入 GPIO_Mode_Out_OD = 0x14,//通用开漏输出 GPIO_Mode_Out_PP = 0x10,//通用推挽输出 GPIO_Mode_AF_OD = 0x1C, // 复用开漏输出 GPIO_Mode_AF_PP = 0x18 //复用推挽输出 }GPIOMode_TypeDef;
在了解了以下知识点以后,针对LED管脚的配置,我们很快就能写出代码:
//1、定义GPIO初始化结构体变量 GPIO_InitTypeDef GPIO_InitStructure; //2、初始化配置管脚 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; //3、初始化管脚速率 GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; //4、初始化管脚模式,配置为推挽输出模式 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //5、调用GPIO_Init完成IO的初始化 GPIO_Init(GPIOD, &GPIO_InitStructure);
6.3 GPIO_SetBits、GPIO_ResetBits
GPIO_SetBits和GPIO_ResetBits主要是对端口进行设置和清除,对应的是下面这个寄存器:
以下是两个函数的源代码:
/** * @brief Sets the selected data port bits. * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. * @param GPIO_Pin: specifies the port bits to be written. * This parameter can be any combination of GPIO_Pin_x where * x can be (0..15). * @retval : None */ void GPIO_SetBits(GPIO_TypeDef* GPIOx, uint32_t GPIO_Pin) { /* Check the parameters */ assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); assert_param(IS_GPIO_PIN(GPIO_Pin)); if(GPIO_Pin>GPIO_Pin_15)GPIOE->BSRR_EXT=GPIO_Pin>>16; else GPIOx->BSRR = GPIO_Pin; } /** * @brief Clears the selected data port bits. * @param GPIOx: where x can be (A..G) to select the GPIO peripheral. * @param GPIO_Pin: specifies the port bits to be written. * This parameter can be any combination of GPIO_Pin_x where * x can be (0..15). * @retval : None */ void GPIO_ResetBits(GPIO_TypeDef* GPIOx, uint32_t GPIO_Pin) { /* Check the parameters */ assert_param(IS_GPIO_ALL_PERIPH(GPIOx)); assert_param(IS_GPIO_PIN(GPIO_Pin)); GPIOx->BRR = GPIO_Pin; }
根据功能描述,很容易可以写出下列代码:
//给GPIOD的第八位 GPIO_SetBits(GPIOD, GPIO_Pin_8); //PC8输出高电平,点亮LED //清除GPIOD的第八位 GPIO_ResetBits(GPIOD, GPIO_Pin_8);//PD8输出低电平,熄灭LED
以下是该Demo的源代码:
/****************************************Copyright (c)**************************************************** ** ** ** **--------------File Info--------------------------------------------------------------------------------- ** File name: main.c ** modified Date: 2017-6-20 ** Last Version: V0.1 ** Descriptions: main 函数调用 ** ** 好钜润科技,芯片事业部----深圳龙华应用分部 *********************************************************************************************************/ #include "HAL_conf.h" /******************************************************************************************************** **函数信息 :int main (void) **功能描述 : **输入参数 : **输出参数 : ********************************************************************************************************/ int main(void) { uint32_t i; GPIO_InitTypeDef GPIO_InitStructure;//定义GPIO初始化结构体变量 RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE); //配置连接LED的GPIO为推挽输出模式 GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8; GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Init(GPIOD, &GPIO_InitStructure); while(1)//无限循环 { GPIO_SetBits(GPIOD, GPIO_Pin_8); //PC8输出高电平,点亮LED for(i=0;i<2000000;i++);//延时 GPIO_ResetBits(GPIOD, GPIO_Pin_8);//PD8输出低电平,熄灭LED for(i=0;i<2000000;i++);//延时 } }
7、TKM32F499程序编译与下载
7.1 程序编译
和STM32一样,程序编译直接点击Keil软件上的编译即可,如果程序没有错误则会生成对应的bin文件。
7.2 程序烧录
TK499 支持 U 盘方式下载,无需借助下载器,一根 USB 线就能下载。
方法一:五向按键向上推,同时按一下复位(注意,松手时,先松复位,再松五向按键),然后点一下 KEIL 的下载程序的按钮就可以下载了。KEIL 不会提示是否下载完成,听到退出 U 盘的“叮咚”声就可以了。下载完成一般可以自动运行,如果没运行,可以按一下复位或者断电再上电就可以运行。
方法二:向上推,同时按一下复位就会进入程序下载模式,同样的也会弹出一个 U 盘,把 KEIL 工程目录下生成的 bin 程序拖进 U 盘就行,如下图。
如果是芯片第一次使用,需要先烧录一个 Bootloader。方法是:把五向按键向左推,同时再按一下复位就进入了 Bootloader 下载模式(注意,松开按键时,先松复位,再松五向按键)。这时会弹出一个 TK499 的 U 盘,把开发包压缩包里的 TK499_Bootloader.bin拖进去就行。(一般情况下开发板已经是烧好 Bootloader,此步可省略)
Bootloader 是一个重要的文件,以后会不断升级 Bootloader,用它可以通过 USB下载图片及字库。
注意:2019.10.1 前发布的程序,一般没把 flash_download.exe 下载程序工具放进工程文件夹里,所以用不了方法一来下载。当然你要自己添加也是很容易的,把这个小工具放到工程目录下,然后复制下面两行命令进去就行。
flash_download ..//@L.bin TK499_V2
8、运行结果
LED以固定频率进行闪烁:
最后,感谢淘宝客服小姐姐多送了一块带触摸的开发板给我:
TKM32F499评估板例程及资料下载
链接:https://pan.baidu.com/s/1xujEO4vJ7i7UUK7v_fGNgw 提取码:g1y2
或者后台回复TK499即可获取。