完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
`前面的章节我们与大家一起学习了基于AMetal平台对MM32 MCU相关外设的操作,各外设都是分散独立操作的,我们今天将针对前面的外设结合具体的案例一起来实战演练测试,与大家一起来熟悉在实际的项目中,我们该如何使用AMetal开发相关产品案例,进一步熟悉AMetal带来的便利。 今天我将结合矩阵键盘、SPI FLASH和UART通信等功能综合实验,用户方案如果有其他的功能需求可以参考周立功老师的《AMetal从入门到实践》一书,上面有详细介绍AMetal平台,从新建工程到商用量产方案,从软件到硬件都有详细的介绍。 01 矩阵键盘按键设计 本次实验采用 6× 8 矩阵按键设计,仅用14个I/O就能实现48个按键检测,行线为6,列线为 8,行线为输出,列线为输入。 矩阵按键可以提高I/O的使用效率,但是要区分和判断按键动作的方法却比较复杂。每次扫描一行,扫描该行时,对应行线输出为低电平,其余行线输出为高电平,然后读取所有列线的电平,若有列线读到低电平,则表明该行与读到低电平的列对应的交叉点有按键按下。逐列扫描法恰好相反,其列线为输出,行线为输入,但基本原理还是一样的。 AMetal已经集成了矩阵按键组件软件,客户只需配置与矩阵键盘相关的信息即可,操作非常简单。在 am_key_matrix_gpio.h 文件中,定义了与矩阵键盘相关配置函数结构体。 typedef struct am_key_matrix_gpio_info{ am_key_matrix_base_info_t base_info; // 矩阵键盘基础信息 const int *p_pins_row; // 行线引脚 cons tint *p_pins_col; // 列线引脚 }am_key_matrix_gpio_info_t; /** * 矩阵键盘基础信息 */ typedef struct am_key_matrix_base_info{ int row; // 行数目 int col; // 列数目 const int *p_codes; // 各个按键对应的编码,按行的顺序依次对应 am_bool_t active_low; // 按键按下后是否为低电平 uint8_t scan_mode; // 扫描方式 (按行扫描或按列扫描) }am_key_matrix_base_info_t; 在am_key_matrix_gpio_info成员中包含了GPIO驱动矩阵键盘的所有信息,包含了矩阵键盘的基础信息,如矩阵键盘的行数和列数、各按键对应的编码、按键扫描时间及扫描方式等,在am_key_matrix_gpio.c 文件中进行赋值,实现源码如下: ● __g_key_pins_row 指向存放矩阵键盘行线对应引脚号的数组, 在此填入行引脚; /******************************************************************************* 按键 GPIO 行线引脚 *******************************************************************************/ /*******************************************************************************/ ● __g_key_pins_col 指向存放矩阵键盘列线对应引脚号的数组, 在此填入列引脚; /******************************************************************************* 按键 GPIO 列线引脚 *******************************************************************************/ static const int __g_key_pins_col[] = { PIOA_15, PIOA_14, PIOA_13, PIOA_12, PIOA_11, PIOA_10, PIOA_09, PIOA_08}; /*******************************************************************************/ ● __g_key_codes 指向按键编码数组, 指定了各按键对应的编码, 在此填入按键编码; /******************************************************************************* 按编码信息 *******************************************************************************/ static const int __g_key_codes[] = { KEY_00, KEY_01,KEY_02, KEY_03,KEY_04, KEY_05,KEY_06, KEY_07, KEY_10, KEY_11,KEY_12, KEY_13,KEY_14, KEY_15,KEY_16, KEY_17, KEY_20, KEY_21,KEY_22, KEY_23,KEY_24, KEY_25,KEY_26, KEY_27, KEY_30, KEY_31,KEY_32, KEY_33,KEY_34, KEY_35,KEY_36, KEY_37, KEY_40, KEY_41,KEY_42, KEY_43,KEY_44, KEY_45,KEY_46, KEY_47, KEY_50, KEY_51,KEY_52, KEY_53,KEY_54, KEY_55,KEY_56, KEY_57, }; ● scan_interval_ms 指定了按键扫描的时间间隔(单位:毫秒),即每隔该段时间执行一次按键检测,检测是否有按键事件发生(按键按下或按键释放),该值一般设置为 5 ms,在结构体中直接赋值即可,实现源码如下: int am_miniport_key_inst_init (void) { static am_key_matrix_gpio_softimer_t miniport_key; static const am_key_matrix_gpio_softimer_info_t miniport_key_info = { { { 6, // 6 行按键 8, // 8 列按键 __g_key_codes, // 各按键对应的编码 AM_TRUE, // 按键低电平视为按下 AM_KEY_MATRIX_SCAN_MODE_COL, // 扫描方式,按列扫描 }, __g_key_pins_row, __g_key_pins_col, }, 5, // 扫描时间间隔, 5ms }; return am_key_matrix_gpio_softimer_init(&miniport_key, &miniport_key_info); } 进行完上述配置之后,就可以调用函数实现按键键值读取,触发完成相应的功能,实现源码如下: static void __input_key_proc (void *p_arg, int key_code, int key_state, int keep_time) { switch (key_code) { case KEY_0: if (key_state == AM_INPUT_KEY_STATE_PRESSED) { AM_DBG_INFO("key0 Press! Keep_time is %d ", keep_time); } else if (key_state == AM_INPUT_KEY_STATE_RELEASED) { AM_DBG_INFO("key0 Released! "); } break; case KEY_1: if (key_state == AM_INPUT_KEY_STATE_PRESSED) { AM_DBG_INFO("key1 Press! Keep_time is %d ", keep_time); } else if (key_state == AM_INPUT_KEY_STATE_RELEASED) { AM_DBG_INFO("key1 Released! "); } break; case KEY_2: if (key_state == AM_INPUT_KEY_STATE_PRESSED) { AM_DBG_INFO("key2 Press! Keep_time is %d ", keep_time); } else if (key_state == AM_INPUT_KEY_STATE_RELEASED) { AM_DBG_INFO("key2 Released! "); } break; case KEY_3: if (key_state == AM_INPUT_KEY_STATE_PRESSED) { AM_DBG_INFO("key3 Press! Keep_time is %d ", keep_time); } else if (key_state == AM_INPUT_KEY_STATE_RELEASED) { AM_DBG_INFO("key3 Released! "); } break; ......../** 更多按键处理 */ default : break; } } void am_get_key (void) 02 SPI NOR Flash 实现数据读写 本次实验需要外扩一个SPI NOR Flash 用于存储控制相关数据,采用 ISSI 的IS25LP032设计。IS25LP032 的通信接口为标准4线SPI接口(支持模式0和模式3),即CS、MOSI、MISO、CLK。其中,CS(#1)、SO(#2)、SI(#5)、SCLK(#6)分别为SPI的CS、MISO、MOSI 和 CLK信号引脚。特别地,WP(#3)用于写保护,HOLD(#7)用于暂停数据传输。一般来说,这两个引脚不会使用,可通过上拉电阻上拉至高电平。AMetal提供了支持常见的 IS25LP064、 MX25L8006、MX25L1606……等系列 SPI NOR Flash 器件的驱动函数,SPI NOR Flash 比较特殊,在写入数据前必须确保相应的地址单元已经被擦除,因此除初始化、读写函数外,还有一个擦除函数,其接口函数如下所示:
利用平台提供的函数接口,下面我们实现对IS25LP032进行数据的读写和校验,这里我们以对地址0x001000进行擦读写为例,对该地址写入16字节大小的内容,写入后在通过 SPI 读出,与写入参数对比,确保数据正确,实现源码如下: #define __BUF_SIZE 16 /* 缓冲区大小 */ void demo_is25xx_entry (am_is25xx_handle_t is25xx_handle, int32_t test_lenth) { int ret; uint8_t i; uint8_t wr_buf[__BUF_SIZE] = {0}; /* 写数据缓存定义 */ uint8_t rd_buf[__BUF_SIZE] = {0}; /* 读数据缓存定义 */ if (__BUF_SIZE < test_lenth) { test_lenth = __BUF_SIZE; } /* 填充发送缓冲区 */ for (i = 0;i < test_lenth; i++) { wr_buf = i; } /* 擦除扇区 */ am_is25xx_erase(is25xx_handle, 0x001000, test_lenth); /* 写数据 */ ret = am_is25xx_write(is25xx_handle, 0x001000, &wr_buf[0], test_lenth); if (ret != AM_OK) { AM_DBG_INFO("am_is25xx_write error(id: %d). ", ret); return; } am_mdelay(5); /* 读数据 */ ret = am_is25xx_read(is25xx_handle, 0x001000, &rd_buf[0], test_lenth); if (ret != AM_OK) { AM_DBG_INFO("am_is25xx_read error(id: %d). ", ret); return; } /* 校验写入和读取的数据是否一致 */ for (i = 0; i < test_lenth; i++) { AM_DBG_INFO("Read FLASH the %2dth data is %2x ", i ,rd_buf); /* 校验失败 */ if(wr_buf != rd_buf) { AM_DBG_INFO("verify failed at index %d. ", i); break; } } if (test_lenth == i) { AM_DBG_INFO("verify success! "); } } 03 带缓冲区的 UART 接口 本次实验通过UART转化为RS232电平串口与主控板通信。由于查询模式会阻塞整个应用,因此在实际应用中几乎都使用中断模式。但在中断模式下,UART 每收到一个数据都会调用回调函数,如果将数据的处理放在回调函数中,很有可能因当前数据的处理还未结束而丢失下一个数据。基于此,AMetal 提供了一组带缓冲区的UART通用接口,其实现是在UART 中断接收与应用程序之间,增加一个接收缓冲区。当串口收到数据时,将数据存放在缓冲区中,应用程序直接访问缓冲区即可。对于UART发送,虽然不存在丢失数据的问题,但为了便于开发应用程序,避免在UART中断模式下的回调函数接口中一次发送单个数据,同样提供了带缓冲区的 UART 发送函数。当应用程序发送数据时,将发送数据存放在发送缓冲区中,串口在发送空闲时提取发送缓冲区中的数据进行发送。
下面,可以基于AMetal提供的函数接口,来实现对数据的收发,数据收取后,通常需要按照预定的协议进行解码,以此来识别接收指令的意图,以便完成其功能。在接收数据时,需要指定接收数据的个数,即从buf区读取数据的量。为了防止接收区没有数据而使得函数一直处于等待状态,可以设置超时时间,设置超时时间后,如果超过时间,函数可以直接返回,其返回值为实际接收到的数据的个数,并可清空接收数据的buf,默认情况下不开启超时时间设置,需要在串口初始化后进行设置。 本实验中,定义了一系列的指令,在此不对指令做具体说明,仅接收数据,这里可以设置超时时间为50ms, 默认指令长度一致,均为10个字节,每次从buf读取10个字节,读取数据收后,对数据进行解码,识别指令,进而完成后续操作,实现源码如下: void demo_std_uart_ringbuf_entry (am_uart_handle_t uart_handle) { uint8_t uart1rx_buf[10]; /* 数据缓冲区 */ am_uart_rngbuf_handle_t handle = NULL; /* 串口环形缓冲区服务句柄 */ /* UART 初始化为环形缓冲区模式 */ handle = am_uart_rngbuf_init( &__g_uart_ringbuf_dev, uart_handle, __uart_rxbuf, UART_RX_BUF_SIZE, __uart_txbuf, UART_TX_BUF_SIZE); am_uart_rngbuf_ioctl( handle, // 串口(带缓冲区)标准服务 handle AM_UART_RNGBUF_TIMEOUT, // “设置读超时时间”控制命令 (void *)50); // 设置超时时间为 50ms am_uart_rngbuf_send(handle, __ch, sizeof(__ch)); while (1) { /* 从接收缓冲区取出 10 个数据到 buf,接收数据 */ am_uart_rngbuf_receive(handle, uart1rx_buf, 10); if(uart1rx_buf[0]=="A") { if(uart1rx_buf[1]=="T") /* 继续识别指令 */ } else if(uart1rx_buf[0]=="B") { if(uart1rx_buf[1]=="T") /* 继续识别指令 */ } } } 04 AMetal源码获取 AMetal开源代码存放在Github,其开源网址为,https://github.com/zlgopen/ametal,用户可直接通过此页面下载AMetal开源代码包,点击“Clone or download”,再点击“Download ZIP”即可下载开源代码包。 AMetal开源代码同步存放在码云,其开源网址为,https://gitee.com/zlgopen/ametal,用户可直接通过此页面下载AMetal开源代码包,点击“克隆/下载”,再点击“下载ZIP”即可下载开源代码包。 05 总结 AMetal平台就是大家经常提到的为广大用户“造轮子”,在该平台实现了用户方案设计后,省略后续如果换平台时需要重新“建轮子”的过程,换平台只需要切换底层实现方式,用户层的代码还是保持原样即可实现最终的量产方案,AMetal打破了传统嵌入式编程的依靠单个硬件编程弊端,采用面向对象的C编程的方式来实现“一次编程,到处使用”的效果。 设计的核心其实就是将共性和可变性匹配到实现的威廉希尔官方网站 结构中,比如类、类层次、函数、数据结构等。框架是针对结构的,当结构与框架关联起来的时候,框架就成了主要产物,AMetal就是将共性和可变的进行重新编排,比如数据收发是共性,但是可变的就是各家MCU对应外设的实现方式,在此基础上AMetal进行了重新的编排整合,就实现了“一次编程,到处使用”的效果,但是会产生更多的中间层,所以代码容量相对于直接操作底层函数更大一些,用户可根据自身的需求选择适合自己的方案实现方式。 AMetal构建了一套抽象度很高的标准化接口,封装了各种MCU底层的变化,为应用软件提供了更稳定的抽象服务,延长了软件系统的生命周期。 最后只想来一句:有轮子真香! 2020灵动MM32协作大会 2020年9月10日 9:00 深圳星河丽思卡尔顿酒店 “2020灵动MM32协作大会”重磅开启! 自2016年以来,每年盛夏,“灵动MM32协作大会”已与大家相伴走过4届,感谢大家一直以来对灵动MM32 MCU的信任和支持! “2020灵动MM32协作大会”将于9月10日在深圳星河丽思卡尔顿酒店如约而至! 全天的活动精彩纷呈,爆款新品发布、成功案例分享、产业专家热点剖析……除此之外,灵动还为各位来宾准备了丰盛的自助午餐、精美的茶歇,现场还将进行幸运抽奖及各种互动游戏,您将有机会获得灵动为您精心准备的各式礼品! 欢迎大家扫描下图二维码报名参会!现场威廉希尔官方网站 分享、市场分析、“MM32 INSIDE”应用方案展演等干货多多哦 ~ ` |
|
相关推荐
|
|
只有小组成员才能发言,加入小组>>
2255个成员聚集在这个小组
加入小组灵动微电子MM32全系列MCU产品应用手册,库函数和例程和选型表
11913 浏览 3 评论
【MM32 eMiniBoard试用连载】+基于OLED12864的GUI---U8G2
6015 浏览 1 评论
【MM32 eMiniBoard试用连载】移植RT-Thread至MM32L373PS
11149 浏览 0 评论
【MM32 eMiniBoard测评报告】+ 开箱 + 初探
4615 浏览 1 评论
灵动微课堂(第106讲) | MM32 USB功能学习笔记 —— WinUSB设备
4351 浏览 1 评论
[MM32软件] MM32F002使用内部flash存储数据怎么操作?
1559浏览 1评论
888浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2025-1-11 13:04 , Processed in 0.468885 second(s), Total 35, Slave 29 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号