당신은 주제를 찾고 있습니까 “stm32 i2c 통신 예제 – [HAL, CubeMX, TrueSTUDIO를 이용한 STM32F4 속성 강의 강좌] 9강. I2C 통신(AT24C04 EEPROM)“? 다음 카테고리의 웹사이트 you.tfvp.org 에서 귀하의 모든 질문에 답변해 드립니다: you.tfvp.org/blog. 바로 아래에서 답을 찾을 수 있습니다. 작성자 Wonyeob Chris Park 이(가) 작성한 기사에는 조회수 10,938회 및 좋아요 59개 개의 좋아요가 있습니다.
stm32 i2c 통신 예제 주제에 대한 동영상 보기
여기에서 이 주제에 대한 비디오를 시청하십시오. 주의 깊게 살펴보고 읽고 있는 내용에 대한 피드백을 제공하세요!
d여기에서 [HAL, CubeMX, TrueSTUDIO를 이용한 STM32F4 속성 강의 강좌] 9강. I2C 통신(AT24C04 EEPROM) – stm32 i2c 통신 예제 주제에 대한 세부정보를 참조하세요
※ 영상에서 사용되는 실습 보드는 별도 구매하실 수 있습니다. https://smartstore.naver.com/mhivestore/products/4953744659
안녕하세요 M-HIVE ChrisP 입니다.
\”HAL 드라이버, CubeMX, TrueSTUDIO를 이용한 STM32F4 속성으로 따라하기\” 동영상 강좌 입니다.
본 영상은 \” 9강. I2C (AT24C04 EEPROM)\” 으로
– I2C 통신 개념
– 1:1 또는 1:N 통신시 선 연결
– I2C 통신에서 풀업 저항의 의미
– 장치 주소 및 내부저장영역 주소의 개념
– ACK의 개념
– 읽기 및 쓰기 시퀀스
– CubeMX로 I2C 설정
– AT24C04 EEPROM에 데이터 쓰기/읽기
※ 용량이 큰 EEPROM의 경우 내부 저장영역의 주소가 16bit 로 표현되는 경우가 있으니 반드시 데이터시트를 참고하세요!
많이 시청해주세요~
stm32 i2c 통신 예제 주제에 대한 자세한 내용은 여기를 참조하세요.
STM32F4 I2C 통신 기초. – 네이버 블로그
이번 시간에는 I2C 통신의 기초를 해보려고 합니다. 사용 보드 : STM32F4-DISCO MCU : STM32F401VC Audio : CS43L22 사용하고 있는 개발보드에 I2C …
Source: m.blog.naver.com
Date Published: 1/27/2022
View: 1338
STM32F103 I2C 기초설정 및 코드 짜기 – DKMIN
LED가 켜진다면 I2C 통신은 잘 되는 것이라고 볼 수 있다. 만약에 안켜진다면 while문에 머물고 있는 것이니, 주소가 틀렸을 가능성이 있다. while( …
Source: dkeemin.com
Date Published: 1/6/2022
View: 6890
I2C 통신을 이용한 DAC 제어 – BME Architecture
STM32/HAL library … HAL 함수를 통해 쉽게 I2C 통신을 할 수 있다. … 오늘은 TI사의 8bit DAC IC인 DAC5571를 구동하는 예제를 작성해본다.
Source: jexe.tistory.com
Date Published: 1/17/2021
View: 3594
[STM32] I2C테스트
일단 STM32 I2C 테스트 하면서 필요한 사항을 정리해 본다. STM32와 MSP430(2013)을 이용하한 기본 I2C 테스트 보드. 테스트 예제소스.
Source: nexp.tistory.com
Date Published: 2/16/2022
View: 3165
STM32 – I2C를 이용한 DS3231 제어 – rs29
사용한 DS3231 모듈. 작동 전압 : 2.3~5.5V; 통신 : I2C; Fast(400kHz) 모드 지원; 배터리 용량이 부족할 경우, 시간 흐름이 부정확해짐 …
Source: rs29.tistory.com
Date Published: 12/19/2022
View: 7557
[Hal, Cubemx, Truestudio를 이용한 Stm32F4 속성 강의 강좌] 9 …
이에 대한 추가 정보 stm32 i2c 통신 예제 주제에 대해서는 다음 문서를 참조하십시오. 아이디어가 있으면 기사 아래에 댓글을 달거나 주제에 대한 다른 …
Source: ko.liriklagu.asia
Date Published: 11/11/2021
View: 6401
STM32. I2C 사용하기. – igotit
I2C 설정하기. 탭 “Configuration” 에서 I2C1 클릭하여,. 탭 “Parameter Settings” 에서 I2C통신속도 선택하여 아래 붉박은 Standard …
Source: igotit.tistory.com
Date Published: 2/21/2022
View: 6877
Getting Started with STM32 – I2C Example
Let’s look at how to connect a simple I2C device to a STM32 Nucleo board to read temperature data using the STM32 HAL API.
Source: www.digikey.be
Date Published: 1/1/2022
View: 4797
주제와 관련된 이미지 stm32 i2c 통신 예제
주제와 관련된 더 많은 사진을 참조하십시오 [HAL, CubeMX, TrueSTUDIO를 이용한 STM32F4 속성 강의 강좌] 9강. I2C 통신(AT24C04 EEPROM). 댓글에서 더 많은 관련 이미지를 보거나 필요한 경우 더 많은 관련 기사를 볼 수 있습니다.
주제에 대한 기사 평가 stm32 i2c 통신 예제
- Author: Wonyeob Chris Park
- Views: 조회수 10,938회
- Likes: 좋아요 59개
- Date Published: 2018. 8. 20.
- Video Url link: https://www.youtube.com/watch?v=gnthBIGKWxo
STM32F4 I2C 통신 기초.
#define CS43L22_ADD (0x4A<<1) 이 부분은 위에 타이밍 차트를 보게되면 칩 address가 1 0 0 1 0 1 ADO 0 입니다. 여기서 0번째 bit 0 = write 1 = read 입니다. 여기서 실질적인 칩 주소값은 1 0 0 1 0 1 ADO 이 되는겁니다. ADO = 0 이기 때문에 0x4A 이지만 왼쪽으로 한칸 Shift를 해주어야 되기 때문에 해준겁니다. 결국 최종값은 0x94가 됩니다. 이제 Receive 하는부분을 주석처리하고 보내는 부분만 한번 Debug 하여 오실로스코프로 한번 찍어 보도록 하겠습니다. 아주 잘나옵니다. 노란선이 SCL 파란선이 SDA 입니다. SCL이 High 인 상태에서 SDA가 High-Low 로 떨어지는 첫번째 구간이 바로 Start 신호입니다. 반대로 SCL이 High 인상태에서 SDA가 Low-High로 상승 할 경우 Stop 신호입니다. SCL 이 High 일때 SDA 라인을 읽어보면 아래 빨간 글자 처럼 읽을 수 있습니다.
STM32F103 I2C 기초설정 및 코드 짜기
STM32F103 I2C 튜토리얼
오늘도 CUBEMX로 기초 설정하고 진행해보고자 한다.
Turn on Cubemx to configure, then click ‘New Project’
프로젝트 열자
Fill ‘f103’ in ‘Part Number Search’, and double click the board
F103을 입력하고 보드를 더블클릭하자
push Yes button
yes 버튼 눌러야 기초 설정 다된다.
click PB8, PB9 Pin and set i2c1 SCL, SDA
먼저 PB8, PB9를 눌러서 각각 i2c 설정을 해주자. 기본 설정이 PB8, PB9번핀이 아니라서 나중에 뉴클레오 보드에 눌러놓고 안돌아가서 샷건칠 수 있다.
You can see that I2C female connectors are connected with PB8, PB9.
뉴클레오 보드에 있는 FEMALE 커넥터에 물리고 싶다면 꼭 확인해봐야 할 사안이다.
Set I2C1
이제 좌측 패널에서 i2c를 설정해주면 주황색이 초록색으로 바뀌는 것을 알 수 있다.
그다음 뭐 별거 없다. 저거 톱니바퀴 누르면 아래와 같이 창이 뜬다.
Click the gear shaped button on the upside.
Fill the Project name and set Toolchain you use.
창이 켜지면 더 만질 것이 없다. 왜냐면 기초 설정도 필요 없기 때문이다. 그래서 그냥 보낸다고 하면 아래 함수 하나만 쓰면 된다.
HAL_I2C_Master_Transmit
이 함수는 데이터를 보낼 때 쓰는 놈이다. 디스크립션은 아래와 같다.
HAL_I2C_Master_Transmit Discription
데이터를 송신한다면 위의 설명을 참고하면 된다.
I2C_HandleTypeDef * hi2c : i2c의 핸들러의 주소를 넘겨주는 인자이다. 위에서 설정한대로라면 i2c1을 사용하므로 &hi2c1 을 사용하면 될 것이다.
을 사용하면 될 것이다. uint16_t DevAddress : 두번째로 넘겨줄 인자는 i2c의 주소를 넘겨주는 방식이다. i2c의 주소는 보통 7비트로 되어 있는데 이것을 <<1시킨 값을 넣어줘야 한다. 왜냐하면 i2c의 주소 7비트 + R/W bit로 총 8비트로 구동하기 때문이다. 이 함수에서는 자동으로 <<1 쉬프트 시켜주지 않으므로 쉬프트 된 결과를 넣어줘야한다. uint8_t * pData : 넘겨줄 데이터의 주소를 넘겨줘야한다. 1개의 데이터라고 하면 보통 &연산자로 넘겨줄거고, 배열이라면 그냥 배열의 이름을 넣어주면 되겠다. uint16_t Size : 이건 뭐 데이터 길이 넣어주면 된다. uint32_t Timeout : 타임아웃 길이 정하는건데..뭐 대충 정해도 된다. 데이터를 보내보기 일단 mpu6050에게 데이터를 보내고 싶으면 주소를 알아야한다. 스펙시트를 보자. MPU6050 I2C Addr AD0이 0이라고 가정하면 주소는 1101000, 0x68이다. 근데 위 함수를 사용하려면 <<1 쉬프트를 해야하니까 주소는 최종적으로 아래과 같다. 0x68<<1 = 0xd0 0x68<<1 = 0xD0 구해졌으면 코드를 작성해보자. declare data, addr 일단 데이터는 자신이 접속하고자 하는 주소와, 데이터를 넣어주면 되겠다. data에 뭘 넣을지는 레지스터 맵을 보면된다. 그 다음에 위 처럼 코드를 작성하고 하단에 led가 켜지는 코드를 집어넣어보자. LED가 켜진다면 I2C 통신은 잘 되는 것이라고 볼 수 있다. 만약에 안켜진다면 while문에 머물고 있는 것이니, 주소가 틀렸을 가능성이 있다. while(HAL_I2C_Master_Transmit(&hi2c1, addr, &data, 1, 100000)!=HAL_OK) 데이터에 뭘 넣냐는 사용자마다 다르고, 기술하려면 내용이 너무 길어질 듯 해서 여기까지만 작성하겠다.
I2C 통신을 이용한 DAC 제어
반응형
HAL 함수를 통해 쉽게 I2C 통신을 할 수 있다.
나의 경우, 이상할 정도로 I2C를 쓰는 일은 없었다, 대부분 SPI나 UART…
작은 사이즈의 OLED 디스플레이나 EEPROM 정도로 기억한다.
인터넷을 찾아보면 대다수가 AT24C02 같은 메모리 예제가 많을 것이다.
오늘은 TI사의 8bit DAC IC인 DAC5571를 구동하는 예제를 작성해본다.
CubeIDE를 실행시키고 I2C1를 선택하면 다음과 같은 Configuration이 나타난다.
-Master Fearues는 MCU가 마스터 역할일 때의 설정.
-Slave Features는 MCU가 슬레이브 역할 일 때의 설정.
DAC5571의 데이터 시트를 확인 한뒤, 기본 설정인 Standard Mode로 선택했다.
그 이상 사실 I2C는 크게 세팅할 것은 없다.
DAC5571는 컨트롤 바이트가 16bit를 2개로 나눠서 받는다는 특징이 있다.
(데이터 시트 참조)
그래서 I2C Transmit을 할때 slave address를 제외하고 Ctrl데이터를 잘라서 버퍼에 담아 2bytes로 보낸다.
아래는 예제에 담긴 동작 코드.
void SetDacValue_8bit (uint8_t DacValue)
{
//DacValue 범위: 0~255 -> 0V~5V
DAC_MSByte = (DacValue & 0xf0) >> 4;
DAC_LSByte = (DacValue & 0x0f) << 4; DAC_TransmitBuffer[0] = DAC_MSByte; DAC_TransmitBuffer[1] = DAC_LSByte; HAL_I2C_Master_Transmit(&hi2c1, 0x98, &DAC_TransmitBuffer[0], 2, 100); } 참고로 괜히 핀 설정에서 내부 Pull-up 했다고 풀업저항 달지 않는 바보같은 행위는 하지말자. 혹시나 했는데 역시 신호가 이상해서 저항 1Kohm 물렸다. 위 함수를 만들어서 10ms마다 비트 값을 올려 0~5V 전압이 반복 출력되도록 동작하였다. 스코프 상에서도 무리없이 잘 출력된다. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 /* USER CODE BEGIN Header */ /** ****************************************************************************** * @file : main.c * @brief : Main program body ****************************************************************************** * @attention * *
© Copyright (c) 2021 STMicroelectronics. * All rights reserved. * * This software component is licensed by ST under BSD 3-Clause license, * the “License”; You may not use this file except in compliance with the * License. You may obtain a copy of the License at: * opensource.org/licenses/BSD-3-Clause * ****************************************************************************** */ /* USER CODE END Header */ /* Includes ——————————————————————*/ #include “main.h” /* Private includes ———————————————————-*/ /* USER CODE BEGIN Includes */ /* 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 ———————————————————*/ I2C_HandleTypeDef hi2c1; /* USER CODE BEGIN PV */ /* USER CODE END PV */ /* Private function prototypes ———————————————–*/ void SystemClock_Config( void ); static void MX_GPIO_Init( void ); static void MX_I2C1_Init( void ); /* USER CODE BEGIN PFP */ void SetDacValue_8bit (uint8_t DacValue); /* USER CODE END PFP */ /* Private user code ———————————————————*/ /* USER CODE BEGIN 0 */ uint8_t DAC_TransmitBuffer[ 2 ] = { 0x00 , 0x00 }; uint8_t DAC_MSByte = 0x00 ; uint8_t DAC_LSByte = 0x00 ; uint8_t DACval = 0x00 ; /* USER CODE END 0 */ /** * @brief The application entry point. * @retval int */ int main( void ) { /* USER CODE BEGIN 1 */ /* 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 */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_I2C1_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while ( 1 ) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ SetDacValue_8bit(DACval); DACval + + ; HAL_Delay( 10 ); } /* USER CODE END 3 */ } /** * @brief System Clock Configuration * @retval None */ void SystemClock_Config( void ) { RCC_OscInitTypeDef RCC_OscInitStruct = { 0 }; RCC_ClkInitTypeDef RCC_ClkInitStruct = { 0 }; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; RCC_OscInitStruct.HSIState = RCC_HSI_ON; RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; 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_HSI; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; if (HAL_RCC_ClockConfig( & RCC_ClkInitStruct, FLASH_LATENCY_0) ! = HAL_OK) { Error_Handler(); } } /** * @brief I2C1 Initialization Function * @param None * @retval None */ static void MX_I2C1_Init( void ) { /* USER CODE BEGIN I2C1_Init 0 */ /* USER CODE END I2C1_Init 0 */ /* USER CODE BEGIN I2C1_Init 1 */ /* USER CODE END I2C1_Init 1 */ hi2c1.Instance = I2C1; hi2c1.Init.ClockSpeed = 100000 ; hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2; hi2c1.Init.OwnAddress1 = 0 ; hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT; hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE; hi2c1.Init.OwnAddress2 = 0 ; hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE; hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE; if (HAL_I2C_Init( & hi2c1) ! = HAL_OK) { Error_Handler(); } /* USER CODE BEGIN I2C1_Init 2 */ /* USER CODE END I2C1_Init 2 */ } /** * @brief GPIO Initialization Function * @param None * @retval None */ static void MX_GPIO_Init( void ) { /* GPIO Ports Clock Enable */ __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); } /* USER CODE BEGIN 4 */ void SetDacValue_8bit (uint8_t DacValue) { //DacValue 범위: 0~255 -> 0V~5V DAC_MSByte = (DacValue & 0xf0 ) > > 4 ; DAC_LSByte = (DacValue & 0x0f ) < < 4 ; DAC_TransmitBuffer[ 0 ] = DAC_MSByte; DAC_TransmitBuffer[ 1 ] = DAC_LSByte; HAL_I2C_Master_Transmit( & hi2c1, 0x98 , & DAC_TransmitBuffer[ 0 ], 2 , 100 ); } /* 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 ", file, line) */ /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ Colored by Color Scripter cs 반응형
[STM32] I2C테스트
STM32 I2C테스트
일단 STM32 I2C 테스트 하면서 필요한 사항을 정리해 본다.
STM32와 MSP430(2013)을 이용하한 기본 I2C 테스트 보드
테스트 예제소스 stm32_I2C.zip STM32 유저 가이드 stm32_i2c.pdf
I2C블럭도
STM32는 2개의 I2C모듈이 있고 그림과 같은 블록도로
I2C1
PB6 – SCL
PB7 – SDA
I2C2
PB10 – SCL
PB11 – SDA
초기화
void i2c_init(void)
{
I2C_InitTypeDef I2C_InitStructure;
//1)클럭 초기화 I2C1 and Periph clock enable
RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
//GPIOB Periph clock enable
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
//2)Configure I2C2 pins: SCL and SDA
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10 | GPIO_Pin_11;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
GPIO_Init(GPIOB, &GPIO_InitStructure);
//3)I2C1 configuration
I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
// I2C_InitStructure.I2C_OwnAddress1 = I2C1_SLAVE_ADDRESS7;
I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
I2C_InitStructure.I2C_ClockSpeed = I2C_Speed;
I2C_Init(I2C2, &I2C_InitStructure);
I2C_Cmd(I2C_ID, ENABLE);
}
I2C 프로토콜
I2C Read 동작
아래 블럭도와 같이 코딩을하면된다.
STM32 I2C를 테스트 하면가 가장 고생했던 점은 처음 한번은 잘 동작하고 어떨때 Read가 되지 않은 현상 이 발생 한다. STM32 I2C를 테스트 하면가 가장 고생했던 점은 처음 한번은 잘 동작하고 어떨때발생 한다.
코드상 문제는 없는데… 딜레이도 주고 여러가지 다 해봤지만 원인을 찾을 수가 없었다.
결국 2일을 고생하고 찾아낸 결론은 I2C_Cmd() 함수로 리셋을 해주면 문제없이 잘 동작한다. 그래서 i2c_start()함수 초기에 항상 리셋을 걸고 있다
unsigned char i2c_start(unsigned char address)
{
I2C_Cmd(I2C_ID, DISABLE);
I2C_Cmd(I2C_ID, ENABLE);
큰 부하는 아니지만 속도가 문제라면 함수가 아니라 메크로로 정의해 둘수도 있겠다.
#define CR1_PE_Set ((u16)0x0001)
#define CR1_PE_Reset ((u16)0xFFFE)
I2C_ID->CR1 = CR1_PE_Reset;
I2C_ID->CR1 = CR1_PE_Set;
STM32 I2C에서 또한번 나를 당혹하게한다. I2C를 이용하는 센서 테스트중 Read시에 EV7체크하는 부분이 있는데… 아무리해도 이 값이 세트되지 않는 현상이 발생하기에 Address Write이후 EV6 체크하는 부분을 삭제하니 정상동작한다. 이게 무슨일인지…다른 MCU에서 잘동작히니 디바이스 문제는 아닌것 같고… 아무튼 해결은 되었지만 맘에 들지 않는다 ST예제 STM32 I2C 코드 /* Send address for read */
I2C_Send7bitAddress(I2C1, EEPROM_ADDRESS, I2C_Direction_Receiver);
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)) while(NumByteToRead) { /* Test on EV7 and clear it */
if(I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
{
/* Read a byte from the EEPROM */
*pBuffer = I2C_ReceiveData(I2C1); 내가 수정한 STM32 I2C코드 //Send address for read
I2C_Send7bitAddress(I2C_ID, LIS3L02_I2C_ADDR, I2C_Direction_Receiver); //Test on EV6 and clear it
while(!I2C_CheckEvent(I2C_ID, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED)); while(1)
{
//Test on EV7 and clear it
if( I2C_CheckEvent (I2C_ID, I2C_EVENT_MASTER_BYTE_RECEIVED))
{
data = I2C_ReceiveData(I2C_ID);
break;
}
}
이상하게 STM32 I2C에만 문제가 있네… 연속적인데이터를 수집하려하니 문제가 있다. start()에서 100us정도의 딜레이를 주니 잘동작은 하는데… 아무래도 나중에 속도문제가 될것 같다… unsigned char i2c_start(unsigned char address)
{
Delay_us(100); :
테스트 동영상
기울기에따라 I2C 가속도센서 데이터를 그래프로 표시
테스트 코드 ( STM32 M-Tyep EVM )
Delay없이 사용할 수 있도록 수정한 STM32 최종 I2C start() 함수 소스
unsigned char i2c2_start(unsigned char address)
{
if(address&I2C_READ)
{
/* Send STRAT condition a second time */
I2C_GenerateSTART(I2C2, ENABLE);
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
/* Send EEPROM address for read */
I2C_Send7bitAddress(I2C2, address-1, I2C_Direction_Receiver); 기울기에따라 I2C 가속도센서 데이터를 그래프로 표시Delay없이 사용할 수 있도록 수정한 STM32 최종 I2C start() 함수 소스unsigned char i2c2_start(unsigned char address)if(address&I2C_READ)/* Send STRAT condition a second time */I2C_GenerateSTART(I2C2, ENABLE);/* Test on EV5 and clear it */while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));/* Send EEPROM address for read */I2C_Send7bitAddress(I2C2, address-1, I2C_Direction_Receiver); /* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
}
else
{
/* While the bus is busy */
while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY));
/* Send START condition */
I2C_GenerateSTART(I2C2, ENABLE);
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));
/* Send EEPROM address for write */
I2C_Send7bitAddress(I2C2, address, I2C_Direction_Transmitter); /* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
/* Clear EV6 by setting again the PE bit */
I2C_Cmd(I2C2, ENABLE);
}
return 0;
}
반응형
STM32 – I2C를 이용한 DS3231 제어
사용한 DS3231 모듈
작동 전압 : 2.3~5.5V
통신 : I2C
Fast(400kHz) 모드 지원
배터리 용량이 부족할 경우, 시간 흐름이 부정확해짐
(사용 모듈의 경우 INT/SQW 핀에 풀업 레지스터 구성되어 있음)
[내부 레지스터 주소 및 데이터]DS3231이 제공하는 데이터들 (총 19 바이트)
0x00~0x06 : 현재 설정되어 있는 시간
0x07~0x0A : 현재 설정되어 있는 알람1 시간
0x0B~0x0D : 현재 설정되어 있는 알람2 시간
0x0E~0x10 : 제어 레지스터
0x11~0x12 : 온도 (0x11 : 정수, 0x12 : 소수)
0x02 시간(Hours) 레지스터에서 6비트를 HIGH로 설정하면 12시간, LOW라면 24시간 기준으로 시간을 표시 12시간 모드에선 오후 시간일 때(PM) 5비트가 HIGH 상태가 된다 24시간 모드에선 5비트는 20~23시를 표시하는데 사용된다
[Read]MASTER가 DS3231로부터 데이터를 읽어올 때 레지스터 포인터에 읽어 올 주소를 Write 후 Read 시작 Read 모드 사용시 레지스터 포인터에 대한 주의사항
데이터를 읽어 올 때, SDA 라인의 첫 1바이트는 MSB부터 7 비트는 DS3231(Slave) 주소 + Read(1) 비트로 구성된다
Write 모드 사용시에만 레지스터 포인터가 설정되는데 이는 DS3231로부터 데이터를 읽어오는 시작 위치가 된다
ex) Write 모드 마지막으로 분이 설정된 경우 레지스터 포인터엔 0x01 주소가 설정되고 DS3231로부터 데이터를 읽어올 때 0x00부터 읽어오는게 아니라 레지스터 포인터에 저장된 0x01부터 설정된 크기만큼의 데이터를 읽어온다
따라서 필요에 따라 레지스터 포인터를 원하는 주소로 재설정한 후 Read 를 시작해야 한다
[Write]Master로부터 DS3231로의 데이터 전송
Write 모드일 때, SDA 라인의 첫번째 바이트는 7 비트 Slave 주소 + Write(0) 비트로 구성
그 이후엔 DS3231내의 데이터를 쓰고자 하는 주소(WORD ADDRESS) ((예)초:0x00, 분:0x01, 시:0x02, …), 그리고 그 다음엔 해당 주소에 입력할 값이 와야 한다
WORD ADDRESS 다음에 바로 이어지는 1 바이트 데이터는 WORD ADDRESS 주소에 입력되고 그 이후에 이어지는 데이터는 순서대로 WORD ADDRESS+1, WORD ADDRESS+2, …, WORD ADDRESS+X 의 주소에 저장된다
ex) 3바이트 크기의 배열 : [0]seconds 주소(0x00), [1]seconds, [2]minutes 의 배열을 전송하는 경우, [0]부터 순서대로 DS3231에 전송되어 0x00 주소에 seconds가 입력되고 그 다음으로 주소가 +1되어 0x01에 minutes가 전송됨
Write 모드를 사용할 때 마지막으로 사용된 DS3231 내부 주소(WORD ADDRESS)가 레지스터 포인터에 설정되어 이후 Read 모드 사용시 데이터를 읽어오기 시작하는 시작점으로 사용된다
[CubeMX 설정]I2C 설정
Fast Mode 설정, 7-bit 주소 설정, 인터럽트 활성화
[코드]– 정의
DS3231 타임 레지스터 구성 및 주소
/* Private define ————————————————————*/ /* USER CODE BEGIN PD */ /* DS3231 주소 실제 통신에선 원 주소인 0x68을 MSB 방향으로 1비트 시프트한 값에 Read or Write 비트가 합해져 1바이트가 된다 */ #define ds3231_addr 0x68<<1 #define ds3231_sec_addr 0x00 //DS3231 내 초(sec) 레지스터 주소 #define ds3231_min_addr 0x01 //분(min) 레지스터 주소 #define ds3231_hour_addr 0x02 //시간 #define ds3231_day_addr 0x03 //요일 #define ds3231_date_addr 0x04 //날짜 #define ds3231_month_addr 0x05 //달 #define ds3231_year_addr 0x06 //년도 /* DS3231 시간 레지스터(0x02)의 6번 비트는 12/24 시간 설정, 5번 비트는 AM/PM or 24 시간 표시를 사용할 때 2x 시간일 경우 사용됨 */ #define ds3231_AM 0 //시간 구조체의 시간 부분을 AM으로 설정할 때 사용 #define ds3231_PM 1 //시간 구조체의 시간 부분을 PM으로 설정할 때 사용 #define ds3231_24_hour 2 //DS3231에 쓸 구조체의 시간 부분을 24시간으로 설정할 때 사용 #define ds3231_day 1 //시간 알람 구조체의 날짜/요일 설정 부분을 요일로 설정할 때 사용 #define ds3231_date 0 //시간 알람 구조체의 날짜/요일 설정 부분을 날짜로 설정할 때 사용 #define ds3231_alarm1_addr 0x07 //DS3231 알람1 레지스터 주소 #define ds3231_alarm2_addr 0x0B //DS3231 알람2 레지스터 주소 #define ds3231_ctrl_addr 0x0E //DS3231 컨트롤 레지스터 주소 #define ds3231_temp_addr 0x11 /* USER CODE END PD */ DS3231 주소 및 DS3231 내의 레지스터 주소들 정의 - 구조체 /* 시간 레지스터에서 AM/PM or 24시간 표기 설정에 사용 */ typedef struct { //12시간 표기를 사용할 경우엔 ds3231_AM or define ds3231_PM, 24시간 표기일 경우엔 ds3231_24_hour 입력 uint8_t am_pm_24; //AM or PM or 24시간 표기 사용 설정 uint8_t hour; //12, 24시간 표기에 상관없이 입력할 시간(Hour) 설정 }am_pm_24; /* DS3231로부터 시간을 읽어오거나 시간 데이터를 쓸 때 사용할 시간 구조체 */ typedef struct { uint8_t sec; uint8_t min; am_pm_24 hour_select; uint8_t day; uint8_t date; uint8_t month; uint8_t year; }ds3231_time; /* 알람에서 요일 or 날짜를 쓸건지 구분 지을 구조체 */ typedef struct { uint8_t day_or_date; //ds3231_day or ds3231_day 를 입력해 날짜 or 요일 설정 uint8_t value; //날짜 or 요일값 설정 }day_date; /* 알람1을 읽어오거나 전송할 때 사용할 구조체 */ typedef struct { uint8_t sec; uint8_t min; am_pm_24 hour_select; day_date day_date_select; }ds3231_Alarm1; - 10진수, 2진수 변환 함수 /* USER CODE BEGIN PFP */ int decTobcd(uint8_t dec) { return ((dec/10)*16)+(dec%10); } int bcdTodec(uint8_t bcd) { return (bcd/16*10)+(bcd%16); } DS3231은 2진수를 사용하므로 10진수 사용을 위해선 DS3231로부터 읽어들인 데이터는 10진수로 변환해야 하고 Master 장치로부터 DS3231로 나가는 데이터는 2진화 해야 한다 - 시간 출력 함수, Read 함수 /* DS3231로부터 읽어 온 시간 데이터를 출력하는 함수 시간 영역에 저장된 값(시간 구조체->hour_select->am_pm_24)을 판단해 AM, PM, 24시간 표기로 구분한 뒤 출력 */ void ds3231_print_time(ds3231_time *current_time) { printf(“year : %d month : %d date : %d day : %d “,current_time->year,current_time->month ,current_time->date,current_time->day); switch (current_time->hour_select.am_pm_24) { //12시간 표기의 경우 0~4번 비트까지 유효한 시간 데이터, 5~6번 비트는 시간 표기 설정 비트 case ds3231_AM : printf(“AM : %d “,(current_time->hour_select.hour)&0x1F); break; case ds3231_PM : printf(“PM : %d “,(current_time->hour_select.hour)&0x1F); break; case ds3231_24_hour : printf(“24 : %d “,current_time->hour_select.hour); break; default : break; } printf(“min : %d sec : %d\r
“,current_time->min,current_time->sec); } /* 전달받은 시간 구조체 주소에 DS3231로부터 읽어 온 시간 데이터를 저장하는 함수 */ void ds3231_read_time(ds3231_time *current_time) { uint8_t ds3231_read_time_buff[7]; //DS3231로부터 읽어 올 데이터를 저장할 데이터 버퍼 (초,분,시간,요일,날짜,달,년도-총 7개) /* 특정 메모리 주소에 액세스해서 원하는 갯수만큼 데이터를 읽어와서 데이터 버퍼에 저장 */ HAL_I2C_Mem_Read_IT(&hi2c1,ds3231_addr,ds3231_sec_addr,I2C_MEMADD_SIZE_8BIT,ds3231_read_time_buff,7); //사용할 I2C, DS3231 주소, 접근할 DS3231 레지스터 주소, 메모리 블럭 크기, 읽어 온 데이터를 저장할 버퍼, //읽어 올 데이터 갯수 while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { //수신 완료 때까지 대기 } /* DS3231로부터 읽어 온 데이터를 10진수로 변환한 뒤 전달받은 구조체에 입력 */ current_time->sec=bcdTodec(ds3231_read_time_buff[0]); current_time->min=bcdTodec(ds3231_read_time_buff[1]); /* 시간 레지스터(0x02)로부터 읽어 온 데이터의 6번 비트(0x40), 5번 비트(0x20)을 확인해 High(1)로 설정되있을 경우 구조체 변수에 AM, PM 설정을 한다 (24시간 표기일 경우 6번 비트 Low(0)) */ if((ds3231_read_time_buff[2]&0x40)!=0) //BIT6!=0 이므로 12시간 표기 { if((ds3231_read_time_buff[2]&0x20)==0) //BIT5==0 이므로 AM { current_time->hour_select.am_pm_24=ds3231_AM; } else //BIT5!=0 이므로 PM { current_time->hour_select.am_pm_24=ds3231_PM; } } else { //24시간 표시 설정일 경우 (6번 비트 0, 5번 비트 2x 시간일 때만 1) } current_time->hour_select.hour=bcdTodec(ds3231_read_time_buff[2]); current_time->day=bcdTodec(ds3231_read_time_buff[3]); current_time->date=bcdTodec(ds3231_read_time_buff[4]); current_time->month=bcdTodec(ds3231_read_time_buff[5]); current_time->year=bcdTodec(ds3231_read_time_buff[6]); ds3231_print_time(current_time); //터미널에 출력할 함수 호출 }
사용자가 지정한 메모리 주소(레지스터 주소)로부터 데이터를 읽어오는 함수 사용 (HAL_I2C_Mem_Read_IT)
레지스터 주소를 지정해서 데이터를 읽어오기 때문에 DS3231에 설정된 레지스터 포인터 위치를 신경 쓸 필요가 없다
시간 레지스터(0x02) 자체에 AM/PM or 24시간 표기 설정을 저장할 수 있으므로 필요에 따라 6, 5번 비트를 설정해준다 (비트6 HIGH-12시간 표기, LOW-24시간 표기 / 비트5 LOW-AM, HIGH-PM (24시간 표기 사용할 경우 따로 설정할 필요는 없다. 시간이 2x시간 이상일 때 사용됨))
– Read 예시
DS3231로부터 데이터를 읽어올 때 (0x00(초)~0x04(날짜)) Start + DS3231 주소(7) + Read(1) + ACK bit(1) + Seconds(8) + ACK(1) + Minutes(8) Minutes (8) + ACK bit (1) + Hours (8) + ACK bit (1) Temp_MSB(8) + Temp_LSB(8) + NACK bit + Stop
SCL 라인이 HIGH일 때, SDA 라인이 HIGH에서 LOW로 이동하는 것을 스타트 상태로 규정
I2C 통신의 첫 데이터는 Slave 장치 주소가 온다
DS3231의 경우 1101000(0x68)의 주소를 갖는데 MSB부터 채워져야 하므로 0xD0 or 0x68<<1 을 사용해야 한다 을 사용해야 한다 7-bit 주소를 사용한다면 MSB부터 주소가 채워지고 마지막 0 bit 자리에 Read(1) 또는 Write(0) 비트가 채워진다 전송이 중단되기 전까지는 SCL 라인의 9번째 비트에 상응하는 SDA 라인의 9번째 비트(Acknowledge bit)가 LOW를 유지 마지막 Temp(LSB) 데이터 전송 후, SCL 라인의 9번째 신호에서 SDA 라인이 HIGH를 유지하며 Not Acknowledge 상태를 나타내고 Master가 전송을 중단하기 위해 STOP 상태 발생 시킴 해당 데이터값 (날짜/요일/시간/분/초) - Write 함수 /* 전달받은 구조체 주소를 통해 DS3231에 전송할 시간 데이터 버퍼를 채움 */ void ds3231_write_time(ds3231_time *ds3231_write_time_struct) { uint8_t write_buf[7]; //전송에 사용할 데이터 버퍼 배열 선언 //입력한 10진수 시간 데이터를 2진수화 write_buf[0]=decTobcd(ds3231_write_time_struct->sec); write_buf[1]=decTobcd(ds3231_write_time_struct->min); write_buf[2]=decTobcd(ds3231_write_time_struct->hour_select.hour); write_buf[3]=decTobcd(ds3231_write_time_struct->day); write_buf[4]=decTobcd(ds3231_write_time_struct->date); write_buf[5]=decTobcd(ds3231_write_time_struct->month); write_buf[6]=decTobcd(ds3231_write_time_struct->year); /* 24시간 표기가 아닌 12시간 표기(AM/PM)을 사용하는 경우엔 DS3231 시간(Hour) 레지스터(0x02)에 씌여질 데이터에 추가로 6, 5번 비트를 설정해줘야 한다 */ switch(ds3231_write_time_struct->hour_select.am_pm_24) { case ds3231_AM : //AM인 경우 6번 비트(12/24)만 High로 설정해주면 된다 write_buf[2]|=0x40; break; case ds3231_PM : //PM인 경우 6, 5번 비트를 High로 설정해줘야 한다 write_buf[2]|=0x60; break; case ds3231_24_hour : break; default : break; } /* DS3231 초(Sec) 레지스터(0x00)부터 7개의 8비트 데이터 배열을 입력 각 타임 레지스터는 8비트의 크기를 가지므로 0x00부터 0x06까지 순차적으로 8비트 데이터 7개가 입력된다 */ HAL_I2C_Mem_Write_IT(&hi2c1,ds3231_addr,ds3231_sec_addr,I2C_MEMADD_SIZE_8BIT, write_buf,7); while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { //입력 완료까지 대기 } } int main(void) { … ds3231_time ds_time_default; //구조체 변수 선언 //구조체 변수 데이터 입력 ds_time_default.sec=0; ds_time_default.min=43; ds_time_default.hour_select.am_pm_24=ds3231_PM; ds_time_default.hour_select.hour=7; ds_time_default.day=6; ds_time_default.date=21; ds_time_default.month=12; ds_time_default.year=19; ds3231_write_time(&ds_time_default); //원하는 시간으로 설정한 구조체 변수 DS3231에 전송 }
사용자가 지정한 시간 위치를 사용자가 입력한 데이터로 변경
0x00 번지에 액세스해 8비트 크기의 배열 7개를 입력한다 (레지스터 주소는 8비트 입력 후 자동으로 1씩 증가한다)
– 예시
int main(void) { … ds3231_time ds_time_default; //구조체 변수 선언 ds3231_read_time(&ds_time_default); //현재 DS3231에 저장되어있는 시간 데이터를 읽어와 선언한 구조체 변수에 저장 //DS3231에 새롭게 입력할 시간 데이터들을 설정 (위에서 선언한 변수 사용) ds_time_default.sec=0; ds_time_default.min=43; ds_time_default.hour_select.am_pm_24=ds3231_PM; ds_time_default.hour_select.hour=7; ds_time_default.day=6; ds_time_default.date=21; ds_time_default.month=12; ds_time_default.year=19; ds3231_write_time(&ds_time_default); //구조체를 이용해 DS3231에 시간 데이터 입력 ds3231_read_time(&ds_time_default); //시간이 변경됐는지 확인하기 위해 타임 레지스터에 저장되어있는 시간 데이터 읽어옴 … }
첫줄 : 기존에 저장되어있던 시간 / 두번째 줄 : 변경 직후. 이후부턴 새로 설정된 시간에 기반해 작동
[알람]알람 레지스터 테이블
DS3231엔 두 개의 알람이 존재한다
알람1 – 초, 분, 시간, 요일 or 날짜. 총 4개의 데이터
알람2 – 분, 시간, 요일 or 날짜. 총 3개의 데이터
설정에 따라 타임 키핑 레지스터(DS3231에서 진행 중인 시간)값이 알람 시간과 일치하면 INT/SQW 핀이 High 에서 Low로 전환되면서 인터럽트가 발생한다
INT/SQW핀을 사용하려면 INT/SQW핀에 외부 풀업 레지스터를 연결해야 한다
인터럽트 감지에 사용될 GPIO 설정 (PINB4, 인터럽트 모드(Falling edge 감지), 풀업/풀다운 X)
– 알람 1
알람1 레지스터 테이블 알람1 마스크 비트 설정에 따른 알람 매칭
알람1은 DS3231내 0x07 부터 0x0A까지 총 4바이트의 주소를 사용한다
0x09에 저장되는 시간(Hour) 데이터의 6번 및 5번 비트 설정에 따라 12시간/24시간 표기 및 AM, PM이 설정된다
0x0A에 저장되는 데이터의 6번 비트 설정에 따라 날짜를 기준으로 알람을 설정할지 아니면 요일을 기준으로 설정할지가 결정된다
각 알람1 레지스터의 7번 비트 설정에 따라 (테이블 순서대로)
1. 매 초마다 알람
2. 타임 키핑 레지스터에 저장되어있는 시간과 알람1에 저장된 초가 일치할 때 알람
3. 타임 키핑 레지스터에 저장되어있는 시간과 알람1에 저장된 분, 초가 일치할 때 알람
4. 타임 키핑 레지스터에 저장되어있는 시간과 알람1에 저장된 시간, 분, 초가 일치할 때 알람
5. 타임 키핑 레지스터에 저장되어있는 시간과 알람1에 저장된 날짜, 시간, 분, 초가 일치할 때 알람
6. 타임 키핑 레지스터에 저장되어있는 시간과 알람1에 저장된 요일, 시간, 분, 초가 일치할 때 알람
– 알람1 설정 함수
void ds3231_set_alarm1(ds3231_Alarm1 *alarm1_data) { uint8_t alarm1_buff[4]; //DS3231에 전송할 알람1 데이터 버퍼 alarm1_buff[0]=decTobcd(alarm1_data->sec); //초 alarm1_buff[1]=decTobcd(alarm1_data->min); //분 alarm1_buff[2]=decTobcd(alarm1_data->hour_select.hour); //시간 alarm1_buff[3]=decTobcd(alarm1_data->day_date_select.value); //날짜 or 요일값 /* 12/24 시간 및 AM, PM 설정 */ switch(alarm1_data->hour_select.am_pm_24) { case ds3231_AM : //전달된 구조체의 알람 시간이 AM일 경우 alarm1_buff[2]|=0x40; //BIT6 Logic High=12시간 표기, BIT5 Logic Low=AM break; case ds3231_PM : //전달된 구조체의 알람 시간이 PM일 경우 alarm1_buff[2]|=0x60; //BIT6 Logic High=12시간 표기, BIT5 Logic High=PM break; case ds3231_24_hour : //전달된 구조체의 알람 시간이 24시간 표기일 경우 (별도 설정 필요 X) break; default : break; } /* 알람 기준을 요일로 할지 아니면 날짜로 할지를 설정 */ switch(alarm1_data->day_date_select.day_or_date) { case ds3231_date : //날짜 설정, BIT6 – Low break; case ds3231_day : alarm1_buff[3]|=0x40; //요일 설정, BIT6 – High break; default : break; } HAL_I2C_Mem_Write_IT(&hi2c1,(uint16_t)ds3231_addr,(uint16_t)ds3231_alarm1_addr,I2C_MEMADD_SIZE_8BIT, (uint8_t*)alarm1_buff,4); //ds3231_alarm1_addr (0x07)에 액세스해 각 1바이트 크기를 가진 알람1 시간 데이터 4개를 전송 while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { //전송 완료까지 대기 } }
– DS3231에 저장된 알람1 데이터를 읽어오는 함수와 알람1 구조체 출력 함수
/* 전달받은 구조체에 저장된 알람1 시간을 출력하는 함수 */ void ds3231_print_alarm1(ds3231_Alarm1 *current_alarm1) { switch (current_alarm1->day_date_select.day_or_date) //0x0A – Alarm1 DAY/DATE Register { case ds3231_date : //BIT6 Low – Date printf(“Date : %d “,(current_alarm1->day_date_select.value)&0x3F); break; case ds3231_day : //BIT6 High – Day printf(“Day : %d “,(current_alarm1->day_date_select.value)&0x0F); break; default : break; } switch (current_alarm1->hour_select.am_pm_24) //0x09 – Alarm1 Hour Register { case ds3231_AM : //BIT6 – High, BIT5 – Low printf(“AM : %d “,current_alarm1->hour_select.hour); break; case ds3231_PM : //BIT6 – High, BIT5 – High printf(“PM : %d “,current_alarm1->hour_select.hour); break; case ds3231_24_hour : //BIT6 – Low, BIT5 – 2x 시간일 때 High printf(“24 : %d “,current_alarm1->hour_select.hour); break; default : break; } printf(“min : %d sec : %d\r
“,current_alarm1->min,current_alarm1->sec); } /* DS3231로부터 읽어 온 알람1 시간을 전달받은 구조체에 입력하는 함수 */ void ds3231_read_alarm1(ds3231_Alarm1 *current_alarm1) { uint8_t read_alarm1_buff[4]; //DS3231로부터 읽어 온 알람1 시간을 저장할 데이터 버퍼 HAL_I2C_Mem_Read_IT(&hi2c1,ds3231_addr,ds3231_alarm1_addr,I2C_MEMADD_SIZE_8BIT,read_alarm1_buff,4); //ds3231_alarm1_addr (0x07)에 액세스해 0x0A까지 1바이트 크기를 가진 4개의 알람1 데이터를 읽어 옴 while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { //수신 완료까지 대기 } /* 수신한 데이터를 10진수로 변환 후 전달받은 구조체에 입력 */ current_alarm1->sec=bcdTodec(read_alarm1_buff[0]); current_alarm1->min=bcdTodec(read_alarm1_buff[1]); if((read_alarm1_buff[2]&0x40)!=0) //12시간 표기를 사용할 경우 { if((read_alarm1_buff[2]&0x20)==0) //알람 설정 시간이 AM일 경우 { current_alarm1->hour_select.am_pm_24=ds3231_AM; current_alarm1->hour_select.hour=bcdTodec(read_alarm1_buff[2]&0x1F); //12시간 표기를 사용할 경우, 시간 레지스터에서 유효한 시간 데이터는 BIT 0 ~ BIT 4 까지 } else //알람 설정 시간이 PM일 경우 { current_alarm1->hour_select.am_pm_24=ds3231_PM; current_alarm1->hour_select.hour=bcdTodec(read_alarm1_buff[2]&0x1F); } } else //24시간 표기 { current_alarm1->hour_select.hour=bcdTodec(read_alarm1_buff[2]); } if((read_alarm1_buff[3]&0x40)!=0) //Day일 경우 (BIT 6 – High) { current_alarm1->day_date_select.day_or_date=ds3231_day; current_alarm1->day_date_select.value=bcdTodec(read_alarm1_buff[3]&0x0F); //Day 사용할 경우, 유효한 날짜 데이터는 BIT 0 ~ BIT 3까지 } else //Date일 경우 (BIT 6 – Low) { current_alarm1->day_date_select.day_or_date=ds3231_date; current_alarm1->day_date_select.value=bcdTodec(read_alarm1_buff[3]&0x3F); //Date 사용할 경우, 유효한 날짜 데이터는 BIT 0 ~ BIT 5까지 } ds3231_print_alarm1(current_alarm1); //읽어 온 알람1 데이터를 출력하기 위한 함수 호출 }
– 알람1 활성화 함수
/* 알람1을 활성화 or 비활성화하는 함수 Control(0x0E), Control/Status(0x0F)에 현재 설정되어있는 값을 읽어 온 뒤 이 값에 활성화 또는 비활성화에 필요한 설정값만을 수정&입력하고 이 값을 DS3231에 재전송 */ void ds3231_enable_alarm1(int enable) { uint8_t ctrl_stat_buff[2]; //읽어 온 Control(0x0E), Contorl/Status(0x0F) 값을 저장할 데이터 버퍼 배열 HAL_I2C_Mem_Read_IT(&hi2c1,ds3231_addr,ds3231_ctrl_addr,I2C_MEMADD_SIZE_8BIT,ctrl_stat_buff,2); //컨트롤 레지스터(0x0E)에 액세스해 컨트롤/스테이터스(0x0F) 레지스터까지 2바이트 데이터를 읽어온다 while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { //수신 완료까지 대기 } if(enable==0) //알람1 비활성화 { ctrl_stat_buff[0]&=~(0x01); //컨트롤 레지스터 비트0 A1IE 비활성화 } else //알람1 활성화 { ctrl_stat_buff[0]|=(0x05); //컨트롤 레지스터 비트3 INTCN, 비트0 A1IE 활성화 ctrl_stat_buff[1]&=~(0x03); //컨트롤/스테이터스 레지스터 비트1(A2F), 비트0(A1F) Clear } HAL_I2C_Mem_Write_IT(&hi2c1,ds3231_addr,ds3231_ctrl_addr,I2C_MEMADD_SIZE_8BIT,ctrl_stat_buff,2); //컨트롤 레지스터(0x0E)에 액세스해 필요에 따라 수정된 컨트롤, 컨트롤/스테이터스 레지스터 값을 전송 while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { //전송 완료까지 대기 } }
컨트롤 레지스터 (0x0E) 스테이터스 레지스터 (0x0F)
알람1을 사용하기 위해선 컨트롤 레지스터(0x0E)에 A1IE(Alarm 1 Interrupt Enable) 비트와 INTCN (Interrupt Control) 비트를 활성화(Logic 1) 해야 한다
위 설정 상태에서 타임 키핑 레지스터에 저장된 값과 알람1에 저장된 값이 일치할 때, 스테이터스 레지스터(0x0F)의 A1F (Alarm 1 Flag)비트가 Logic 1로 변경되고 High 상태를 유지하던 INT/SQW 핀이 Low로 전환되면서 인터럽트가 발생한다
(A1F=1 일 때, SQW-HIGH / A1F=0 일 때, SQW-LOW)
알람을 재활성화 하기 위해선 A1F 비트를 0으로 재설정해야 한다
– 예시
int main(void) { … ds3231_time ds_time_default; //시간 구조체 변수 선언 //DS3231에 입력할 구조체 변수 시간 설정 ds_time_default.sec=50; ds_time_default.min=43; ds_time_default.hour_select.am_pm_24=ds3231_PM; ds_time_default.hour_select.hour=7; ds_time_default.day=6; ds_time_default.date=21; ds_time_default.month=12; ds_time_default.year=19; ds3231_write_time(&ds_time_default); //DS3231에 입력한 구조체 변수 값들을 전송 ds3231_Alarm1 alarm1_default; //알람1 구조체 변수 선언 ds3231_read_alarm1(&alarm1_default); //DS3231에 현재 설정된 알람1 데이터 읽어오는 함수 호출 //DS3231에 입력할 알람1 구조체 변수 시간 설정 alarm1_default.sec=0; alarm1_default.min=44; alarm1_default.hour_select.am_pm_24=ds3231_PM; alarm1_default.hour_select.hour=7; alarm1_default.day_date_select.value=6; alarm1_default.day_date_select.day_or_date=ds3231_day; ds3231_set_alarm1(&alarm1_default); //DS3231에 입력한 알람1 구조체 변수 값들 전송 ds3231_read_alarm1(&alarm1_default); //DS3231에 설정된 알람1 값 읽어옴 ds3231_enable_alarm1(1); //알람1 활성화 (0 입력시 비활성화) while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ ds3231_read_time(&ds_time_default); HAL_Delay(1000); } } /* USER CODE BEGIN 4 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { printf(“external interrupt occurred\r
“); if (GPIO_Pin == Alarm_Interrupt_Line_Pin) { printf(“Alarm interrupt occurred\r
“); HAL_GPIO_TogglePin(LD1_GPIO_Port,LD1_Pin); } }
알람1으로 설정한 Day 6, PM 7, 44분, 0초에 인터럽트가 발생 INT/SQW 핀이 HIGH 상태를 유지하다 타임 키핑 레지스터에 저장된 시간과 알람1에 설정된 시간이 일치하면서 LOW로 전환되어 인터럽트 발생
A1F를 0으로 재설정해주지 않는다면 INT/SQW 핀은 LOW 상태를 계속 유지한다
따라서 알람을 재활성화 하기 위해선 A1F 비트를 0으로 재설정해줘야 한다
– 알람2
알람2 레지스터 테이블 알람2 마스크 비트 테이블
0x0B~0x0D까지 1바이트씩 총 3바이트의 레지스터 주소를 사용
알람1과의 차이점은 초를 설정할 수 없다는 점이다 (분, 시간, 요일 or 날짜. 세 개만 설정 가능)
그외에 컨트롤 레지스터에서 A2IE 비트, 스테이터스 비트에서 A2F 비트를 설정해줘야 한다는 것과
마스크 비트 설정에 따른 알람 차이점이 존재한다 (마스크 비트 테이블 순서대로)
1. 매 분마다 알람 발생 (매 분 00초)
2. 타임 키핑 레지스터에 저장되어있는 시간과 알람2에 저장된 분이 일치할 때 알람
3. 타임 키핑 레지스터에 저장되어있는 시간과 알람2에 저장된 시간, 분이 일치할 때 알람
4. 타임 키핑 레지스터에 저장되어있는 시간과 알람2에 저장된 날짜, 시간, 분이 일치할 때 알람
5. 타임 키핑 레지스터에 저장되어있는 시간과 알람2에 저장된 요일, 시간, 분이 일치할 때 알람
– 알람2 코드 (알람1에서 초를 설정하는 부분이 빠진 것과 알람2 설정에 따른 레지스터 주소 변경 외엔 차이점이 없다)
void ds3231_print_alarm2(ds3231_Alarm2 *current_alarm2) { switch (current_alarm2->day_date_select.day_or_date) { case ds3231_date : //bit6 Low printf(“Date : %d “,(current_alarm2->day_date_select.value)&0x3F); break; case ds3231_day : //bit6 High printf(“Day : %d “,(current_alarm2->day_date_select.value)&0x0F); break; default : break; } switch (current_alarm2->hour_select.am_pm_24) { case ds3231_AM : printf(“AM : %d “,current_alarm2->hour_select.hour); break; case ds3231_PM : printf(“PM : %d “,current_alarm2->hour_select.hour); break; case ds3231_24_hour : printf(“24 : %d “,current_alarm2->hour_select.hour); break; default : break; } printf(“min : %d\r
“,current_alarm2->min); } void ds3231_read_alarm2(ds3231_Alarm2 *current_alarm2) { uint8_t read_alarm2_buff[3]; HAL_I2C_Mem_Read_IT(&hi2c1,ds3231_addr,ds3231_alarm2_addr,I2C_MEMADD_SIZE_8BIT,read_alarm2_buff,3); while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { } current_alarm2->min=bcdTodec(read_alarm2_buff[0]); if((read_alarm2_buff[1]&0x40)!=0) { if((read_alarm2_buff[1]&0x20)==0) { current_alarm2->hour_select.am_pm_24=ds3231_AM; current_alarm2->hour_select.hour=bcdTodec(read_alarm2_buff[1]&0x1F); } else { current_alarm2->hour_select.am_pm_24=ds3231_PM; current_alarm2->hour_select.hour=bcdTodec(read_alarm2_buff[1]&0x1F); } } else { current_alarm2->hour_select.hour=bcdTodec(read_alarm2_buff[2]); //24시간 표기 } if((read_alarm2_buff[2]&0x40)!=0) //Day or Date { current_alarm2->day_date_select.day_or_date=ds3231_day; //Day=BIT6 High current_alarm2->day_date_select.value=bcdTodec(read_alarm2_buff[2]&0x0F); } else { current_alarm2->day_date_select.day_or_date=ds3231_date; //Date=BIT6 Low current_alarm2->day_date_select.value=bcdTodec(read_alarm2_buff[2]&0x3F); } ds3231_print_alarm2(current_alarm2); } void ds3231_set_alarm2(ds3231_Alarm2 *alarm2_data) { uint8_t alarm2_buff[4]; alarm2_buff[0]=decTobcd(alarm2_data->min); alarm2_buff[1]=decTobcd(alarm2_data->hour_select.hour); alarm2_buff[2]=decTobcd(alarm2_data->day_date_select.value); switch(alarm2_data->hour_select.am_pm_24) { case ds3231_AM : alarm2_buff[1]|=0x40; //BIT5 AM/PM, Logic High=PM break; case ds3231_PM : alarm2_buff[1]|=0x60; //BIT6 12/24, Logic High=12 break; case ds3231_24_hour : break; default : break; } switch(alarm2_data->day_date_select.day_or_date) { case ds3231_date : break; case ds3231_day : alarm2_buff[2]|=0x40; //day-BIT6 High break; default : break; } HAL_I2C_Mem_Write_IT(&hi2c1,(uint16_t)ds3231_addr,(uint16_t)ds3231_alarm2_addr,I2C_MEMADD_SIZE_8BIT, (uint8_t*)alarm2_buff,3); while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { } } void ds3231_enable_alarm2(int enable) { uint8_t ctrl_stat_buff[2]; HAL_I2C_Mem_Read_IT(&hi2c1,ds3231_addr,ds3231_ctrl_addr,I2C_MEMADD_SIZE_8BIT,ctrl_stat_buff,2); while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { } if(enable==0) { ctrl_stat_buff[0]&=~(0x02); //A2IE disable } else { ctrl_stat_buff[0]|=(0x06); //INTCN, A2IE enable ctrl_stat_buff[1]&=~(0x03); //A1F, A2F Clear } HAL_I2C_Mem_Write_IT(&hi2c1,ds3231_addr,ds3231_ctrl_addr,I2C_MEMADD_SIZE_8BIT,ctrl_stat_buff,2); while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { } }
– 알람2 예시
int main(void) { … ds3231_time ds_time_default; ds_time_default.sec=50; ds_time_default.min=29; ds_time_default.hour_select.am_pm_24=ds3231_AM; ds_time_default.hour_select.hour=10; ds_time_default.day=6; ds_time_default.date=21; ds_time_default.month=12; ds_time_default.year=19; ds3231_write_time(&ds_time_default); /* 알람2 구조체 변수 선언 및 설정 후 DS3231에 전송 */ ds3231_Alarm2 alarm2_default; alarm2_default.min=30; alarm2_default.hour_select.am_pm_24=ds3231_AM; alarm2_default.hour_select.hour=10; alarm2_default.day_date_select.day_or_date=ds3231_date; alarm2_default.day_date_select.value=21; ds3231_set_alarm2(&alarm2_default); ds3231_read_alarm2(&alarm2_default); ds3231_enable_alarm2(1); while (1) { /* USER CODE END WHILE */ /* USER CODE BEGIN 3 */ ds3231_read_time(&ds_time_default); HAL_Delay(1000); } } /* USER CODE BEGIN 4 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { printf(“external interrupt occurred\r
“); if (GPIO_Pin == Alarm_Interrupt_Line_Pin) { printf(“Alarm interrupt occurred\r
“); HAL_GPIO_TogglePin(LD1_GPIO_Port,LD1_Pin); } }
설정된 알람2 값이 타임 키핑 레지스터에 저장된 값과 일치하면서 인터럽트 발생
알람2도 알람1과 같이 인터럽트 발생 후 스테이터스 레지스터의 A2F 비트를 0으로 재설정해줘야 알람이 재활성화된다
– 마스크 비트 단순 예시 (매 초마다 알람 인터럽트가 발생하도록 설정했을 때)
int alarm_every_sec=0; //알람 인터럽트 발생 후 알람 재활성화 하기 위해 사용할 전역 변수 /* 매 초마다 알람을 발생 시키려면 알람1의 모든 시간 레지스터들의 7번 비트를 1로 설정해줘야 한다 */ void ds3231_alarm_every_sec() { uint8_t alarm_buff[4]={0x80,0x80,0x80,0x80}; //매 초마다 알람 발생 시키기 위한 마스크 비트 설정 HAL_I2C_Mem_Write_IT(&hi2c1,ds3231_addr,ds3231_alarm1_addr,I2C_MEMADD_SIZE_8BIT,alarm_buff,4); while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { } } int main(void) { … ds3231_enable_alarm1(1); //매 초마다 알람을 발생 시킬 수 있는 건 알람1 뿐이다 (알람2는 매 분마다) ds3231_alarm_every_sec(); while(1) { if((HAL_I2C_GetState(&hi2c1)==HAL_I2C_STATE_READY)&&(alarm_every_sec==0)) { ds3231_read_time(&ds_time_default); /* 알람 발생 이후 A1F=1, INT/SQW핀=LOW 상태를 유지해 INT/SQW핀을 통한 새 인터럽트가 발생하지 못하므로 A1F를 0으로 재설정해 INT/SQW핀을 다시 HIGH 상태로 만들어 준다 */ ds3231_enable_alarm1(1); alarm_every_sec=1; } } } /* USER CODE BEGIN 4 */ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { printf(“external interrupt occurred\r
“); if (GPIO_Pin == Alarm_Interrupt_Line_Pin) { printf(“Alarm interrupt occurred\r
“); alarm_every_sec=0; } }
매 초마다 알람 인터럽트가 발생하도록 설정한 결과 알람 인터럽트 발생 후, 스테이터스 레지스터의 A1F 비트 값을 0으로 재설정해 INT/SQW 핀을 다시 HIGH로 만들어 다음 인터럽트가 발생할 수 있게끔 만들어준다
– 온도
온도 레지스터
DS3231의 온도 레지스터엔 64초를 주기로 갱신되는 0.25℃ 해상도를 가진 온도값이 저장된다
정수 부분(레지스터 0x11), 소수 부분(0x12) 두 부분으로 나뉘어 온도값이 저장된다
소수 부분은 상위 2비트(비트6, 비트7)만 사용된다
float ds3231_temperature; //읽어 온 온도값을 저장할 float 전역 변수 void ds3231_read_temperature() { uint8_t ds3231_temp_buff[2]; //상위 8비트, 하위 8비트를 읽어와야 하므로 2개의 배열을 가진 변수 생성 HAL_I2C_Mem_Read_IT(&hi2c1,ds3231_addr,ds3231_temp_addr,I2C_MEMADD_SIZE_8BIT, ds3231_temp_buff,2); //ds3231_temp_addr(0x11)에 액세스해 정수(0x11), 소수(0x12) 레지스터를 읽어온다 while(HAL_I2C_GetState(&hi2c1)!=HAL_I2C_STATE_READY) { } ds3231_temperature=ds3231_temp_buff[0]+((ds3231_temp_buff[1]>>6)*0.25); //온도값을 저장하기 위해 선언한 float 전역 변수에 정수 부분과 소수 부분을 합산해 저장한다 //소수 부분은 상위 2비트에 소수 온도 데이터가 저장되므로 이를 LSB 방향으로 6비트 비트 시프트 한 뒤 계산 //00=0.00, 01=0.25, 10=0.5, 11=0.75 printf(“%.2f\r
“,ds3231_temperature); }
TrueStudio를 사용하는 경우, printf를 통한 float 변수 출력이 제대로 되지 않는 문제가 있는데 이는 프로젝트 설정을 변경해줌으로써 해결할 수 있다
https://www.google.co.kr/search?newwindow=1&sxsrf=ACYBGNRQK8mHRqzyYmt66AzcjuymmnkLpg%3A1577109903830&source=hp&ei=j8kAXt2iMKH2hwOYvrfQBA&q=truestudio+float&oq=truestudio+float&gs_l=psy-ab.3..0j0i30l2j0i8i30j0i333j0i5i30l2.203.2123..2284…0.0..0.124.1685.1j15……0….1..gws-wiz…….0i131.oml1Wn25NWA&ved=0ahUKEwidptmc-MvmAhUh-2EKHRjfDUoQ4dUDCAY&uact=5
[Hal, Cubemx, Truestudio를 이용한 Stm32F4 속성 강의 강좌] 9강. I2C 통신(At24C04 Eeprom) | stm32 i2c 통신 예제 새로운 업데이트
We are using cookies to give you the best experience on our website.
You can find out more about which cookies we are using or switch them off in settings.
STM32. I2C 사용하기.
CubeMX 프로그램에서 Pinout탭에서 I2C1 에서 I2C 선택한다(아래 붉박), 그럼 I2C SDA, SCL 핀이 자동으로 지정된다(아래 파박).
I2C 설정하기. 탭 “Configuration” 에서 I2C1 클릭하여,
탭 “Parameter Settings” 에서 I2C통신속도 선택하여 아래 붉박은 Standard Mode 를 선택한 경우이다. 이 경우의 I2C주파수는 100kHz 로 자동 설정된다. Fast 모드를 선택하면 400kHz, Fast Mode Plus 를 선택하면 1000kHz 로 I2C 주파수가 자동설정된다. 개발대상 시스템의 I2C 버스에 물려있는 모든 Slave 들의 가능한 지원속도에 맞게 선택하면된다.
I2C 핀의 Pull-up 설정.
I2C로 핀이 할당되면 자동으로 풀업으로 설정된다. 외부회로상에서 I2C 버선 선 2개에 별도로 풀업저항 달지 않아도 되므로 편하다.
여기까지가 CubeMX 에서 I2C 관련 설정의 전부임. 이후 I2C 설정을 프로젝트에 반영하기 위하여
메뉴 : Project -> Generate Code 클릭하여 프로젝트에 반영시킨다.
Getting Started with STM32 – I2C Example
Getting Started with STM32 – I2C Example
The STM32 line of microcontrollers are a popular implementation of the ARM Cortex-M core from STMicroelectronics. As with most microcontrollers, almost all STM32 parts come equipped with 1 (or more!) I2C interfaces.
If you have not set up STM32CubeIDE with your Nucleo board, you will need to do so following the steps outlined in this tutorial.
If video is your preferred medium, check out this video for how to use I2C with STM32:
Required Components
You will need the following components to complete this tutorial: https://www.digikey.com/short/p59jrr
Note that any Nucleo board may be used, but steps are shown for the Nucleo-L476RG.
TMP102 and I2C
The TMP102 is an extremely simple digital temperature sensor from Texas Instruments. It relies on I2C to communicate data to and from a host device. I recommend the SparkFun TMP102 Breakout Board to test the device.
Inter-Integrated Circuit (I2C) is a communication bus protocol developed by Philips Semiconductor (now NXP Semiconductors) in 1982. It is a relatively slow protocol but has seen widespread use due to its simplicity and robustness. Most microcontrollers have at least 1 I2C peripheral controller built in to the silicon.
If you would like to learn more about the I2C protocol, I recommend this tutorial from SparkFun.
By looking at the TMP102 datasheet, we can determine that retrieving temperature data from the temperature register would require a set of I2C write/read commands as follows:
Note that we need to first send out a write command from the STM32 to the TMP102 with 1 byte that contains the address of the temperature register in the TMP102 (address 0x00). We immediately follow that with a read command where we read 2 bytes from the TMP102. These 2 bytes will contain the temperature data.
Hardware Hookup
Connect your Nucleo to the TMP102 as shown in the following Fritzing diagram:
Create a New Project in STM32CubeIDE
Open STM32CubeIDE and click File > New > STM32 Project. Select the Nucleo-L476RG (or your preferred board) and name your project. Stick with the C target language. In the Pinout & Configuration window, assign PB8 and PB9 to I2C1_SCL and I2C1_SDA functions, respectively.
The pins should be colored yellow, which indicates that while the pins are assigned to a peripheral, that peripheral has not yet been initialized in the CubeMX software. On the left pane in CubeMX, select Categories > Connectivity > I2C1. In the Mode pane, change I2C Disable to I2C.
You should see the PB8 and PB9 pins turn green, indicating that we’ve fully configured the I2C peripheral.
Click File > Save and click Yes to generate code.
The Code
Open up main.c, and modify it to look like the following:
Copy Code /* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
*
© Copyright (c) 2019 STMicroelectronics. * All rights reserved.
*
* This software component is licensed by ST under BSD 3-Clause license,
* the “License”; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ——————————————————————*/
#include “main.h”
/* Private includes ———————————————————-*/
/* USER CODE BEGIN Includes */
#include
#include
/* 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 ———————————————————*/
I2C_HandleTypeDef hi2c1;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
static const uint8_t TMP102_ADDR = 0x48 << 1; // Use 8-bit address static const uint8_t REG_TEMP = 0x00; /* USER CODE END PV */ /* Private function prototypes -----------------------------------------------*/ void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_USART2_UART_Init(void); static void MX_I2C1_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 */ HAL_StatusTypeDef ret; uint8_t buf[12]; int16_t val; float temp_c; /* 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 */ /* USER CODE END SysInit */ /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_USART2_UART_Init(); MX_I2C1_Init(); /* USER CODE BEGIN 2 */ /* USER CODE END 2 */ /* Infinite loop */ /* USER CODE BEGIN WHILE */ while (1) { // Tell TMP102 that we want to read from the temperature register buf[0] = REG_TEMP; ret = HAL_I2C_Master_Transmit(&hi2c1, TMP102_ADDR, buf, 1, HAL_MAX_DELAY); if ( ret != HAL_OK ) { strcpy((char*)buf, "Error Tx\r "); } else { // Read 2 bytes from the temperature register ret = HAL_I2C_Master_Receive(&hi2c1, TMP102_ADDR, buf, 2, HAL_MAX_DELAY); if ( ret != HAL_OK ) { strcpy((char*)buf, "Error Rx\r "); } else { //Combine the bytes val = ((int16_t)buf[0] << 4) | (buf[1] >> 4);
// Convert to 2’s complement, since temperature can be negative
if ( val > 0x7FF ) {
val |= 0xF000;
}
// Convert to float temperature value (Celsius)
temp_c = val * 0.0625;
// Convert temperature to decimal format
temp_c *= 100;
sprintf((char*)buf,
“%u.%u C\r
“,
((unsigned int)temp_c / 100),
((unsigned int)temp_c % 100));
}
}
// Send out buffer (temperature or error message)
HAL_UART_Transmit(&huart2, buf, strlen((char*)buf), HAL_MAX_DELAY);
// Wait
HAL_Delay(500);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
// Rest of auto-generated Cube functions
// …
Let’s take a look at the I2C portion of the code. To begin reading temperature, we first send a write request to the TMP102 with the following HAL API function:
Copy Code ret = HAL_I2C_Master_Transmit(&hi2c1, TMP102_ADDR, buf, 1, HAL_MAX_DELAY);
We pass in a handle to our I2C typedef with &hi2c1 and the address of the TMP102 on the bus. Note that the address should be 0x48 (with A0 tied to ground on the TMP102), but we need to left-shift that byte by 1 bit, as we are using a 7-bit address. We also pass in our buffer, which is simply a uint8_t array, along with the number of bytes we wish to send (1 byte). Because this is a blocking function, we need to tell it how long to wait before giving up. We will use HAL_MAX_DELAY, which comes out to be something like 50 days. Feel free to use your own timeout period here.
The return value, which we store in ret, is a common HAL Status type. If the returned value is not equal to HAL_OK, we transmit an error message to the console. Otherwise, we continue to the read function:
Copy Code ret = HAL_I2C_Master_Receive(&hi2c1, TMP102_ADDR, buf, 2, HAL_MAX_DELAY);
This works similarly to the transmit function. We provide the I2C peripheral handle and the address of the TMP102 on the bus. While we pass in the same buffer, it will be used as an output. When the function returns, if all goes well, the bytes read from the TMP102 will be stored in that buffer. Since we expect 2 bytes from the TMP102 (temperature data is sent as 12 bits), we tell the function that we want to read 2 bytes. Finally, we use the HAL_MAX_DELAY to essentially have no timeout.
Again, we hope to have HAL_OK as our return value. If so, we can expect the buffer, buf, to contain our temperature data. We can then perform some math to transform the 12-bit value into a human readable format, like Celsius. The math for calculating the temperature data can be found in the Digital Temperature Output section of the TMP102 datasheet.
Running and Debugging
Save your code. Click Project > Build Project. The project should compile without any errors. When it’s done, click Run > Debug As > STM32 MCU C/C++ Application.
Click OK on the pop-up window asking you to set the debug configurations. The default configurations will work for now. Click Switch when asked about switching to a new perspective. Click the Resume button at the top of the toolbar to begin running code on the Nucleo board.
The installation process for STM32CubeIDE should have installed any necessary serial drivers for the Nucelo boards by default. If not, follow the instructions on this mbed page to install the drivers.
Open up your favorite serial terminal program and connect it to the COM port of your Nucleo board with a baud rate of 115200 (8 data bits, no parity, 1 stop bit). You should see temperature data (in Celsius) being reported twice per second.
Try lightly placing your finger on the TMP102 or breathing on it. You should see the reported temperature go up. If you remove the connections to SCL or SDA, you should see error messages being reported.
Resources and Going Further
Try talking to other I2C devices! Additionally, if you take a look at the I2C transmit and receive functions, you will see that they are blocking, which means that the program will not continue until those functions have completed. Take a look at the HAL API documentation for your particular board, and you’ll notice that there are lots of other I2C functions. The functions that end with _IT() rely on hardware interrupts within the STM32, which means you can construct non-blocking versions of our I2C communication. Give it a shot!
Overview of DigiKey’s STM32 offerings: https://www.digikey.com/en/product-highlight/s/stmicroelectronics/stm32-overview
DigiKey’s Nucleo offerings: https://www.digikey.com/en/product-highlight/s/stmicroelectronics/nucleo-development-boards
STM32 HAL API for L4/L4+: https://www.st.com/resource/en/user_manual/dm00173145.pdf
Download STM32CubeIDE: https://www.st.com/en/development-tools/stm32cubeide.html
키워드에 대한 정보 stm32 i2c 통신 예제
다음은 Bing에서 stm32 i2c 통신 예제 주제에 대한 검색 결과입니다. 필요한 경우 더 읽을 수 있습니다.
이 기사는 인터넷의 다양한 출처에서 편집되었습니다. 이 기사가 유용했기를 바랍니다. 이 기사가 유용하다고 생각되면 공유하십시오. 매우 감사합니다!
사람들이 주제에 대해 자주 검색하는 키워드 [HAL, CubeMX, TrueSTUDIO를 이용한 STM32F4 속성 강의 강좌] 9강. I2C 통신(AT24C04 EEPROM)
- #STM
- #STM32
- #STM32F4
- #HAL
- #CubeMX
- #TrueSTUDIO
- #강의
- #강좌
- #ChrisP
- #M-HIVE
- #엠하이브
- I2C
- EEPROM
- AT24C
YouTube에서 stm32 i2c 통신 예제 주제의 다른 동영상 보기
주제에 대한 기사를 시청해 주셔서 감사합니다 [HAL, CubeMX, TrueSTUDIO를 이용한 STM32F4 속성 강의 강좌] 9강. I2C 통신(AT24C04 EEPROM) | stm32 i2c 통신 예제, 이 기사가 유용하다고 생각되면 공유하십시오, 매우 감사합니다.