iot-fan

联系: iotfan123#163.com
注意:
1,本博客之内容来源于网上收集以及相关技术人员提供,如果有侵犯到您的权益,请电邮我沟通;
2,本博客之内容乃分享,交流,学习,研究之目的,作者不对内容的真实性,有效性,及时性负责,也不对因本博客的任何内容导致的任何后果负责;
3,本博客之内容禁止转发到CSDN网站,转到别的网站请保留出处.

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

CH5xx 系列BLE芯片 SPI使用

本文只是对wch 官方例程做的一个补充说明, 以下未特别说明的情况下皆为实际项目验证过的代码

适用芯片

  • CH57x
  • CH58x
  • CH59x
  • CH565/569 (未验证)

SPI master

字节传输(全双工)

__attribute__((section(".highcode"))) //ram code
uint8_t ch57x_hal_spi_transfer(uint8_t data) {
    R8_SPI0_BUFFER = data;
    while( !(R8_SPI0_INT_FLAG & RB_SPI_FREE) );
    return ( R8_SPI0_BUFFER );
}

FIFO mode

阻塞模式

见官方例程

DMA mode

非阻塞+中断

(在ch582上测试,适用于ch58x,ch57x,ch59x)
头文件:

#include "CH58x_common.h"

typedef enum {
    SPI_MASTER_EVENT_DONE,      //< Transfer done.
} spi_master_evt_t;

typedef struct {
    spi_master_evt_t  type;      //< Event type.
} spi_evt_t;

typedef void (* spi_master_evt_handler_t)(spi_evt_t const * p_event);

uint8_t ch57x_hal_spi_dma_init(spi_master_evt_handler_t  handler);

uint8_t ch57x_hal_spi_dma_write(uint8_t *data,uint16_t length);

uint8_t ch57x_hal_spi_dma_read(uint8_t *data,uint16_t length);

c文件:

#include "ch57x_hal_spi_dma.h"

static spi_master_evt_handler_t m_spi_handles = NULL;

uint8_t ch57x_hal_spi_dma_init(spi_master_evt_handler_t  handler) {
    SPI0_MasterDefInit();
    SPI0_CLKCfg(60);
    m_spi_handles = handler;
    //NVIC_EnableIRQ( SPI0_IRQn );
    PFIC_EnableIRQ(SPI0_IRQn);
    return 0;
}

//length should <4096
uint8_t ch57x_hal_spi_dma_write(uint8_t *data,uint16_t length) {
    if(length >4095) {
        return 0xff;
    }
    R32_PA_DIR |= GPIO_Pin_14; //mosio out
    R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; //out
    R16_SPI0_DMA_BEG = ((uint32_t)data)&0xffff;
    R16_SPI0_DMA_END = ((uint32_t)data + length)&0xffff;
    R16_SPI0_TOTAL_CNT = length;
    R8_SPI0_INT_FLAG = RB_SPI_IF_CNT_END | RB_SPI_IF_DMA_END; //clear flag
    R8_SPI0_INTER_EN |= RB_SPI_IE_CNT_END|RB_SPI_IE_DMA_END;  //enable flag
    R8_SPI0_CTRL_CFG |= RB_SPI_DMA_ENABLE;                    //start dma
    return 0;
}

//length should <4096
uint8_t ch57x_hal_spi_dma_read(uint8_t *data,uint16_t length) {
    if(length >4095) {
        return 0xff;
    }
    R32_PA_DIR &= ~GPIO_Pin_14; //mosi input

    R8_SPI0_CTRL_MOD |= RB_SPI_FIFO_DIR; //out
    R16_SPI0_DMA_BEG = ((uint32_t)data)&0xffff;
    R16_SPI0_DMA_END = ((uint32_t)data + length)&0xffff;
    R16_SPI0_TOTAL_CNT = length;

    R8_SPI0_INT_FLAG = RB_SPI_IF_CNT_END | RB_SPI_IF_DMA_END; //clear flag
    R8_SPI0_INTER_EN |= RB_SPI_IE_CNT_END|RB_SPI_IE_DMA_END;  //enable flag
    R8_SPI0_CTRL_CFG |= RB_SPI_DMA_ENABLE;
    return 0;
}


__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((section(".highcode")))
void SPI0_IRQHandler(void){
    if( R8_SPI0_INT_FLAG & RB_SPI_IF_DMA_END ){ ///dma end
        R8_SPI0_CTRL_CFG &= ~RB_SPI_DMA_ENABLE; //dma stop
        R16_SPI0_DMA_BEG = 0;                   //reset dma begin address
        R8_SPI0_INT_FLAG = RB_SPI_IF_DMA_END;   //clear flag
    }
    if( R8_SPI0_INT_FLAG & RB_SPI_IF_CNT_END) { //total cnt transform end
            spi_evt_t evt;
            evt.type = SPI_MASTER_EVENT_DONE;
            m_spi_handles(&evt);
            R8_SPI0_INTER_EN &= ~RB_SPI_IE_CNT_END; //disable dma
            R8_SPI0_INT_FLAG = RB_SPI_IF_CNT_END;   //clear flag
    }
}

使用:

static volatile uint32_t spi_is_going = false;
#define spi_user_cs_low()   GPIOA_ResetBits(GPIO_Pin_12)
#define spi_user_cs_high()  GPIOA_SetBits(GPIO_Pin_12)

__attribute__((section(".highcode")))
void spi_dma_evt_handler(spi_evt_t const * p_event){
    switch(p_event->type){
        case SPI_MASTER_EVENT_DONE:
            spi_is_going = false;
            break;
        default:
            break;
    }
}

void spi0_init(void) {
    //spi0 gpio init
    GPIOA_ModeCfg(GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14, GPIO_ModeOut_PP_5mA);
    GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU);

    ch57x_hal_spi_dma_init(spi_dma_evt_handler);
}

int main() {
    SetSysClock(CLK_SOURCE_PLL_60MHz);
    DebugInit();
    PRINT("Start @ChipID=%02X\n", R8_CHIP_ID);
    spi0_init();
    uint8_t tx_data[4095];
    uint8_t rx_data[4095];

    for(uint32_t i=0;i<sizeof(tx_data);i++) {
        tx_data[i] = i&0xff;
    }
    PRINT("spi start write and read\r\n");
    while(1) {
        spi_user_cs_low();
        spi_is_going = true;
        ch57x_hal_spi_dma_write(tx_data,16);
        while(spi_is_going);
        spi_user_cs_high();

        spi_user_cs_low();
        spi_is_going = true;
        ch57x_hal_spi_dma_read(rx_data,8);
        while(spi_is_going);
        spi_user_cs_high();
        DelayMs(100);
    }
}

详细代码,见附件

SPI slave

中断模式

  • 下面是全双工模式

void ch5xx_spi_slave_init(void)
{
    PRINT( "ch5xx_spi_slave_init\n");

    /**  slave(ch59x)--------- master
     *   cs   (pa12) --------- m_cs 
     *   clk  (pa13) --------- m_clk
     *   mosi (pa14) --------- mosi
     *   miso (pa15) --------- miso 
     */
    GPIOA_ModeCfg(GPIO_Pin_12|GPIO_Pin_13|GPIO_Pin_14|GPIO_Pin_15, GPIO_ModeIN_PU);

    SPI0_SlaveInit();
    R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR;   // FIFO OUT
    R8_SPI0_CTRL_MOD |= RB_SPI_SLV_CMD_MOD; // CMD MODE
    SPI0_ITCfg(ENABLE,RB_SPI_IE_BYTE_END);  // byte interrupt

    PFIC_EnableIRQ( SPI0_IRQn );

    R8_SPI0_SLAVE_PRE = 0x11;               //TEST PRE
}

__attribute__((interrupt("WCH-Interrupt-fast")))
__attribute__((section(".highcode")))
void SPI0_IRQHandler (void)
{
    if(R8_SPI0_INT_FLAG & RB_SPI_IF_BYTE_END ){      //byte end
        if(R8_SPI0_RUN_FLAG & RB_SPI_SLV_CMD_ACT ) { //first byte
            rx_index = 0;
            R8_SPI0_FIFO = 0x80; //test ack 0x80
            rx_buf[rx_index] = R8_SPI0_BUFFER;
        }else {
            R8_SPI0_FIFO = 0x40; //test ack 0x40
            rx_buf[rx_index] = R8_SPI0_BUFFER;
        }
        rx_index++;
    }
    R8_SPI0_INT_FLAG = 0xff;
}

注意事项

  • master模式下,MOSI的默认电平受到MISO影响,如果用来驱动诸如WS2812,需要把MISO悬空并且设置为低电平输出
  • master模式下,spi传输时候,两个byte之间会插入一个系统时钟周期的时间
posted on 2020-09-14 10:21  iot-fan  阅读(2450)  评论(3编辑  收藏  举报