• nordic的nrf52系列32M速率的SPI-SPIM3


    简介:在nordic的nrf52系列中的nrf52833和nrf52840的SPIM3都是支持最大32M的spi速率,其余的只有8M,当在需要刷屏,或者一些需要高速32M-SPI时,这是一个很好的使用方式,下面我就结合GPIOTE+PPI+SPIM3实现无CPU参与的32M-SPI数据发送:

    测试环境:软件:SDK_17.1,在ble_app_uart例子中添加

    硬件:nrf52832-DK板

    一、引脚选择

    特别注意一点,需要使用32M的SPI时,一定要选择非低频的引脚作为SPI的pin脚,如下面这些引脚就不可以:

    在数据手册中还有这样一条提示,他的意思是在使用32M的时候,可能还要配置为高驱模式,不然可能有数据错乱。

    根据数据手册我当前选择4,5,27,26这4个引脚作为SPI的引脚。

    二、代码编写

    1.源文件添加

    在工程中加入如下几个.c源文件,其中spi_test.c不是官方库文件,是我的SPIM配置代码,这一点注意,你只用添加前面的5个源文件就行

    2、宏定义启用

     这一步主要是启用相关的源文件,如果宏定义不启用刚刚添加的部分源文件是无法添加的,这一步很重要:

     第一步启用SPIM3:

     还要勾选另一个宏定义,在调试过程中发现如果只启动NRFX_SPI_ENABLED,不任意使能它下面的NRFX_SPI0_ENABLED,NRFX_SPI1_ENABLED,NRFX_SPI2_ENABLED的其中一个,居然无法让NRFX_SPIM_ENABLED生效,这一点需要注意一下。

     经过以上的宏定义勾选,SPI的驱动就启动完成了。

    第二步启用PPI:

     到这一步宏定义就都启用完成了,你可以检测一下加入的源文件是否都启用了。

     然后就是代码部分,我已经进行了相关注释,可以直接copy使用。

     三、代码

     1、.c文件

    复制代码
    #include "nrf_drv_spi.h"
    #include "app_util_platform.h"
    #include "nrf_drv_gpiote.h"
    #include "nrf_delay.h"
    #include "boards.h"
    #include "app_error.h"
    #include <string.h>
    
    /*log打印的头文件*/
    #include "nrf_log.h"
    #include "nrf_log_ctrl.h"
    #include "nrf_log_default_backends.h"
    
    
    #include "nrf_drv_ppi.h"
    #include "spi_test.h"
    
    /*触发中断的按键,由于我使用的是DK板,所以直接使用按键4,如果你测试的是自己的板子,可以自己定义pin脚
    #define PIN_IN    BUTTON_4 
    #define leng 10
    #define TEST_STRING "spi_ppi_test"
    /*测试buffer设置*/
    uint8_t TX_buffer[]=TEST_STRING;
    uint8_t RX_buffer[leng];
    
    /*PPI 通道实例变量定义*/
    static nrf_ppi_channel_t     m_ppi_channel;
    
    /*SPIM3可以达到32M*/
    static nrfx_spim_t my_spi=NRFX_SPIM_INSTANCE(3);
    
    /*变量定义,用于确定SPI是否正确传输,每一次都会进行加一,0x31只是一个本次随意设置的初始值*/
    uint8_t test=0x31;
    
    
    /*同时设置TX和RX的buffer*/
    nrfx_spim_xfer_desc_t m_buffer = NRFX_SPIM_XFER_TRX(TX_buffer,sizeof(TX_buffer),RX_buffer,leng);
    /**
     * @brief SPI user event handler.
     * @param event
     */
    void spi_event_handler(nrfx_spim_evt_t const * p_event,
                           void *                  p_context)
    {
            ret_code_t err_code;
        /*传输事件判断*/
            if(NRF_DRV_SPI_EVENT_DONE == p_event->type)
            {
                NRF_LOG_INFO("Transfer completed.")
                TX_buffer[0]=++test;
                /*一次传输完成,提供下一次传输的buffer,如果有大量接收的数据,可以使用两个buffer交替接收,便于流出时间进行数据处理,第二个buffer也是使用 
                nrfx_spim_xfer_desc_t m_buffer = NRFX_SPIM_XFER_TRX(TX_buffer2,sizeof(TX_buffer2),RX_buffer2,leng2);进行定义,如果是只想单独定义一个TX和RX
                请使用 NRFX_SPIM_XFER_TX 或者 NRFX_SPIM_XFER_RX
                */
                err_code = nrfx_spim_xfer(&my_spi,&m_buffer,NRF_DRV_SPI_FLAG_HOLD_XFER);
                APP_ERROR_CHECK(err_code);
            }
            /*如果有接收数据就打印一下,实际应用中可以是其余处理*/
        if (RX_buffer[0] != 0)
        {
            NRF_LOG_INFO(" Received:");
            NRF_LOG_HEXDUMP_INFO(RX_buffer, strlen((const char *)RX_buffer));
        }
    }
    
    void gpiote_init(void)
    {
         ret_code_t err_code;
        /*由于这个例子是基于ble_app_uart,在 主函数中的buttons_leds_init(&erase_bonds);中已经初始化了GPIOTE,所以注释掉,不能重复初始化*/
    //        err_code = nrf_drv_gpiote_init();
    //    APP_ERROR_CHECK(err_code);
        
            nrf_drv_gpiote_in_config_t in_config = NRFX_GPIOTE_CONFIG_IN_SENSE_HITOLO(true);
        in_config.pull = NRF_GPIO_PIN_PULLUP;
        
        /*由于这个例子是基于ble_app_uart,在 主函数中的buttons_leds_init(&erase_bonds);中已经初始化了DK板上的按键4,我要用来做SPI数据触发,所以先解绑这个引脚在配置 */
            nrf_drv_gpiote_in_uninit(PIN_IN);
        /* 中断初始化,回调为NULL,因为是通过PPI触发SPIM所以不需要回调,当然如果有需要你也可以设置 */
            err_code = nrf_drv_gpiote_in_init(PIN_IN, &in_config, NULL);
        APP_ERROR_CHECK(err_code);
        /* GPIO中断事件使能 */
            nrf_drv_gpiote_in_event_enable(PIN_IN, true);
    }
    
    void spim_init(void)
    {
        /*不能使用低频引脚,还要配置为高驱*/
        nrfx_spim_config_t spi_config = NRFX_SPIM_DEFAULT_CONFIG;
        spi_config.ss_pin   = 27;
        spi_config.miso_pin = 4;//如果是刷屏可以不使用MISO数据pin脚,可以设置为 NRFX_SPIM_PIN_NOT_USED
        spi_config.mosi_pin = 5;
        spi_config.sck_pin  = 26;
            spi_config.frequency=NRF_SPIM_FREQ_32M;
        APP_ERROR_CHECK(nrfx_spim_init(&my_spi, &spi_config, spi_event_handler, NULL));
        
        /*高驱配置*/
            nrf_gpio_cfg(27,
                    NRF_GPIO_PIN_DIR_OUTPUT,
                    NRF_GPIO_PIN_INPUT_DISCONNECT,
            NRF_GPIO_PIN_NOPULL,
            NRF_GPIO_PIN_H0H1,
            NRF_GPIO_PIN_NOSENSE);
            nrf_gpio_cfg(4,
                    NRF_GPIO_PIN_DIR_INPUT,
                    NRF_GPIO_PIN_INPUT_CONNECT,
            NRFX_SPIM_MISO_PULL_CFG,
            NRF_GPIO_PIN_H0H1,
            NRF_GPIO_PIN_NOSENSE);
            nrf_gpio_cfg(5,
                    NRF_GPIO_PIN_DIR_OUTPUT,
                    NRF_GPIO_PIN_INPUT_DISCONNECT,
            NRF_GPIO_PIN_NOPULL,
            NRF_GPIO_PIN_H0H1,
            NRF_GPIO_PIN_NOSENSE);
            nrf_gpio_cfg(26,
                    NRF_GPIO_PIN_DIR_OUTPUT,
                    NRF_GPIO_PIN_INPUT_DISCONNECT,
            NRF_GPIO_PIN_NOPULL,
            NRF_GPIO_PIN_H0H1,
            NRF_GPIO_PIN_NOSENSE);
                    
            NRF_LOG_INFO("SPI init end.");
    }
    
    void ppi_init(void)
    {
            ret_code_t err_code;
    
        err_code = nrf_drv_ppi_init();
        APP_ERROR_CHECK(err_code);
        
            /*给spi初始化缓存,并flag选择什么时候启动传输*/
            /*NRF_DRV_SPI_FLAG_HOLD_XFER ---- 占时不启用,可在PPI触发下的SPIM的传输*/
            err_code = nrfx_spim_xfer(&my_spi,&m_buffer,NRF_DRV_SPI_FLAG_HOLD_XFER);
            APP_ERROR_CHECK(err_code);
            
            /* 触发的事件地址*/
            uint32_t gpio_event_addr = nrfx_gpiote_in_event_addr_get(PIN_IN);
            /* 执行的任务地址 */
            uint32_t spim_tick_addr  = nrfx_spim_start_task_get(&my_spi);
        
            /*PPI设置*/
            //把PPI的通道1分配出来
        err_code = nrf_drv_ppi_channel_alloc(&m_ppi_channel);
        APP_ERROR_CHECK(err_code);
            /* 关联事件和任务*/
            err_code = nrf_drv_ppi_channel_assign(m_ppi_channel,
                                              gpio_event_addr,
                                              spim_tick_addr);
            APP_ERROR_CHECK(err_code);
            /* 启动PPI*/
            err_code = nrf_drv_ppi_channel_enable(m_ppi_channel);
        APP_ERROR_CHECK(err_code);
        
    }
    
    void SPIM_test_init(void)
    {
            spim_init();
            gpiote_init();
            ppi_init();
    }
    复制代码

    2、.h文件

    复制代码
    #ifndef SPI_TEST_H__
    #define SPI_TEST_H__
    
    
    void SPIM_test_init(void);
    
    
    #endif
    复制代码

    只要在主函数中引用SPIM_test_init();就可以了。如下是我的逻辑分析仪的采样波形,由于我使用的逻辑分析仪器最大只有100M采样速率,感觉有点不准确,导致有些偏差,还有就是我使用的是杜邦线连接,32M速率下也可能有影响,测试如果是使用杜邦线,尽量短一点,逻辑分析仪采样率尽量高。

     

  • 相关阅读:
    解决LaTeX:!Package CJK Error:Invalid character code报错
    echarts 饼图 环形图 lable添加下划线
    Java11安装
    Vue04/ 计算属性computed 介绍和完整写法 get( ) set( )
    三个目前主流的计算机视觉软件
    keycloak~jwt的rs256签名的验证方式
    STM32---IIC通信协议(含源码,小白进)
    计算机毕业设计JavaWeb闲置服装交易平台(源码+系统+mysql数据库+lw文档)
    批量规范化+代码+Q&A
    leetcode 769. Max Chunks To Make Sorted 最多能完成排序的块(中等)
  • 原文地址:https://www.cnblogs.com/HW-liu/p/17877460.html