IO模拟SPI从机,采用中断方式

1、本示例采用的通信方式为是SPI从机的模式0,如果需要其他模式需要对应的微调代码

2、简易流程示意图:

3、相关接口宏定义,采用直接操作寄存器节省函数调用的时间

#define SPIS_CLK_Port		GPIOB
#define SPIS_CLK_Pin		GPIO_Pin_0

#define SPIS_CS_Port		GPIOB
#define SPIS_CS_Pin			GPIO_Pin_1

#define SPIS_MOSI_Port		GPIOB
#define SPIS_MOSI_Pin		GPIO_Pin_2

#define SPIS_MISO_Port		GPIOB
#define SPIS_MISO_Pin		GPIO_Pin_3

#define SPIS_MISO_1       	SPIS_MISO_Port->BSRR = SPIS_MISO_Pin;            /* MOSI = 1 */
#define SPIS_MISO_0       	SPIS_MISO_Port->BSRR = SPIS_MISO_Pin<<16;        /* MOSI = 0 */

#define SPIS_READ_MOSI    	((SPIS_MOSI_Port->IODR >> 16)&SPIS_MOSI_Pin)    /* 读MOSI口线状态 */
#define SPIS_READ_CS    	((SPIS_CS_Port->IODR >> 16)&SPIS_CS_Pin)    	/* 读CS线状态 */
#define SPIS_READ_CLK    	((SPIS_CLK_Port->IODR >> 16)&SPIS_CLK_Pin)    	/* 读CLK线状态 */

4、中断处理代码,此时CLK及CS中断为同一个中断服务函数

void  EXTI1_IRQHandler(void)
{
	static uint8_t send_old = 0; 
	static uint8_t SEND_F = 0;
	if(GPIO->INTP_TYPE_STA[SPIS_CS_EXTI_Line].INTP_STA&SPIS_CS_EXTI_Pins)//中断管脚判断,直接读取寄存器节约时间
	{
		if(SPIS_READ_CS)		//检测到片选拉高
		{
			//CS拉高,结束数据收发
			spis_recv_data = 0;
			spis_send_data = 0;
			spis_bits_offset = 0;
			//关闭中断
			GPIO->INTP_TYPE_STA[SPIS_CLK_EXTI_Line].INTP_TYPE &= ~(0x03 << (SPIS_CLK_EXTI_offset * 2));
		}
		else
		{
			//发送数据的的第一个bit
			if(!SEND_F)
			{
				/*发送新数据,在模式0时,主机的第一个上升沿就会采样数据,所以在片选信号或者上一包数据通信完成之后就需要马上
				 *将下一帧数据的首bit发送,由于后续可能不会再进行通信,所以需要保存当前数据,在新的一轮通信中继续发送当前帧
				 *数据,所以这里将数据保存到send_old中,如果当前数据没有发送,后续会继续发送该数据
				 */
				send_old = spis_send_data = spis_send_buf[spis_send_offset++];
				spis_send_offset = spis_send_offset == SPIS_FIFO_LEN?0:spis_send_offset;		
			}
			else
			{
				spis_send_data = send_old;
			}
			
			if(spis_send_data&0x80)
			{
				SPIS_MISO_1;
			}
			else
			{
				SPIS_MISO_0;
			}
			spis_send_data <<= 1;
			spis_bits_offset = 0;
			EXTI_LineConfig(SPIS_CLK_EXTI_Line, SPIS_CLK_EXTI_Pins, EXTI_Trigger_Rising_Falling);
			
			GPIO->INTP_TYPE_STA[SPIS_CLK_EXTI_Line].INTP_TYPE |= (0x03 << (SPIS_CLK_EXTI_offset * 2));
		}
		GPIOB->BSRR = GPIO_Pin_4<<16;
	}
	if(GPIO->INTP_TYPE_STA[SPIS_CLK_EXTI_Line].INTP_STA&SPIS_CLK_EXTI_Pins)
	{
		GPIOB->BSRR = GPIO_Pin_5;
		if(!SPIS_READ_CS){		//检测到片选拉低
			SEND_F = 1;	
		
			if(SPIS_READ_CLK)//检测到时钟上升沿的到来	
			{
				//读取数据
				if(SPIS_READ_MOSI)
				{
					spis_recv_data |= 0x01;
				}
				spis_bits_offset++;
				if(spis_bits_offset == 8)//分开处理,避免上升或者下降沿的中断处理耗时
				{
//					GPIOB->BSRR = GPIO_Pin_5<<16;
//					GPIOB->BSRR = GPIO_Pin_5;
					//数据缓冲
					spis_recv_buf[spis_recv_offset++] = spis_recv_data;
					spis_recv_offset = spis_recv_offset == SPIS_FIFO_LEN?0:spis_recv_offset;
					spis_recv_data = 0;
				}
				spis_recv_data <<= 1;	//接收完成之后赋值0,移位不影响
				
			}
			else//检测到时钟下降沿的到来	
			{
				if(spis_bits_offset == 8)
				{
					//输出下一包数据的头,便于后续字节通信的第一个时钟沿主机就能正确获取到数据,与片选信号有效后的第一步操作一样
					send_old = spis_send_data = spis_send_buf[spis_send_offset++];
					spis_send_offset = spis_send_offset == SPIS_FIFO_LEN?0:spis_send_offset;
					//接收完成1包数据
					spis_bits_offset = 0;
					SEND_F = 1;	//一包数据完整发送,开始新一包数据
				}
				else
					SEND_F = 0;//新数据开始发送,后续中断后丢弃该包数据
				if(spis_send_data&0x80)
				{
					SPIS_MISO_1;
				}
				else
				{
					SPIS_MISO_0;
				}
				spis_send_data <<= 1;	
				
			}
		}
		GPIOB->BSRR = GPIO_Pin_5<<16;		
	}
	//printf("EXTI1_GPIO_Status %08X\n", EXTI_GetITLineStatus(EXTI_Line1));
	GPIO->INTP_TYPE_STA[1].INTP_STA = 0xFFFF;
	NVIC_ClearPendingIRQ(EXTI1_IRQn);
}

5、GPIO及中断初始化需要结合对于的芯片平台进行操作,这里就不过多描述

6、注意:本示例只提供了基础的中断处理部分,所有的数据收发均通过对应的缓冲区进行收发,实现从机的基础数据收发,正常应用时需要自行完善相应的软件处理逻辑

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
以下是IO中断模拟IIC从机GD32F130的例程,请参考: ```C++ #include "gd32f1x0.h" #include "stdio.h" #include "systick.h" #define I2C2_SCL_PIN GPIO_PIN_6 /*!< GPIO pin of the I2C clock */ #define I2C2_SCL_GPIO_PORT GPIOB /*!< GPIO port of the I2C clock */ #define I2C2_SDA_PIN GPIO_PIN_7 /*!< GPIO pin of the I2C data */ #define I2C2_SDA_GPIO_PORT GPIOB /*!< GPIO port of the I2C data */ #define I2C2_RCC_CLK_ENABLE() rcu_periph_clock_enable(RCU_GPIOB) /*!< Enable GPIOB clock */ #define I2C2_RCC_CLK_DISABLE() rcu_periph_clock_disable(RCU_GPIOB) /*!< Disable GPIOB clock */ #define I2C2_SCL_HIGH() gpio_bit_set(I2C2_SCL_GPIO_PORT, I2C2_SCL_PIN) /*!< Set GPIOB pin 6 */ #define I2C2_SCL_LOW() gpio_bit_reset(I2C2_SCL_GPIO_PORT, I2C2_SCL_PIN) /*!< Reset GPIOB pin 6 */ #define I2C2_SDA_HIGH() gpio_bit_set(I2C2_SDA_GPIO_PORT, I2C2_SDA_PIN) /*!< Set GPIOB pin 7 */ #define I2C2_SDA_LOW() gpio_bit_reset(I2C2_SDA_GPIO_PORT, I2C2_SDA_PIN) /*!< Reset GPIOB pin 7 */ #define I2C2_SDA_READ() gpio_input_bit_get(I2C2_SDA_GPIO_PORT, I2C2_SDA_PIN) /*!< Read GPIOB pin 7 */ void delay(uint32_t count) { uint32_t index = 0; for(index = count; index != 0; index--) { } } void I2C2_GPIO_Config(void) { rcu_periph_clock_enable(RCU_AF); rcu_periph_clock_enable(RCU_GPIOB); /*configure GPIO pin of I2C2*/ gpio_init(I2C2_SCL_GPIO_PORT, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, I2C2_SCL_PIN); gpio_init(I2C2_SDA_GPIO_PORT, GPIO_MODE_OUT_OD, GPIO_OSPEED_50MHZ, I2C2_SDA_PIN); /*configure I2C2 as a master*/ gpio_init(I2C2_SCL_GPIO_PORT, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, I2C2_SCL_PIN); gpio_init(I2C2_SDA_GPIO_PORT, GPIO_MODE_AF_OD, GPIO_OSPEED_50MHZ, I2C2_SDA_PIN); gpio_pin_remap_config(GPIO_SPI2_REMAP,I2C2_MAP); } void I2C2_Start(void) { I2C2_SDA_HIGH(); I2C2_SCL_HIGH(); delay(5); I2C2_SDA_LOW(); delay(5); I2C2_SCL_LOW(); } void I2C2_Stop(void) { I2C2_SDA_LOW(); I2C2_SCL_HIGH(); delay(5); I2C2_SDA_HIGH(); delay(5); } uint8_t I2C2_WaitAck(void) { uint8_t ucErrTime = 0; I2C2_SDA_HIGH(); delay(1); I2C2_SCL_HIGH(); delay(1); while(I2C2_SDA_READ()) { ucErrTime++; if(ucErrTime > 50) { I2C2_Stop(); return 1; } delay(1); } I2C2_SCL_LOW(); return 0; } void I2C2_Ack(void) { I2C2_SCL_LOW(); I2C2_SDA_LOW(); delay(2); I2C2_SCL_HIGH(); delay(2); I2C2_SCL_LOW(); } void I2C2_NAck(void) { I2C2_SCL_LOW(); I2C2_SDA_HIGH(); delay(2); I2C2_SCL_HIGH(); delay(2); I2C2_SCL_LOW(); } void I2C2_SendByte(uint8_t ucData) { uint8_t ucCount = 0; for(ucCount = 0; ucCount < 8; ucCount++) { if(ucData & 0x80) I2C2_SDA_HIGH(); else I2C2_SDA_LOW(); ucData <<= 1; delay(2); I2C2_SCL_HIGH(); delay(2); I2C2_SCL_LOW(); } } uint8_t I2C2_ReadByte(uint8_t ucAck) { uint8_t ucCount = 0, ucData = 0; I2C2_SDA_HIGH(); for(ucCount = 0; ucCount < 8; ucCount++) { ucData <<= 1; I2C2_SCL_LOW(); delay(2); I2C2_SCL_HIGH(); delay(2); if(I2C2_SDA_READ()) ucData++; } if(!ucAck) I2C2_NAck(); else I2C2_Ack(); return ucData; } void GD32_I2C2_Init(void) { I2C2_GPIO_Config(); } uint8_t GD32_I2C2_ReadByte(uint8_t ucAddr, uint8_t ucReg) { uint8_t ucData = 0; I2C2_Start(); I2C2_SendByte(ucAddr << 1 | 0); if(I2C2_WaitAck()) { I2C2_Stop(); return 0; } I2C2_SendByte(ucReg); I2C2_WaitAck(); I2C2_Start(); I2C2_SendByte(ucAddr << 1 | 1); I2C2_WaitAck(); ucData = I2C2_ReadByte(0); I2C2_Stop(); return ucData; } void GD32_I2C2_WriteByte(uint8_t ucAddr, uint8_t ucReg, uint8_t ucValue) { I2C2_Start(); I2C2_SendByte(ucAddr << 1 | 0); if(I2C2_WaitAck()) { I2C2_Stop(); return; } I2C2_SendByte(ucReg); I2C2_WaitAck(); I2C2_SendByte(ucValue); I2C2_WaitAck(); I2C2_Stop(); } int main(void) { uint8_t ucData = 0; GD32_I2C2_Init(); while(1) { ucData = GD32_I2C2_ReadByte(0x68, 0x75); printf("WHO_AM_I is %X \r\n", ucData); delay_1ms(1000); } } ```

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值