CH58x/59x SPI0代码参考
1.CH58x/59x SPI0讲解
2.CH58x/59x SPI0代码参考
前言:
代码参考为首字节模式和数据流模式,均使用DMA,建议使用数据流DMA。
一、数据流/首字节收发代码参考
数据流流程:
- 主机定时器1ms间隔发送;
- 从机接收数据;
- 从机填入发送数据到DMA并通知主机接收;
- 主机DMA接收数据;
首字节流程:
- 主机定时器1ms间隔发送;
- 从机首字节接收并DMA接收完整数据;
- 从机DMA发送数据并通知主机接收;
- 主机DMA接收数据;
注意:PA4作为从机通知主机提供clk波形功能,因此需接线主从机的PA4,可以修改为其他GPIO。
主机收发代码:
#include "CH59x_common.h" __attribute__((aligned(4))) UINT8 tx_buff[32] ={0x1,0x55,0x2,0x55,0x3,0x55,0x4,0x55,0x5,0x55, 0x1,0x55,0x2,0x55,0x3,0x55,0x4,0x55,0x5,0x55, 0x1,0x55,0x2,0x55,0x3,0x55,0x4,0x55,0x5,0x55, 0x1,0x55}; __attribute__((aligned(4))) UINT8 rx_buff[32]; uint8_t tx_len[60] = {0}; volatile uint8_t buf_judge[10]; void DebugInit(void){ GPIOA_SetBits(GPIO_Pin_9); GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); UART1_DefInit(); UART1_BaudRateCfg(1500000); } VOID SPI_Init(){ GPIOA_ModeCfg(GPIO_Pin_4, GPIO_ModeIN_PU);/*用于从机通知主机提供时钟,从机准备发送数据*/ GPIOA_ITModeCfg(GPIO_Pin_4, GPIO_ITMode_FallEdge); PFIC_EnableIRQ(GPIO_A_IRQn); GPIOA_SetBits(GPIO_Pin_12); GPIOA_ModeCfg(GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14, GPIO_ModeOut_PP_5mA); GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeIN_PU); SPI0_MasterDefInit(); SPI0_CLKCfg(4); PFIC_EnableIRQ(SPI0_IRQn); tx_len[0] = sizeof(tx_buff); printf("tx_playload[0]:%d\n", tx_len[0]); for (uint8_t i=0; i<sizeof(tx_buff); i++){ tx_len[1+i] = tx_buff[i]; } PRINT("SPI0 Master Init\n"); } int main(){ SetSysClock(CLK_SOURCE_PLL_60MHz); DebugInit();/* 配置串口调试 */ PRINT("Start @ChipID=%02X\n", R8_CHIP_ID); SPI_Init(); TMR0_TimerInit(FREQ_SYS / 1000); // 设置定时时间 1ms TMR0_ITCfg(ENABLE, TMR0_3_IT_CYC_END); // 开启中断 PFIC_EnableIRQ(TMR0_IRQn); while(1){ if(buf_judge[0] != 0){ printf("%x %x\n",buf_judge[0], buf_judge[1]); buf_judge[0] = 0; } } } __INTERRUPT __HIGH_CODE void SPI0_IRQHandler(void){ if(R8_SPI0_INT_FLAG & RB_SPI_IF_DMA_END) { if(R8_SPI0_CTRL_MOD&RB_SPI_FIFO_DIR) { GPIOA_SetBits(GPIO_Pin_12); buf_judge[0] = rx_buff[30]; buf_judge[1] = rx_buff[31]; R8_SPI0_CTRL_CFG &= ~RB_SPI_DMA_ENABLE; R16_SPI0_DMA_BEG = 0x3300; //更改DMA地址,出中断 R8_SPI0_INT_FLAG |= RB_SPI_IF_DMA_END|RB_SPI_IF_CNT_END; R8_SPI0_INTER_EN &=~ (RB_SPI_IE_DMA_END|RB_SPI_IE_CNT_END); R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; } if(!(R8_SPI0_CTRL_MOD&RB_SPI_FIFO_DIR))//主机发送 { R8_SPI0_INT_FLAG |= RB_SPI_IF_DMA_END; } } if((R8_SPI0_INT_FLAG & RB_SPI_IF_CNT_END)&&(!(R8_SPI0_CTRL_MOD&RB_SPI_FIFO_DIR))) { GPIOA_SetBits(GPIO_Pin_12); R8_SPI0_CTRL_CFG &= ~RB_SPI_DMA_ENABLE; R16_SPI0_DMA_BEG = 0x4300; //更改DMA地址,出中断 R8_SPI0_INT_FLAG |= RB_SPI_IF_DMA_END|RB_SPI_IF_CNT_END; R8_SPI0_CTRL_MOD |= RB_SPI_FIFO_DIR; R8_SPI0_INTER_EN &=~ (RB_SPI_IE_DMA_END|RB_SPI_IE_CNT_END); } } __INTERRUPT __HIGH_CODE void GPIOA_IRQHandler(void){ GPIOA_ClearITFlagBit(GPIO_Pin_4); GPIOA_ResetBits(GPIO_Pin_12); R8_SPI0_CTRL_MOD |= RB_SPI_FIFO_DIR; R16_SPI0_DMA_BEG = ((uint32_t)rx_buff);//从机先填DMA,主机再接收 R16_SPI0_DMA_END = ((uint32_t)rx_buff+32); R16_SPI0_TOTAL_CNT = 32; R8_SPI0_INTER_EN |= RB_SPI_IE_DMA_END; R8_SPI0_CTRL_CFG |= RB_SPI_DMA_ENABLE; } __INTERRUPT __HIGH_CODE void TMR0_IRQHandler(void) // TMR0 定时中断 { if(TMR0_GetITFlag(TMR0_3_IT_CYC_END)) { TMR0_ClearITFlag(TMR0_3_IT_CYC_END); // 清除中断标志 GPIOA_ResetBits(GPIO_Pin_12); R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; R16_SPI0_DMA_BEG = (uint32_t)tx_buff; R16_SPI0_DMA_END = (uint32_t)(tx_buff+32); R16_SPI0_TOTAL_CNT = 32; R8_SPI0_INTER_EN |= RB_SPI_IE_DMA_END; R8_SPI0_CTRL_CFG |= RB_SPI_DMA_ENABLE; } }
从机收发代码:
#include "CH59x_common.h" __attribute__((aligned(4))) UINT8 tx_buff[32]={0x1,0xaa,0x2,0xaa,0x3,0xaa,0x4,0xaa,0x5,0xaa, 0x1,0xaa,0x2,0xaa,0x3,0xaa,0x4,0xaa,0x5,0xaa, 0x1,0xaa,0x2,0xaa,0x3,0xaa,0x4,0xaa,0x5,0xaa, 0x1,0xaa,}; __attribute__((aligned(4))) UINT8 rx_buff[32]; volatile uint8_t buf_judge[10]; volatile uint8_t len = 0; void DebugInit(void){ GPIOA_SetBits(GPIO_Pin_9); GPIOA_ModeCfg(GPIO_Pin_8, GPIO_ModeIN_PU); GPIOA_ModeCfg(GPIO_Pin_9, GPIO_ModeOut_PP_5mA); UART1_DefInit(); UART1_BaudRateCfg(1500000); } VOID SPI_Init(){ /* 设备模式 */ GPIOA_SetBits(GPIO_Pin_4);//PA4用于触发主机中断 GPIOA_ModeCfg(GPIO_Pin_4, GPIO_ModeOut_PP_5mA); GPIOA_ModeCfg(GPIO_Pin_12 | GPIO_Pin_13 | GPIO_Pin_14, GPIO_ModeIN_PU);/*SPI0 GPIOInit*/ GPIOA_ModeCfg(GPIO_Pin_14, GPIO_ModeIN_PU); GPIOA_SetBits(GPIO_Pin_15); GPIOA_ModeCfg(GPIO_Pin_15, GPIO_ModeOut_PP_5mA); R8_SPI0_CTRL_MOD = RB_SPI_ALL_CLEAR; R8_SPI0_CTRL_MOD = RB_SPI_MODE_SLAVE; R8_SPI0_CTRL_MOD |= RB_SPI_FIFO_DIR; //输入模式,接收数据 R8_SPI0_CTRL_MOD &= ~(RB_SPI_MOSI_OE | RB_SPI_SCK_OE); R8_SPI0_CTRL_MOD |= RB_SPI_MISO_OE ; R16_SPI0_DMA_BEG = (UINT32)(rx_buff); R16_SPI0_DMA_END = (UINT32)((rx_buff)+32); R16_SPI0_TOTAL_CNT = 32; R8_SPI0_CTRL_CFG |= RB_SPI_DMA_ENABLE; R8_SPI0_INTER_EN |= RB_SPI_IE_DMA_END; PFIC_EnableIRQ(SPI0_IRQn); PRINT("SPI0 Slave Init\n"); } int main(){ SetSysClock(CLK_SOURCE_PLL_60MHz); DebugInit(); PRINT("Start @ChipID=%02X\n", R8_CHIP_ID); SPI_Init(); while(1){ if(buf_judge[0] != 0){ printf("%x %x\n", buf_judge[0], buf_judge[1]); buf_judge[0] = 0; } } } __attribute__((interrupt("WCH-Interrupt-fast"))) __attribute__((section(".highcode"))) void SPI0_IRQHandler(void){ if((R8_SPI0_INT_FLAG & RB_SPI_IF_DMA_END)&&(R8_SPI0_CTRL_MOD&RB_SPI_FIFO_DIR)){ buf_judge[0] = rx_buff[30]; buf_judge[1] = rx_buff[31]; R8_SPI0_CTRL_MOD &= ~RB_SPI_FIFO_DIR; //从机接收到数据后,切换方向为发送 R16_SPI0_DMA_BEG = (UINT32)(tx_buff); //从机先填DMA,主机再接收 R16_SPI0_DMA_END = (UINT32)((tx_buff)+32); R16_SPI0_TOTAL_CNT = 32; //从机模式下,由主机决定何时取走 R8_SPI0_INT_FLAG |= RB_SPI_IF_DMA_END;//xxx 不要进中断立马清除标志 GPIOA_ResetBits(GPIO_Pin_4);//xxx 从机发送中断通知脚。从机先填DMA,主机再接收 GPIOA_SetBits(GPIO_Pin_4); } if((R8_SPI0_INT_FLAG & RB_SPI_IF_CNT_END)&&(!(R8_SPI0_CTRL_MOD&RB_SPI_FIFO_DIR))){ R8_SPI0_CTRL_MOD |= RB_SPI_FIFO_DIR; //从机发送数据完成后,切换方向为接收 R16_SPI0_DMA_BEG = (UINT32)(rx_buff); R16_SPI0_DMA_END = (UINT32)((rx_buff)+32); R16_SPI0_TOTAL_CNT = 32; R8_SPI0_INT_FLAG |= RB_SPI_IF_CNT_END; } }
二、波形参考:
三、注意事项
- master模式下,MOSI的默认电平受到MISO影响,如果用来驱动诸如WS2812,需要把MISO悬空并且设置为低电平输出;
- master模式下,spi传输时候,两个byte之间会插入一个系统时钟周期的时间。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 周边上新:园子的第一款马克杯温暖上架
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· 使用C#创建一个MCP客户端
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!