完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
`SPI 接口广泛用于不同设备之间的板级通讯,如扩展串行Flash,DAC,LCD等。SPI允许 MCU 与外部设备以全双工、同步、串行方式通信。应用软件可以通过查询状态或SPI中断来通信,支持DMA请求以达到更快的通信速率。 MM32 MCU支持如下特征:
– 接收的数据有效,接收端的数据溢出 – 在 SPI 主模式完整接收,发送端为空 今天我们将基于AMetal平台来操作SPI对外部SPI FLASH进行读写操作。 Part1 初始化 在使用SPI通用接口前,必须先完成SPI的初始化,以获取标准的SPI实例句柄。 MM32L073 支持SPI功能的外设有SPI1 和SPI2,为方便用户使用,AMetal提供了与各外设对应的实例初始化函数。
这些函数的返回值均为 am_spi_handle_t 类型的 SPI 实例句柄,该句柄将作为 SPI 通用接口中 handle 参数的实参。类型 am_spi_handle_t(am_spi.h)定义如下: typedef struct am_spi_serv *am_spi_handle_t 因为函数返回的SPI实例句柄仅作为参数传递给SPI通用接口,不需要对该句柄做其它任何操作,因此完全不需要了解该类型。注意,若函数返回的实例句柄的值为NULL,则表明初始化失败,不能使用该实例句柄。 如需使用 SPI1,则直接调用 SPI1 实例初始化函数,即可获取对应的实例句柄: am_spi_handle_t spi1_handle = am_mm32l073_spi1_int_inst_init () 打开新建工程的main.c文件, 添加SPI头文件和MM32L073的外设实例初始化函数声明,在am_main函数中添加 SPI1实例初始化函数,并编译该工程,即可完成SPI初始化。 Part2 接口函数 MCU 的 SPI 主要用于主从机的通信,AMetal提供了8个接口函数。
本例中选择MX25L1606 为从机,MCU通过SPI对它写入数据。MX25L1606总容量为 16M(16× 1024× 1024)bits,即2M字节。每个字节对应一个存储地址,因此其存储数据的地址范围为 0x000000 ~ 0x1FFFFF。 Part3 从机实例初始化 对于用户来说,使用 SPI 往往是直接操作一个从机器件, MCU 作为 SPI 主机,为了与从机器件通信,需要知道从机器件的相关信息,比如, SPI 模式、 SPI 速率、数据位宽等。 这就需要定义一个与从机器件对应的实例(从机实例),并使用相关信息完成对从机实例的初始化。其函数原型为: void am_spi_mkdev ( am_spi_device_t *p_dev, //待初始化的从机实例 am_spi_handle_t handle, //通过SPI实例初始化函数获得句柄 uint8_t bits_per_word, // 数据宽度,为 0 默认 8bit uint16_t mode, //模式选择 uint32_t max_speed_hz, // 从设备支持的最高时钟频率 int cs_pin, // 片选引脚 void (*pfunc_cs)(am_spi_device_t *p_dev, int state)) p_dev 是指向 SPI 从机实例描述符的指针, am_spi_device_t 在 am_spi.h 文件中定义: typedef struct am_spi_device am_spi_device_t 该类型用于定义从机实例,用户无需知道其定义的具体内容,只需要使用该类型定义一个从机实例。即: am_spi_device_t spi_dev; // 定义一个 SPI 从机实例
mode 指定使用的模式,SPI 协议定义了 4 种模式,各种模式的主要区别在于空闲时钟极性( CPOL)和时钟相位选择(CPHA)的不同。CPOL 和 CPHA均有两种选择,因此两两组合可以构成 4 种不同的模式,即模式 0~3。当 CPOL 为 0 时,表示时钟空闲时,时钟线为低电平,反之,空闲时为高电平;当 CPHA 为 0 时,表示数据在第 1 个时钟边沿采样,反之,则表示数据在第 2 个时钟边沿采样。 cs_pin 和 pfunc_cs 均与片选引脚相关。pfunc_cs 是指向自定义片选控制函数的指针,若pfunc_cs 的值为 NULL,驱动将自动控制由 cs_pin 指定的引脚实现片选控制;若 pfunc_cs 的值不为 NULL,指向了有效的自定义片选控制函数,则 cs_pin 不再被使用,片选控制将完全由应用实现。当需要片选引脚有效时,驱动将自动调用 pfunc_cs 指向的函数,并传递 state的值为 1。当需要片选引脚无效时,也会调用 pfunc_cs 指向的函数,并传递 state 的值为 0。 一般情况下,片选引脚自动控制即可,即设置 pfunc_cs 的值为 NULL, cs_pin 为片选引脚,如 PIOA_4。 am_spi_mkdev()范例程序am_spi_handle_t spi0_hanlde =am_mm32l073_spi1_inst_init(); //使用MM32L073 的 SPI1 获取 SPI 句柄 am_spi_device_tspi_dev; // 定义从机设备 am_spi_mkdev( &spi_dev, // 传递从机设备 spi1_handle, // SPI1 操作句柄 8, // 数据宽度为 8-bit AM_SPI_MODE_0, // 选择模式 0 3000000, // 最大频率 3000000Hz PIOA_4, // 片选引脚 PIOA_4 NULL); // 无自定义片选控制函数,设置为 NULL Part4 设置从机实例 设置 SPI 从机实例时, 会检查 MCU 的 SPI 主机是否支持从机实例的相关参数和模式。 如果不能支持, 则设置失败, 说明该从机不能使用。其函数原型为:int am_spi_setup (am_spi_device_t *p_dev) 其中的 p_dev 是指向 SPI 从机实例描述符的指针,如果返回 AM_OK,说明设置成功;如果返回-AM_ENOTSUP,说明设置失败,不支持的位宽、模式等。 am_spi_handle_t spi0_handle =am_mm32l073_spi1_inst_init(); // 使用 MM32L073 的 SPI1 获取 SPI 句柄 am_spi_device_t spi_dev; am_spi_mkdev( &spi_dev, spi1_handle, 8, // 数据宽度为 8-bit AM_SPI_MODE_0, // 选择模式 0 3000000, // 最大频率 3000000Hz PIOA_4, // 片选引脚 PIOA_4 NULL); // 无自定义片选控制函数,设置为 NULL am_spi_setup(&spi_dev); // 设置 SPI 从设备 Part5 传输初始化 在 AMetal 中,将收发一次数据的过程抽象为一个“传输” 的概念,要完成一次数据传输,首先就需要初始化一个传输结构体,指定该次数据传输的相关信息。其函数原型为: void am_spi_mktrans( am_spi_transfer_t *p_trans, //待初始化的 SPI 传输 const void *p_txbuf, // 发送数据缓冲区,NULL无数据 void *p_rxbuf, //接收数据缓冲区,NULL无数据 uint32_t nbytes, //传输的字节数 uint8_t cs_change, //传输是否影响片选, 0-不影响,1-影响 uint8_t bits_per_word, //为 0 默认使用设备的字大小 uint16_t delay_usecs, //传输结束后的延时(us) uint32_t speed_hz, //为0默认使用设备中的max_speed_hz uint32_t f lags); // 本次传输的特殊标志 其中,p_trans 为指向SPI传输结构体的指针,am_spi_transfer_t类型是在 am_spi.h 中定义的。即: typedef struct am_spi_transfer am_spi_transfer_t 在实际使用时,只需要定义一个该类型的传输结构体即可。比如: am_spi_transfer_t spi_trans; //定义一个 SPI 传输结构体
因为 SPI 是全双工通信协议,所以单次传输过程中同时包含了数据的发送和接收。函数的参数中,p_txbuf 指定了发送数据的缓冲区,p_rxbuf 指定了接收数据的缓冲区,nbytes 指定了传输的字节数。特别地,有时候可能只希望单向传输数据,若只发送数据,则可以设置p_rxbuf 为 NULL;若只接收数据,则可以设置 p_txbuf 为 NULL。 当传输正常进行时,片选会置为有效状态, cs_change 的值将影响片选何时被置为无效状态。若 cs_change 的值为 0,表明不影响片选,此时,仅当该次传输是消息(多次传输组成一个消息,消息的概念后文会介绍)的最后一次传输时,片选才会被置为无效状态。若cs_change 的值为 1,表明影响片选,此时,若该次传输不是消息的最后一次传输,则在本次传输结束后会立即将片选设置为无效状态,若该次传输是消息的最后一次传输,则不会立即设置片选无效,而是保持有效直到下一个消息的第一次传输开始。 uint8_t tx_buf[8]; uint8_t rx_buf[8]; am_spi_transfer_t spi_trans; am_spi_mktrans( &spi_trans, tx_buf, // 发送数据缓冲区 rx_buf, // 接收数据缓冲区 8, // 传输数据个数为 8 0, // 本次传输不影响片选 0, // 位宽为 0,使用默认位宽(设备中的位宽) 0, // 传输后无需延时 0, // 时钟频率,使用默认速率 0); // 无特殊标志 Part6 消息初始化 一般来说,与实际的 SPI 器件通信时,往往采用的是“命令” +“数据”的格式,这就需要两次传输:一次传输命令,一次传输数据。为此, AMetal 提出了“消息”的概念,一个消息的处理即为一次有实际意义的 SPI 通信,其间可能包含一次或多次传输。 一次消息处理中可能包含很多次的传输,耗时可能较长,为避免阻塞,消息的处理采用异步方式。这就要求指定一个完成回调函数,当消息处理完毕时,自动调用回调函数以通知用户消息处理完毕。回调函数的指定在初始化函数中完成,初始化函数的原型为: void am_spi_msg_init ( am_spi_message_t *p_msg, // 待初始化的 SPI 传输 am_pfnvoid_t pfn_complete, // 消息处理完成回调函数 void *p_arg); // 回调函数的参数 其中的 p_msg 为指向 SPI 消息结构体的指针, am_spi_message_t 类型是在 am_spi.h 中定义的。即: typedef struct am_spi_message am_spi_message_t 实际使用时,仅需使用该类型定义一个消息结构体。即: am_spi_message_t spi_msg; // 定义一个 SPI 消息结构体 pfn_callback 指向的是消息处理完成回调函数, 当消息处理完毕时, 将调用指针指向的函数。其类型 am_pfnvoid_t 在 am_types.h 中定义的。即: typedef void (*am_pfnvoid_t) (void *) 由此可见,函数指针指向的是参数为void *类型的无返回值函数。驱动调用回调函数时,传递给该回调函数的void*类型的参数即为 p_arg 的设定值。 static void __spi_msg_complete_callback (void *p_arg) { // 消息处理完毕 } int am_main() { am_spi_message_t spi_msg; // 定义一个 SPI 消息结构体 am_spi_msg_init ( &spi_msg, __spi_msg_complete_callback, // 消息处理完成回调函数 NULL); // 未使用回调函数的参数 p_arg,设置为 NULL } Part7 应用实例 MM32L073 通过SPI与SPI Flash通信,对地址为0x0000进行擦写读操作,并将写入数据读出进行校验。 #include "ametal.h" #include "am_board.h" #include "am_vdebug.h" #include "am_delay.h" #include "am_gpio.h" #include "demo_all_entries.h" #include "am_spi.h" #include "am_mm32l073_inst_init.h" #include "mm32l073_pin.h" #define FLASH_PAGE_SIZE 256 // SPI Flsah 页大小定义 #define TEST_ADDR 0x0000 // 测试地址 #define TEST_LEN FLASH_PAGE_SIZE // 测试字节长度 static uint8_t g_tx_buf[TEST_LEN]={0x9F}; // 写数据缓存 static uint8_t g_rx_buf[TEST_LEN]={0}; // 读数据缓存 int am_main (void) { uint32_t ength, i; AM_DBG_INFO("Start up successful! "); am_spi_handle_t spi_handle = am_mm32l073_spi1_int_inst_init(); am_spi_device_t spi_dev; am_spi_mkdev(&spi_dev, spi_handle, 8, // 数据宽度 8-bit AM_SPI_MODE_0, // 模式 0 3000000, // 最大频率 3000000Hz PIOA_4, // 片选 PIOA_4 NULL); // 无自定义函数,设置 NULL am_spi_setup(&spi_dev); // 设置从机设备 spi_flash_erase(&spi_dev, TEST_ADDR);//擦除当前地址中数据 AM_DBG_INFO("FLASH 擦除完成 "); for (i = 0; i < length; i++) // 填充数据 { g_tx_buf = i +1; } spi_flash_write(&spi_dev, TEST_ADDR, length); //写入数据到设定 SPI_FLASH 地址 am_mdelay(10); AM_DBG_INFO("FLASH 数据写入完成 "); for (i = 0; i < length; i++) { g_rx_buf = 0; } spi_flash_read(&spi_dev, TEST_ADDR, length); //从设定的FLASH地址中读取数据 am_mdelay(10); for (i = 0; i < length; i++) // 数据校验 { AM_DBG_INFO(" read %2dst data is : 0x%2x ", i, g_rx_buf); if(g_rx_buf != ((1+ i) & 0xFF)) { AM_DBG_INFO("verify failed! "); while(1); } } } Part8 测试截图 |
||
相关推荐
|
||
只有小组成员才能发言,加入小组>>
2249个成员聚集在这个小组
加入小组灵动微电子MM32全系列MCU产品应用手册,库函数和例程和选型表
11703 浏览 3 评论
【MM32 eMiniBoard试用连载】+基于OLED12864的GUI---U8G2
5930 浏览 1 评论
【MM32 eMiniBoard试用连载】移植RT-Thread至MM32L373PS
10965 浏览 0 评论
【MM32 eMiniBoard测评报告】+ 开箱 + 初探
4577 浏览 1 评论
灵动微课堂(第106讲) | MM32 USB功能学习笔记 —— WinUSB设备
4302 浏览 1 评论
[MM32软件] MM32F002使用内部flash存储数据怎么操作?
977浏览 1评论
805浏览 0评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-27 09:18 , Processed in 0.672072 second(s), Total 67, Slave 49 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号