赞
踩
使用硬件I2C是,不用编写协议时序细节,通过寄存器配置好MCU的I²C控制器,I²C控制器便可控制引脚自动输出I²C时序。这里还是基于STM32F103_MIN开发板,以读写EEPROM(ATC2402)为例,进行回顾和分析。
STM32F103系列的I2C控制器,可作为通信主机或从机,因此有4中工作模式可选择:主机发送模式、主机接收模式,从机发送模式、从机接收模式。
传输速度上,支持标准模式和快捷模式。同时还支持SMBus2.0(系统管理总线)和PMBus(电源管理时钟总线)。
I2C控制器结构如下:
引脚:I2C协议只需要两个引脚(SDA和SCL),SMBA引脚仅用于SMBus模式的Alert引脚,通常不用管。
数据收发:主要涉及到数据寄存器(DR)和数据移位寄存器(DSR)。
当发送数据时,将发送的字节写入DR,硬件会把DR中的字节搬到DSR中,然后再时钟信号的配合下,把DSR最高位的数据放到数据线SDA上,并对DSR进行移位操作。
当接收数据时,数据控制器(Data Control)根据时钟信号,把SDA线上的高低电平转换为“1”或“0”的数据,写到DSR的最低位,同时DSR移位操作,当接收完一个字节的8位数据后,把DSR中的数据搬到DR寄存器中。
时钟信号:时钟控制器用于驱动同步时钟信号线SCL。通过配置时钟控制寄存器(CCR),可以调整SCL的频率。
逻辑控制:有两个控制寄存器CR1和CR2用于控制逻辑。通过它们可以触发起始和停止信号,做出ACK响应,配置外设时钟频率,开启DMA和中断功能,同时控制逻辑的状态会反馈到SR1和SR2两个状态寄存器上,根据它们可以知道当前总线是否被占用,本机是主设备还是从设备,数据是否发送完毕等。
目的:使用MCU的硬件I2C,获取AT24C02的数据。
/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * * Copyright (c) 2022 STMicroelectronics. * All rights reserved. * * This software is licensed under terms that can be found in the LICENSE file * in the root directory of this software component. * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" #include "usart.h" #include "gpio.h" /* Private includes ----------------------------------------------------------*/ /* USER CODE BEGIN Includes */ #include<stdio.h> #include <string.h> #include "i2c.h" #include "eeprom.h" /* USER CODE END Includes */ /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN PTD */ /* USER CODE END PTD */ /* Private define ------------------------------------------------------------*/ /* USER CODE BEGIN PD */ /* USER CODE END PD */ /* Private macro -------------------------------------------------------------*/ /* USER CODE BEGIN PM */ /* USER CODE END PM */ /* Private variables ---------------------------------------------------------*/ /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_NVIC_Init(void); /* USER CODE BEGIN PFP */ /* USER CODE END PFP */ /* Private user code ---------------------------------------------------------*/ /* USER CODE BEGIN 0 */ /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main(void) { /* USER CODE BEGIN 1 */ char tx_data[20]={"Hello I2C"}; char rx_data[20]={0}; /* USER CODE END 1 */ /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ HAL_Init(); /* USER CODE BEGIN Init */ /* USER CODE END Init */ /* Configure the system clock */ SystemClock_Config(); /* USER CODE BEGIN SysInit */ I2C_Init(); /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART1_UART_Init(); /* Initialize interrupts */ MX_NVIC_Init(); /* USER CODE BEGIN 2 */ printf("*************\r\n"); printf("I2C Test 2\r\n"); printf("*************\r\n"); /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ if(get_keyvalue()){ //发送数据 EEPROM_WriteNByte(0,(uint8_t *)tx_data,sizeof(tx_data)); HAL_Delay(1); printf("EEPROM Write:%s\r\n",rx_data); EEPROM_ReadNByte(0,(uint8_t *)rx_data,sizeof(rx_data)); HAL_Delay(1); printf("EEPROM Read:%s\r\n",rx_data); //清空接收缓冲区 memset(rx_data,0,sizeof(rx_data)); } } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) { Error_Handler(); } } /** * @brief NVIC Configuration. * @retval None */ static void MX_NVIC_Init(void) { /* EXTI0_IRQn interrupt configuration */ HAL_NVIC_SetPriority(EXTI0_IRQn, 0, 0); HAL_NVIC_EnableIRQ(EXTI0_IRQn); /* USART1_IRQn interrupt configuration */ HAL_NVIC_SetPriority(USART1_IRQn, 0, 0); HAL_NVIC_EnableIRQ(USART1_IRQn); } /* USER CODE BEGIN 4 */ /* USER CODE END 4 */ /** * @brief This function is executed in case of error occurrence. * @retval None */ void Error_Handler(void) { /* USER CODE BEGIN Error_Handler_Debug */ /* User can add his own implementation to report the HAL error return state */ __disable_irq(); while (1) { } /* USER CODE END Error_Handler_Debug */ } #ifdef USE_FULL_ASSERT /** * @brief Reports the name of the source file and the source line number * where the assert_param error has occurred. * @param file: pointer to the source file name * @param line: assert_param error line source number * @retval None */ void assert_failed(uint8_t *file, uint32_t line) { /* USER CODE BEGIN 6 */ /* User can add his own implementation to report the file name and line number, ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */
i2c.h
#include"i2c.h" #include "main.h" #include "usart.h" #include <stdio.h> #include "stm32f1xx_hal_i2c.h" I2C_HandleTypeDef hi2c; //初始化I2C速率和地址格式 void I2C_Init(void){ hi2c.Instance =I2Cx; //I2C寄存器基地址 hi2c.Init.ClockSpeed =I2Cx_ClockSpeed; hi2c.Init.DutyCycle =I2C_DUTYCYCLE_2; //I2C的SCL时钟占空比 hi2c.Init.GeneralCallMode=I2C_GENERALCALL_DISABLE;//设置广播呼叫模式 hi2c.Init.NoStretchMode =I2C_NOSTRETCH_DISABLE;//设置禁止时钟延长模式 hi2c.Init.AddressingMode=I2C_ADDRESSINGMODE_7BIT;//设置I2C寻址长度 hi2c.Init.DualAddressMode=I2C_DUALADDRESS_DISABLE;//是否使用两个STM32的设备地址 hi2c.Init.OwnAddress1 =0x0A; //STM32的设备地址1(支持7和10bit) hi2c.Init.OwnAddress2 =0; //STM32的设备地址2(只支持7bit) if(HAL_I2C_Init(&hi2c)!=HAL_OK){ Error_Handler(); } } //使能I2C时钟,引脚时钟,并设置复位 void HAL_I2C_MspInit(I2C_HandleTypeDef *hi2c){ GPIO_InitTypeDef GPIO_InitStruct; if(hi2c->Instance==I2Cx){ I2Cx_CLK_EN(); //使能I2C时钟 SCL_PIN_CLK_EN(); //使能引脚时钟 SDA_PIN_CLK_EN(); GPIO_InitStruct.Pin=SCL_PIN; GPIO_InitStruct.Mode=GPIO_MODE_AF_OD; GPIO_InitStruct.Speed=GPIO_SPEED_FREQ_HIGH; GPIO_InitStruct.Pull=GPIO_NOPULL; HAL_GPIO_Init(SCL_PORT,&GPIO_InitStruct); GPIO_InitStruct.Pin=SDA_PIN; HAL_GPIO_Init(SDA_PORT,&GPIO_InitStruct); I2Cx_FORCE_RESET(); //强制复位 I2Cx_RELEASE_RESET();//释放复位 I2C初始化后 有一定概率 处于总线忙碌,返回I2C_BUSY强制复位有改善 } }
eeprom.h
#ifndef _EEPROM_H #define _EEPROM_H #include"stm32f1xx_hal.h" #include "stm32f1xx_hal_i2c.h" #define EEPROM_DEV_ADDR (0xA0) #define EEPROM_WR (0x00) #define EEPROM_RD (0x01) #define EEPROM_WORD_ADDR_SIZE (0x08) void EEPROM_WirteByte(uint16_t addr,uint8_t data); void EEPROM_ReadByte(uint16_t addr,uint8_t *pdata); void EEPROM_WriteNByte(uint16_t addr,uint8_t *pdata,uint16_t size); void EEPROM_ReadNByte(uint16_t addr,uint8_t *pdata,uint16_t size); #endif
eeprom.c
#include "i2c.h" #include <stdio.h> #include "usart.h" #include "eeprom.h" #include "main.h" //写一个字节的数据 void EEPROM_WirteByte(uint16_t addr,uint8_t data){ uint8_t write_data[10]; uint8_t data_num; if(EEPROM_WORD_ADDR_SIZE==0x08){//判别型号 write_data[0]=(uint8_t)(addr & 0x00ff); write_data[1]=data; data_num=2; }else { write_data[0]=(uint8_t)(addr>>8); write_data[1]=(uint8_t)(addr & 0xff); write_data[2]=data; data_num=3; } //发送给数据和地址 if(HAL_I2C_Master_Transmit(&hi2c,EEPROM_DEV_ADDR | EEPROM_WR,write_data,data_num,3000)!=HAL_OK){ Error_Handler(); } while(HAL_I2C_GetState(&hi2c)!=HAL_I2C_STATE_READY); } //读一个字节 void EEPROM_ReadByte(uint16_t addr,uint8_t *pdata){ uint8_t write_data[10]; uint8_t data_num; if(EEPROM_WORD_ADDR_SIZE==0x08){ write_data[0]=(uint8_t)(addr & 0x00ff); data_num=1; } else{ write_data[0]=(uint8_t)(addr>>8); write_data[1]=(uint8_t)(addr & 0x00ff); data_num=2; } //发送数据 if(HAL_I2C_Master_Transmit(&hi2c,EEPROM_DEV_ADDR | EEPROM_WR,write_data,data_num,300)!=HAL_OK){ Error_Handler(); } while(HAL_I2C_GetState(&hi2c)!=HAL_I2C_STATE_READY); //接收数据 if(HAL_I2C_Master_Receive(&hi2c,EEPROM_DEV_ADDR | EEPROM_RD,pdata,1,300)!=HAL_OK){ Error_Handler(); } } //写N个字节的数据 void EEPROM_WriteNByte(uint16_t addr,uint8_t *pdata,uint16_t size){ uint16_t i=0; for(;i<size;i++){ EEPROM_WirteByte(addr,pdata[i]); addr++; HAL_Delay(10);//期间要间隔至少5ms } } void EEPROM_ReadNByte(uint16_t addr,uint8_t *pdata,uint16_t size){ uint8_t i=0; for(;i<size;i++){ EEPROM_ReadByte(addr,&pdata[i]); addr++; } }
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
赞
踩
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。