STM32CubeMX 硬件IIC從機查詢收發實現

STM32CubeMX 硬件IIC從機查詢收發實現

前言

這幾天再做STM32的從機實驗,由於是新手,以前都是用IO口模擬實現的,多次聽說STM32硬件IIC有大坑,這次想看看,網上搜集了很多資料,主機的還有跡可循,可做從機的就沒看見一份完整的所以這裏給整理下。(太坑了,硬件iic過程沒法看,標誌位又一堆硬件控制,只能一點點查手冊)
有一些關鍵的都用了寄存器解釋,不是同庫的也可參考下
尚存bug:偶爾會出現收發錯誤的情況,可能是被中斷中斷了通訊

IIC外設初始化

對應Cube來說就是配置下外設,這裏就不粘了,直接上代碼:
首先是管腳的初始化,大家可以參照一下:

void HAL_I2C_MspInit(I2C_HandleTypeDef* hi2c)
{

  GPIO_InitTypeDef GPIO_InitStruct;
   if(hi2c->Instance==I2C3)
  {
  /* USER CODE BEGIN I2C3_MspInit 0 */

  /* USER CODE END I2C3_MspInit 0 */
      __HAL_RCC_I2C3_CLK_ENABLE();
    /**I2C3 GPIO Configuration    
    PA7     ------> I2C3_SCL
    PB4 (NJTRST)     ------> I2C3_SDA 
    */
    GPIO_InitStruct.Pin = GPIO_PIN_7;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;//很重要,必須是開漏模式
    GPIO_InitStruct.Pull = GPIO_NOPULL;	//這裏cube設置的是PULLUP,調的時候參照論壇大佬改了,但實際使用中,兩種皆ok
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF4_I2C3;
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    GPIO_InitStruct.Pin = GPIO_PIN_4;
    GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;//很重要,必須是開漏模式
    GPIO_InitStruct.Pull = GPIO_NOPULL;//這裏也一樣,不影響
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
    GPIO_InitStruct.Alternate = GPIO_AF4_I2C3;
    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    /* Peripheral clock enable */

    /* I2C3 interrupt Init */
    //這裏中斷沒用到,後面等樓主調出來了再更新,目前是while裏查詢做的
    HAL_NVIC_SetPriority(I2C3_EV_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(I2C3_EV_IRQn);
    HAL_NVIC_SetPriority(I2C3_ER_IRQn, 0, 0);
    HAL_NVIC_EnableIRQ(I2C3_ER_IRQn);
  /* USER CODE BEGIN I2C3_MspInit 1 */

  /* USER CODE END I2C3_MspInit 1 */
  }

}

IIC外設初始化配置

static void MX_I2C3_Init(void)
{

  hi2c3.Instance = I2C3;
  hi2c3.Init.Timing = 0x10909CEC;
  hi2c3.Init.OwnAddress1 = 0x60;//這個是從機地址,很重要,如果沒和主機對上,那麼沒有一點響應
  hi2c3.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c3.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c3.Init.OwnAddress2 = 0;//這裏是可以配置第二個地址的,我沒有使用
  hi2c3.Init.OwnAddress2Masks = I2C_OA2_NOMASK;
  hi2c3.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c3.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c3) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure Analogue filter 
    */
  if (HAL_I2CEx_ConfigAnalogFilter(&hi2c3, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

    /**Configure Digital filter 
    */
  if (HAL_I2CEx_ConfigDigitalFilter(&hi2c3, 0) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }

}

完成初始化後,對應寄存器的數據應該是這樣的:
在這裏插入圖片描述
這裏之後,初始化完成了。

從機的收發

由於從機接受代碼的缺乏,以下是樓主根據其寄存器規則摸索的經驗。當然仍然有一些問題,希望大家可以填坑,不過倒是實現了基本的收發。
同正常的IIC一樣,從機的收發都需要主機發起,當從機接受到主機的起始信號後,硬件自動檢測與自己是否匹配。
這裏主要是硬件操作ISR寄存器
地址匹配:ISR->addr 自動置1,否則爲零。 操作ICR清空
讀寫判斷:ISR->DIR
1:進入transfer(主機 讀,從機需要發數據)
0:進入recieve(主機寫,從機只管收數據)
addr更新時自動更新
主機地址:ISR->ADDCODE (這裏樓主一直很困惑,IIC應該沒發主機地址呀,我這個從機是怎麼知道主機是什麼地址的?但事實是確實這裏存儲着主機地址)
ISR->busy 總線繁忙標誌位,受到起始信號置1,結束信號置0(這個位確實很忙有時候受到了結束信號,但卻仍沒有被置0)
ISR->STOPF 這個位被置1(手冊上解釋是在發送時受到了stop信號),導致busy一直爲1,我是自己通過操作ICR寄存器清空的。
下面是受到讀命令後的寄存器
在這裏插入圖片描述

從機收發代碼:

		if((((I2C3->ISR)&0x200)>>9)==1)//判斷 仲裁是否失敗,否則重置
		{
			I2C3->CR1=0x00;
			I2C3->CR1=0x01;
		}
		if((((I2C3->ISR)&0x08)>>3)==1)//判斷是否受到主機數據(addr)
		{
			
			if((((I2C3->ISR)&0x10000)>>16)==1)//判斷DIR(讀寫)
			{
			//(主機讀數據)
				if(I2C3->RXDR==0x00)//讀 地址,這裏根據自己協議
				x=HAL_I2C_Slave_Transmit(&hi2c3,ppp1,8,10);//庫自帶的發送函數
				if(I2C3->RXDR==0x12)//讀取 0x12 的數據
				{
					x=HAL_I2C_Slave_Transmit(&hi2c3,ppp2,21,30);
					ppp2[0]++;
				}
			}
			else
			{
			//主機寫	
				x=HAL_I2C_Slave_Receive(&hi2c3,ppp,2,10);//讀或者寫的字節數都是固定的,樓主查了很多資料,都沒辦法修改,不過讀的字節超過主機發的字節會提示讀取失敗,但已讀到的數據會保留
				//I2C3->ICR=(I2C3->ICR)|0x04;
			}
	
		}
				if((((I2C3->ISR)&0x20)>>5)==1)//手動清空stop標誌位
			{
//				
				I2C3->ICR|=0x20;
//				I2C3->CR1=0x01;
			}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章