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之间会插入一个系统时钟周期的时间