普冉MCU单片机 PY32 作为I2C从机实现不固定长度收发数据的实现方式(基于HAL库例程实现)【STM32等MCU可参照实现】

        在使用I2C外设HAL库或者LL库例程作为从机时进行通信时,如果我们使用发送/接收库函数对从机进行初始化,就要按照官方例程这样,先写一个接收初始化函数,并设定接收字节长度,接收buffer,查询从机I2C状态后,再执行一次发送初始化函数。

        如果我们非常清楚主机的发送时序,这样没有任何问题。但如果我们不知道主机何时开始发送,何时开始接收,也不知道主机会发送/接收多少个byte长度,这样的写法就会有问题。        

  /* I2C初始化 */
  I2cHandle.Instance             = I2C;                       /* I2C */
  I2cHandle.Init.ClockSpeed      = I2C_SPEEDCLOCK;            /* I2C通讯速度 */
  I2cHandle.Init.DutyCycle       = I2C_DUTYCYCLE;             /* I2C占空比 */
  I2cHandle.Init.OwnAddress1     = I2C_ADDRESS;               /* I2C地址 */
  I2cHandle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;   /* 禁止广播呼叫 */
  I2cHandle.Init.NoStretchMode   = I2C_NOSTRETCH_DISABLE;     /* 允许时钟延长 */
  if (HAL_I2C_Init(&I2cHandle) != HAL_OK)
  {
    APP_ErrorHandler();
  }
  
  /*I2C从机中断方式接收*/
  while (HAL_I2C_Slave_Receive_IT(&I2cHandle, (uint8_t *)aRxBuffer, DARA_LENGTH) != HAL_OK)
  {
    APP_ErrorHandler();
  }
  /*判断当前I2C状态*/
  while (HAL_I2C_GetState(&I2cHandle) != HAL_I2C_STATE_READY);
  /*I2C从机中断方式发送*/
  while (HAL_I2C_Slave_Transmit_IT(&I2cHandle, (uint8_t *)aTxBuffer, DARA_LENGTH) != HAL_OK)
  {
    APP_ErrorHandler();
  }
  /*判断当前I2C状态*/
  while (HAL_I2C_GetState(&I2cHandle) != HAL_I2C_STATE_READY);

         为了探究更灵活的处理方式,我们可以从硬件I2C的中断逻辑入手,根据官方手册:EV表示在I2C通信过程中,从机会产生的中断。

         我们先定义从机I2C需要发送、接收的数据buffer,数据size等数据结构:

/*---------IIC1---------------*/
#define BUFFER_LENGTH 140
uint8_t  aTxBuffer[BUFFER_LENGTH];//发送缓存
uint8_t  aRxBuffer[BUFFER_LENGTH];//接收缓存
uint8_t  TxIndex = 0;//发送计数
uint8_t  RxIndex = 0;//接收计数
uint8_t  FlagRcvOk = 0;// 接收完成标志 

         然后根据上图的I2C中断需要处理的节点,编写处理函数:

void User_I2C_EV_IRQHandler(void)
{
	static uint32_t SR1Register =0;
    static uint32_t SR2Register =0;
	
	SR1Register = I2C1->SR1;
	SR2Register = I2C1->SR2;
	
	/* I2C1是从机(MSL = 0) */
    /* 主机已发送地址,地址为被置位·(ADDR = 1: EV1(包括发送和接收)) */
	if((SR1Register & 0x0002) == 0x0002)
	{
		RxIndex=0;
		TxIndex=0;
		
		/* 验证传输方向,Read 时的方向,Slave 进入接收器模式 */
		if((uint32_t)(READ_BIT(SR2Register, I2C_SR2_TRA)) == 0x00000000U)//从机读
		{
			
		}
		else //从机写
		{
			
		}
	}

	/* 接收数据(RXNE = 1: EV2) */
	if((SR1Register & 0x0040) == 0x0040)
	{
		aRxBuffer[RxIndex++] = I2C1->DR;
	}
	
	/* 检测到停止条件(STOPF =1: EV4) */
	if((SR1Register & 0x0010) == 0x0010)
	{
			I2C1->CR1 |= 0x0001;
			FlagRcvOk = 1;
	}
	
	//无应答执行
	if((I2C1->SR1 & I2C_SR1_AF)==I2C_SR1_AF)								
	{
		CLEAR_BIT(I2C1->SR1,I2C_SR1_AF);
		FlagRcvOk = 1;
	}

	/* 发送数据(TxE = 1: EV3、EV3-1) */
	if((SR1Register & 0x0080) == 0x0080)
	{
		I2C1->DR = aTxBuffer[TxIndex++]; 
	}
}

        在中断函数里面调用处理函数,并做好发生错误中断时需要进行的处理:

void I2C1_IRQHandler(void)
{
    User_I2C_EV_IRQHandler();
	
	if((I2C1->SR1 & I2C_SR1_BERR)==I2C_SR1_BERR)					//总线错误执行
	{
		CLEAR_BIT(I2C1->SR1,I2C_SR1_BERR);
		I2C_Init();
	}

	if((I2C1->SR1 & I2C_SR1_OVR)==I2C_SR1_OVR)						//过载执行
	{
		CLEAR_BIT(I2C1->SR1,I2C_SR1_OVR);
		I2C_Init();
	}
	
	if((I2C1->SR1 & I2C_SR1_PECERR)==I2C_SR1_PECERR)				//PEC执行
	{
		CLEAR_BIT(I2C1->SR1,I2C_SR1_PECERR);
		I2C_Init();
	}
}

        贴一下HAL库的I2C配置函数,配置完开启中断就好了:

void I2C_Init(void)	
{
    I2cHandle.Instance             = I2C;                                                                   //I2C
    I2cHandle.Init.ClockSpeed      = I2C_SPEEDCLOCK;                                                        //I2C通讯速度
    I2cHandle.Init.DutyCycle       = I2C_DUTYCYCLE;                                                         //I2C占空比
    I2cHandle.Init.OwnAddress1     = I2C_ADDRESS;                                                           //I2C地址                                                                                                             
    I2cHandle.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;                                               //禁止广播呼叫
    I2cHandle.Init.NoStretchMode   = I2C_NOSTRETCH_ENABLE;                                                 //允许时钟延长
  
	if (HAL_I2C_DeInit(&I2cHandle) != HAL_OK)																														//I2C初始化
    {
        Error_Handler();
    }
	__HAL_RCC_I2C_CLK_DISABLE();
	
	if (HAL_I2C_Init(&I2cHandle) != HAL_OK)                                                              //I2C初始化
    {
        Error_Handler();
    }
	
	SET_BIT(I2C1->CR1, I2C_CR1_ACK);//使能应答
	SET_BIT(I2C1->CR2, I2C_IT_EVT);															
	SET_BIT(I2C1->CR2, I2C_IT_BUF);
	SET_BIT(I2C1->CR2, I2C_IT_ERR);
}

        最后在main函数里面更新从机要发送的数据,直接判断完成标志后,对buffer进行数据赋值就好了:

int main(void)
{
	unsigned char i;
	
	HAL_Init();
	
	I2C_Init();//I2C初始化
	
    for(i = 0;i < DARA_LENGTH;i++)
	{
		aTxBuffer[i]=i;
	}
	
	while(1)
	{
			if(FlagRcvOk)
			{
				FlagRcvOk = 0;
				for(i = 0;i < DARA_LENGTH;i++)
				{
					aTxBuffer[i]++;
				}
			}
	}
}

        完成这些操作,这时候就可以让PY32作为从机进行I2C通信并自由应答了!

        程序写完用逻辑分析器看一下波形,非常nice:

        完结撒花✿✿ヽ(°▽°)ノ✿

>>>【点击进入,普冉PY32仿真烧录工具】​​icon-default.png?t=N7T8https://zhuanlan.zhihu.com/p/673851365

  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 10
    评论
要在 STM32F4 上使用 HAL 库实现 SPI1 从机接收数据,您可以按照以下步骤进行操作: 1. 配置 SPI1 接口为从机模式: ```c hspi1.Instance = SPI1; hspi1.Init.Mode = SPI_MODE_SLAVE; hspi1.Init.Direction = SPI_DIRECTION_2LINES; hspi1.Init.DataSize = SPI_DATASIZE_8BIT; hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; hspi1.Init.NSS = SPI_NSS_SOFT; hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; hspi1.Init.TIMode = SPI_TIMODE_DISABLE; hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_DISABLE; hspi1.Init.CRCPolynomial = 10; HAL_SPI_Init(&hspi1); ``` 2. 配置 DMA 接收通道: ```c hdma_spi_rx.Instance = DMAx_STREAMy; // 根据实际情况修改 DMA 实例和通道 hdma_spi_rx.Init.Channel = DMA_CHANNEL_x; // 根据实际情况修改 DMA 通道 hdma_spi_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; hdma_spi_rx.Init.PeriphInc = DMA_PINC_DISABLE; hdma_spi_rx.Init.MemInc = DMA_MINC_ENABLE; hdma_spi_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; hdma_spi_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; hdma_spi_rx.Init.Mode = DMA_NORMAL; hdma_spi_rx.Init.Priority = DMA_PRIORITY_LOW; HAL_DMA_Init(&hdma_spi_rx); __HAL_LINKDMA(&hspi1, hdmarx, hdma_spi_rx); ``` 3. 设置接收缓冲区并启动 DMA 接收: ```c uint8_t rxBuffer[BufferSize]; // 根据实际需求设置缓冲区大小 HAL_SPI_Receive_DMA(&hspi1, rxBuffer, BufferSize); ``` 4. 在 `DMAx_STREAMy_IRQHandler()` 中处理 DMA 接收完成中断: ```c void DMAx_STREAMy_IRQHandler(void) { HAL_DMA_IRQHandler(&hdma_spi_rx); } ``` 5. 在 `HAL_SPI_RxCpltCallback()` 回调函数中处理接收完成的数据: ```c void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef *hspi) { // 处理接收完成的数据 // ... } ``` 通过以上步骤,您就可以使用 HAL 库在 STM32F4 上实现 SPI1 从机接收数据了。根据您的实际需求,您可能需要进行进一步的配置和处理。请根据 HAL 库的文档和您的具体硬件需求进行相应的修改和调整。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

鲜衣怒马少年时'''

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值