GD32F30x SPI转CAN 从机实现

SPI 转CAN 目前常用MCP251x 年代较远,目前多数MCU 自带CAN 模块和SPI 主从模块,很方便实现SPI 转CAN 串口等外设,可用作linux 主机下的一个驱动外设;

SPI通信不同于串口等全双工通信,主机在发送数据的同时也能得到从机传送的数据,所以SPI通信是同步的,从机要想发送数据给主机,必须等主机主动发送时钟来读取;所以主机要想读取从机的数据必须发送 2帧数据才能读取到想要的结果(不同与FPGA 内部状态机一帧数据实现读写,ARM模拟SPI从机实现高速通信需要2帧,并且2帧中间需要等待大于一个SCK 时钟周期)。

cpp 复制代码
#include "bsp_spi.h"
#include "tty_com.h"
#include "fifo_t.h"

static uint8_t _spi0_rxbuf[_SPI0_DMA_RX_BUFFER_SIZE];
__IO struct _SPI_CAN_ST spi0_canMsg={
	.this_base = (uint32_t*)&spi0_canMsg,/*myself addr base*/
	.__uuid={1,2,3,4},/*is id message*/
	._can0_phy_base = CAN0, /* can0 base*/
	.loop_can0Rx_buf_base =(uint32_t*)_spi0_rxbuf,/*can0 rx buffer,(4byte ID) +(8byte data)*/
	.loop_can0Tx_buf_base =(uint32_t*)_spi0_rxbuf,/*can0 tx buffer, */
	._can1_phy_base = 0, /* not use can1*/
	.loop_can1Rx_buf_base =(uint32_t*)_spi0_rxbuf,/*can0 rx buffer,(4byte ID) +(8byte data)*/
	.loop_can1Tx_buf_base =(uint32_t*)_spi0_rxbuf,/*can0 tx buffer, */

	._can0_1_state =0, /*>0 ,io =0*/
	._can0_tx_rx_size =0,/*read only*/
	._can1_tx_rx_size =0,/*read only*/
};
fifoRst _can0_rx_fifo={0};
fifoRst _can0_tx_fifo={0};
fifoRst _can1_rx_fifo={0};
fifoRst _can1_tx_fifo={0};
static uint32_t can0_ctrl=0;
// X^8+X^2+X^1+1 
static const unsigned char crc_table[] ={
	0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
	0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
	0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
	0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
	0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
	0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
	0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
	0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
	0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
	0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
	0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
	0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
	0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
	0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
	0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
	0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
};
uint8_t get_crc8(uint8_t *ptr, uint16_t len){
    uint8_t  crc = 0x07;
    while (len--){
        crc = crc_table[crc ^ *ptr++];
    }
    return (crc);
}

void _spi_salve_init(void){
	uint32_t *ids =(uint32_t*)0x1FFFF7E8;/*SOC 96bit ID*/
	spi0_canMsg.__uuid[0] = _DEV_INFO_ID;
	for(int8_t i=1;i<4;i++){
		spi0_canMsg.__uuid[i] = *ids++;
	}
	ids = (uint32_t*)&spi0_canMsg;
	for(int j =0;j<sizeof(spi0_canMsg)/4;j++){
		tty_serial_printf("%d: %08x \r\n",j,*ids++);
	}
	/*init fifo rxd base*/
	QueueInit(&_can0_rx_fifo);
	QueueInit(&_can1_rx_fifo);
	QueueInit(&_can0_tx_fifo);
	QueueInit(&_can1_tx_fifo);
	spi0_canMsg.loop_can0Rx_buf_base =(uint32_t*)get_next_read_ops(&_can0_rx_fifo);
	spi0_canMsg.loop_can1Rx_buf_base =(uint32_t*)get_next_read_ops(&_can1_rx_fifo);

	rcu_periph_clock_enable(RCU_GPIOA);//RCU_CAN0
	rcu_periph_clock_enable(RCU_AF);
	rcu_periph_clock_enable(RCU_SPI0);
	
	gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_MAX, GPIO_PIN_4);/*NSS*/
	gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_MAX, GPIO_PIN_5);/*SCK*/
	gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_6);/*MISO*/
	gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_MAX, GPIO_PIN_7);/*MOSI*/

	gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_8);/*INT*/
	_SPI_CAN_INT_GPIO(1); /*io =1, int =0*/

	// 120Mhz /2 =   60 Mhz max input SCK
	rcu_periph_clock_enable(RCU_DMA0);
	
	dma_deinit(DMA0,DMA_CH1);/*SPI0_RX*/
	dma_parameter_struct spi_dma;	
	dma_struct_para_init(&spi_dma);
	spi_dma.periph_addr =(uint32_t)&SPI_DATA(SPI0);
	spi_dma.periph_width =DMA_PERIPHERAL_WIDTH_8BIT;
	spi_dma.periph_inc =DMA_PERIPH_INCREASE_DISABLE;
	spi_dma.memory_addr =(uint32_t)_spi0_rxbuf;
	spi_dma.memory_width =DMA_MEMORY_WIDTH_8BIT;
	spi_dma.memory_inc =DMA_MEMORY_INCREASE_ENABLE;
	spi_dma.direction =DMA_PERIPHERAL_TO_MEMORY;/*spi-> mem*/
	spi_dma.number =_SPI0_DMA_RX_BUFFER_SIZE;
	spi_dma.priority =DMA_PRIORITY_HIGH;
	dma_init(DMA0,DMA_CH1,&spi_dma);
	dma_circulation_enable(DMA0,DMA_CH1);
	dma_memory_to_memory_disable(DMA0,DMA_CH1);
	
	dma_deinit(DMA0,DMA_CH2);/*SPI0_TX*/
	dma_struct_para_init(&spi_dma);
	spi_dma.periph_addr =(uint32_t)&SPI_DATA(SPI0);
	spi_dma.periph_width =DMA_PERIPHERAL_WIDTH_8BIT;
	spi_dma.periph_inc =DMA_PERIPH_INCREASE_DISABLE;
	spi_dma.memory_addr =(uint32_t)_spi0_rxbuf;
	spi_dma.memory_width =DMA_MEMORY_WIDTH_8BIT;
	spi_dma.memory_inc =DMA_MEMORY_INCREASE_ENABLE;
	spi_dma.direction =DMA_MEMORY_TO_PERIPHERAL;/*mem -> spi*/
	spi_dma.number =_SPI0_DMA_RX_BUFFER_SIZE;
	spi_dma.priority =DMA_PRIORITY_HIGH;
	dma_init(DMA0,DMA_CH2,&spi_dma);
	dma_circulation_enable(DMA0,DMA_CH2);
	dma_memory_to_memory_disable(DMA0,DMA_CH2);
	
	// SPI_CTL0(SPI0) =(1UL<<13); /*CRC calc enable default ?*/
	SPI_CTL1(SPI0) =(1UL<<1)|(1UL<<0);/*tx .rx dma enable*/
	SPI_RCRC(SPI0) =7U;
	SPI_CTL0(SPI0)|=(1UL<<6);/*SPI enable*/
	
	gpio_exti_source_select(GPIO_PORT_SOURCE_GPIOA,GPIO_PIN_SOURCE_4);/**/
	exti_init(EXTI_4,EXTI_INTERRUPT,EXTI_TRIG_RISING);
	exti_interrupt_flag_clear(EXTI_4);
	nvic_irq_enable(EXTI4_IRQn,5,0);

	DMA_CHCTL(DMA0,DMA_CH2) |= DMA_CHXCTL_CHEN;
	DMA_CHCTL(DMA0,DMA_CH1) |= DMA_CHXCTL_CHEN;

	_spi0_send((uint8_t*)&spi0_canMsg,sizeof(spi0_canMsg));/*wait master read*/

	/*can init*/
	rcu_periph_clock_enable(RCU_CAN0);
	rcu_periph_clock_enable(RCU_GPIOB);
	rcu_periph_clock_enable(RCU_AF);
	gpio_pin_remap_config(GPIO_CAN_PARTIAL_REMAP, ENABLE);
	gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_MAX, GPIO_PIN_9);
	gpio_init(GPIOB, GPIO_MODE_IPU, GPIO_OSPEED_MAX, GPIO_PIN_8);
	can_deinit(CAN0);
	nvic_irq_enable(USBD_LP_CAN0_RX0_IRQn, 5, 1);
	can0_ctrl=CAN_CTL(CAN0);
}
/*wring create*/
inline uint8_t _inside_can0_fifo_wring_gen(fifoRst *can0tx,fifoRst *can0rx){
	spi0_canMsg._can0_tx_rx_size = ((uint32_t)can0tx->pack_count<<16)+can0rx->pack_count;
	int8_t res = (can0tx->pack_count>=_FIFO_PACK_SIZE)?1:0;
	res |= (can0rx->pack_count>0)?2:0;
	return res;
}

inline uint8_t _inside_can1_fifo_wring_gen(fifoRst *can1tx,fifoRst *can1rx){
	spi0_canMsg._can1_tx_rx_size = ((uint32_t)can1tx->pack_count<<16)+can1rx->pack_count;
	uint8_t res = (can1tx->pack_count>=_FIFO_PACK_SIZE)?4:0;
	res |= (can1rx->pack_count>0)?8:0;
	return res;
}
#if _DEBUG_PRINT_ENABLE
uint16_t rxsize_spi = 0;
#endif
void _canx_cmd_proc(void){
	uint8_t res = _inside_can0_fifo_wring_gen(&_can0_tx_fifo,&_can0_rx_fifo);
	res|= _inside_can1_fifo_wring_gen(&_can1_tx_fifo,&_can1_rx_fifo);
	spi0_canMsg._can0_1_state =res;
	_SPI_CAN_INT_GPIO(spi0_canMsg._can0_1_state>0); /*int state*/
	/*can send */
	int8_t j=26;
	if(_can0_tx_fifo.pack_count>0){
		for(j=26;j<29;j++){
			if(CAN_TSTAT(CAN0)&(1UL<<j)){/*fifo x empty */
				memcpy((void*)&CAN_TMI(CAN0,26-j),(void*)get_next_read_ops(&_can0_tx_fifo),_FIFO_DATA_SIZE);
				CAN_TMI(CAN0,26-j) |= 0x1;/*enable can tx*/
			}
		}
	}
	if(can0_ctrl!=CAN_CTL(CAN0)){/*clear fifo*/
		can0_ctrl=CAN_CTL(CAN0);
		if(can0_ctrl&0x1){/*can enable ,fifo clear*/
			QueueInit(&_can0_rx_fifo);
			QueueInit(&_can1_rx_fifo);
			QueueInit(&_can0_tx_fifo);
			QueueInit(&_can1_tx_fifo);
		}
	}
	// if(_can1_tx_fifo.pack_count>0){
	// 	for(j=26;j<29;j++){
	// 		if(CAN_TSTAT(CAN0)&(1UL<<j)){/*fifo x empty */
	// 			memcpy((void*)&CAN_TMI(CAN1,26-j),(void*)get_next_read_ops(&_can1_tx_fifo),_FIFO_DATA_SIZE);
	// 			CAN_TMI(CAN0,26-j) |= 0x1;/*enable can tx*/
	// 		}
	// 	}
	// }
#if _DEBUG_PRINT_ENABLE
	if(rxsize_spi>0){
		tty_serial_printf("\r\nrxlen = %d,",rxsize_spi);
		for(int i=0;i<rxsize_spi;i++){
			tty_serial_printf("%02x ",_spi0_rxbuf[i]);
		}
		rxsize_spi =0;
	}
#endif
}

#pragma arm section code ="RAMCODE"
void EXTI4_IRQHandler(void){
	EXTI_PD = EXTI_4;/*clear int flag*/
	struct _spi_can_rx_frame *pdata = (struct _spi_can_rx_frame*)_spi0_rxbuf;
	uint16_t spi0RecvSize =_SPI0_DMA_RX_BUFFER_SIZE - DMA_CHCNT(DMA0,DMA_CH1);
#if _DEBUG_PRINT_ENABLE
	rxsize_spi =spi0RecvSize;
#endif
	_reload_spi0_dmaRx_size(_SPI0_DMA_RX_BUFFER_SIZE);
	uint8_t *data_tx_base = (uint8_t*)&spi0_canMsg;
	uint16_t tx_len = sizeof(spi0_canMsg); 
	int8_t crc_enable =0;
	if(SPI_CTL0(SPI0) &0x2000){/*crc enable?*/
		if(SPI_RCRC(SPI0)!=(pdata->cmd_head&0xff))crc_enable=-1;
		_RESET_SPI_CRC();
	}
	if(spi0RecvSize<10)crc_enable=-1;/*至少10 byte*/
	if((crc_enable==0)&&(pdata->ops_addr>=0x20000000)){/*inside addr Almost completely open*/
		switch(pdata->cmd_head&0xffffff00){
			case _READ_CMD:
				data_tx_base = (uint8_t *)pdata->ops_addr;
				tx_len = pdata->sizedata[0];
			break;
			case _WRTIE_CMD:
				memcpy((uint8_t *)pdata->ops_addr,(uint8_t *)&pdata->sizedata[1],pdata->sizedata[0]);
			break;
			case _READ_DATA:
				if(pdata->ops_addr==spi0_canMsg.this_base[7]){/*read fifo mem*/
					data_tx_base = (uint8_t *)get_next_read_ops(&_can0_rx_fifo);
					tx_len = _FIFO_DATA_SIZE;
				}else if(pdata->ops_addr==spi0_canMsg.this_base[10]){
					data_tx_base = (uint8_t *)get_next_read_ops(&_can1_rx_fifo);
					tx_len = _FIFO_DATA_SIZE;
				}else{
					data_tx_base = (uint8_t *)pdata->ops_addr;
					tx_len = pdata->sizedata[0];
				}
			break;
			case _WRTIE_DATA:
				if(pdata->ops_addr==spi0_canMsg.this_base[8]){/*write fifo mem*/
					copy_data_to_ops(&_can0_tx_fifo,(uint8_t *)&pdata->sizedata[1],_FIFO_DATA_SIZE);
				}else if(pdata->ops_addr==spi0_canMsg.this_base[11]){
					copy_data_to_ops(&_can1_tx_fifo,(uint8_t *)&pdata->sizedata[1],_FIFO_DATA_SIZE);
				}else{
					memcpy((uint8_t *)pdata->ops_addr,(uint8_t *)&pdata->sizedata[1],pdata->sizedata[0]);
				}
			break;
			default :break;
		}
	}
	_spi0_send(data_tx_base,tx_len);/*wait master next read*/	
}
/*4byte id + 2byte timestamp + 2byte data size + 8byte data */
void USBD_LP_CAN0_RX0_IRQHandler(void){
	while(CAN_RFIFO0(CAN0)&3){
		copy_data_to_ops(&_can0_rx_fifo,(void*)&CAN_RFIFOMI(CAN0,0),_FIFO_DATA_SIZE);
		CAN_RFIFO0(CAN0) |=(1UL<<5);/*res*/
	}
	while(CAN_RFIFO1(CAN0)&3){
		copy_data_to_ops(&_can0_rx_fifo,(void*)&CAN_RFIFOMI(CAN0,1),_FIFO_DATA_SIZE);
		CAN_RFIFO1(CAN0) |=(1UL<<5);/*res*/
	}
}
#pragma arm section
cpp 复制代码
#ifndef			_BSP_SPI_H_
#define			_BSP_SPI_H_			1

#include "gd32f30x.h"
#include <stdio.h>
#include "systick.h"
#include "string.h"
#include "fifo_t.h"

#pragma pack(4)
struct _SPI_CAN_ST{
	uint32_t *this_base;
	uint32_t __uuid[4];
	uint32_t *inside_buffer_base;
	uint32_t _can0_phy_base;
	uint32_t *loop_can0Rx_buf_base;
	uint32_t *loop_can0Tx_buf_base;
	uint32_t _can1_phy_base;
	uint32_t *loop_can1Rx_buf_base;
	uint32_t *loop_can1Tx_buf_base;
	uint32_t _can0_1_state;
	uint32_t _can0_tx_rx_size;/*bit[31:16]= tx size ,bit[15:0]= rx size*/
	uint32_t _can1_tx_rx_size;
};
struct _spi_can_rx_frame{
	uint32_t cmd_head;
	uint32_t ops_addr;
	uint16_t sizedata[2];
	uint32_t data[63];
};

#pragma pack()
#define	 _spi0_send(pdata,size){\
	DMA_CHCTL(DMA0,DMA_CH2) &=~DMA_CHXCTL_CHEN;\
	SPI_DATA(SPI0) = *pdata;\
	if(size>0){\
		DMA_CHMADDR(DMA0,DMA_CH2) = (uint32_t)pdata+1;\
		DMA_CHCNT(DMA0,DMA_CH2) = size;\
		DMA_CHCTL(DMA0,DMA_CH2) |= DMA_CHXCTL_CHEN;\
	}\
}

#define	_reload_spi0_dmaRx_size(size){\
	DMA_CHCTL(DMA0,DMA_CH1) &= ~DMA_CHXCTL_CHEN;\
	DMA_CHCNT(DMA0,DMA_CH1) = size;\
	DMA_CHCTL(DMA0,DMA_CH1) |= DMA_CHXCTL_CHEN;\
}

#define	_RESET_SPI_CRC(){\
	SPI_CTL0(SPI0)  =0x40;\
	SPI_CTL0(SPI0)  =0x2040;\
}

#define		_SPI_CAN_INT_GPIO(x){\
	if(x){\
		GPIO_BOP(GPIOA) =GPIO_PIN_8;\
	}else{\
		GPIO_BC(GPIOA) =GPIO_PIN_8;\
	}\
}

#define			_DEV_INFO_ID						0x04cf5fff /*dev ID*/
#define			_READ_CMD							0xfd550300 /*master read cmd*/
#define			_WRTIE_CMD							0xf5dd0200 /*master write cmd*/
#define			_READ_DATA							0x80550300 /*master read data*/
#define			_WRTIE_DATA							0x80dd0200 /*master write data*/

extern void _spi_salve_init(void);
extern void _canx_cmd_proc(void);
#define		_SPI0_DMA_RX_BUFFER_SIZE			4096
#endif

该代码模拟了一个SPI 从机功能,可以通过SPI 读取或者写入MCU > 0x20000000 地址。

相关推荐
apple_ttt15 小时前
SystemVerilog学习——虚拟接口(Virtual Interface)
fpga开发·fpga·systemverilog·uvm
学习路上_write1 天前
FPGA/Verilog,Quartus环境下if-else语句和case语句RT视图对比/学习记录
单片机·嵌入式硬件·qt·学习·fpga开发·github·硬件工程
jjjxxxhhh1231 天前
FPGA,使用场景,相比于单片机的优势
单片机·嵌入式硬件·fpga开发
诚实可靠小郎君95271 天前
FPGA高速设计之Aurora64B/66B的应用与不足的修正
fpga开发·aurora·高速通信
百锦再1 天前
基于Zynq FPGA对雷龙SD NAND的测试
fpga开发
∑狸猫不是猫2 天前
HDLBIts习题(4):边沿检测、分频计数器、多位BCD计数器
fpga开发
黑旋风大李逵2 天前
FPGA使用Verilog实现CAN通信
fpga开发·can通信·sja1000t·fpga实现can通信
hi942 天前
PYNQ 框架 - 中断(INTR)驱动
嵌入式硬件·fpga开发·zynq·pynq
transfer_ICer3 天前
Vscode搭建verilog开发环境
vscode·fpga开发·编辑器
沐欣工作室_lvyiyi4 天前
汽车牌照识别系统的设计与仿真(论文+源码)
人工智能·单片机·fpga开发·汽车·单片机毕业设计·matlab车牌识别