STM32, 직렬 I2C 인터페이스. STM32, 직렬 인터페이스 I2C Stm32 인터페이스 i2c 설명 계속

파이를 좋아하는 사람도 있고 그렇지 않은 사람도 있습니다.

i2c 인터페이스는 널리 널리 사용되고 있습니다. stm32f4에는 이 프로토콜을 구현하는 모듈이 세 개나 있습니다.
자연스럽게, 전폭적인 지원이 모든 것.

모듈 작업은 일반적으로 다른 컨트롤러와 동일합니다. 명령을 내리면 실행하고 결과를 보고합니다.
나> START 했어요.
S> 네, 보냈어요.
나> 알겠습니다. 지금 주소를 보내주세요. 예: 0xXX.
에스> 알겠습니다. 보냈습니다. ACK라고 들었습니다. 계속 진행합시다.
나> 아직 살아있어요, 좋아요. 레지스터 번호는 다음과 같습니다: 0xYY, - 가자.
에스> 보내고 ACK를 받았습니다.
I> 이제 그에게 데이터를 보냅니다. 바이트는 0xZZ입니다.
에스> 전송되면 그는 더 많은 것에 동의합니다: ACK.
나> 엿 먹어라, 아직은 아니야. 그들은 멈췄다.
스> 알았어.

그리고 모든 것이 대략 이러한 정신에 있습니다.

안에 이 컨트롤러 i2c 핀은 다음과 같이 포트 전체에 분산되어 있습니다.
PB6: I2C1_SCL
PB7: I2C1_SDA

PB8: I2C1_SCL
PB9: I2C1_SDA

PB10: I2C2_SCL
PB11: I2C2_SDA

PA8: I2C3_SCL
PC9: I2C3_SDA
일반적으로 59페이지의 주변기기 핀아웃을 보면 편리합니다.

놀랍게도 i2c를 사용하려면 모든 레지스터가 필요하지만 다행스럽게도 그 중 몇 가지가 있습니다.
I2C_CR1- 명령/상태를 전송하고 작동 모드를 선택하기 위해 모듈에 대한 명령;
I2C_CR2- DMA를 설정하고 모듈의 작동 주파수(2-42MHz)를 표시합니다.
I2C_OAR1- 장치 주소(슬레이브용), 주소 크기(7 또는 10비트) 설정
I2C_OAR2- 장치 주소 설정(주소가 두 개인 경우)
I2C_DR- 데이터 레지스터;
I2C_SR1- 모듈 상태 레지스터;
I2C_SR2- 상태 레지스터(슬레이브, ADDR 또는 STOPF 플래그가 SR1에 설정된 경우 읽어야 함)
I2C_CCR- 인터페이스 속도 설정;
I2C_TRISE- 가장자리의 타이밍을 설정합니다.

그러나 그 중 절반은 '적어두고 잊어버리는' 유형이다.

STM32F4-Discovery 보드에는 이미 연습할 수 있는 I2C 장치인 CS43L22, 오디오 DAC가 있습니다. 핀 PB6/PB9에 연결됩니다. 가장 중요한 것은 핀 PD4(~RESET이 거기에 있음)에 높은 레벨을 적용하는 것을 잊지 않는 것입니다. 그렇지 않으면 DAC가 응답하지 않습니다.

설정 절차는 대략 다음과 같습니다.
1 . 포트와 모듈 자체의 클로킹을 허용합니다.
핀 PB6/PB9가 필요하므로 포트를 활성화하려면 RCC_AHB1ENR 레지스터에서 비트 1(GPIOBEN)을 설정해야 합니다.
그리고 RCC_APB1ENR 레지스터의 비트 21(I2C1EN)을 설정하여 I2C 모듈을 활성화합니다. 두 번째와 세 번째 모듈의 경우 비트 번호는 각각 22와 23입니다.
2 . 다음으로 핀은 Oped Drain 출력(GPIO->OTYPER), 대체 기능 모드(GPIO->MODER) 및 대체 기능 번호(GPIO->AFR)로 구성됩니다.
원할 경우 풀업(GPIO->PUPDR)을 구성할 수 있습니다. 풀업이 보드에 없으면 어떤 형태로든 두 라인의 전원 공급 장치에 대한 풀업이 필요합니다. I2C의 번호는 항상 동일합니다. 4. 각 주변 장치 유형마다 별도의 번호가 있다는 점이 좋습니다.
3 . Fpclk1 주변 장치의 현재 클록 주파수(MHz로 표시)는 CR2 레지스터에 표시됩니다. 내가 이해하는 바에 따르면 이는 다양한 프로토콜 타이밍을 계산하는 데 필요합니다.
그런데 일반 모드는 최소 2개 이상, 고속 모드는 최소 4개 이상이어야 합니다. 그리고 400kHz의 최대 속도가 필요한 경우 이를 10(10, 20, 30, 40MHz)으로 나누어야 합니다.
최대 허용 클록 주파수: 42MHz.
4 . 인터페이스 속도는 CCR 레지스터에서 구성되며 모드(일반/고속)가 선택됩니다.
의미는 다음과 같습니다. Tsck = CCR * 2 * Tpccl1, 즉 SCK 기간은 CCR에 비례합니다(빠른 모드의 경우 모든 것이 조금 더 까다롭지만 RM에 설명되어 있습니다).
5 . TRISE 레지스터의 최대 상승 에지 시간이 조정됩니다. 표준 모드의 경우 이 시간은 1μs입니다. 레지스터에 이 시간에 맞는 버스 사이클 수에 1을 더해 기록해야 합니다.
Tpclk1 주기가 125ns 동안 지속되면 (1000ns / 125ns) + 1 = 8 + 1 = 9를 씁니다.
6 . 인터럽트 신호(오류, 상태 및 데이터) 생성은 선택적으로 활성화됩니다.
7 . 모듈이 켜집니다. CR1 레지스터의 PE 플래그가 1로 설정됩니다.

그러면 모듈이 정상적으로 작동합니다. 올바른 명령 순서를 구현하고 결과를 확인하기만 하면 됩니다. 예를 들어, 레지스터 항목은 다음과 같습니다.
1 . 먼저 CR1 레지스터에 동일한 이름의 플래그를 설정하여 START를 보내야 합니다. 모든 것이 정상이면 잠시 후 SR1 레지스터에 SB 플래그가 설정됩니다.
한 가지 점에 주목하고 싶습니다. 라인에 풀업이 없고(그리고 0에 있는 경우) 이 플래그는 전혀 기다리지 않을 수 있습니다.
2 . 플래그가 수신되면 주소를 보냅니다. 7비트 주소의 경우 라인에 있는 그대로 DR에 간단히 씁니다(7주소 비트 + 방향 비트). 10비트의 경우 더 복잡한 알고리즘입니다.
장치가 ACK로 주소에 응답하면 SR1 레지스터에 ADDR 플래그가 나타나고, 그렇지 않으면 AF(Acknowledge failure) 플래그가 나타납니다.
ADDR이 나타나면 SR2 레지스터를 읽어야 합니다. 거기에서는 아무것도 볼 필요가 없습니다. SR1과 SR2를 순차적으로 읽으면 이 플래그가 재설정됩니다. 그리고 플래그가 설정된 동안 SCL은 마스터에 의해 낮게 유지됩니다. 이는 데이터를 전송하기 전에 원격 장치에 기다리도록 요청해야 하는 경우에 유용합니다.
모든 것이 정상이면 모듈은 전송된 주소의 최하위 비트에 따라 데이터 수신 또는 전송 모드로 전환합니다. 쓰기 위해서는 0이어야 하고, 읽기 위해서는 1이어야 합니다.
하지만 우리는 기록을 보고 있으므로 거기에 0이 있다고 가정하겠습니다.
3 . 다음으로 관심 있는 등록부의 주소를 보냅니다. 같은 방법으로 DR에 적어둡니다. 전송 후에는 TXE(전송 버퍼가 비어 있음) 및 BTF(전송 완료) 플래그가 설정됩니다.
4 . 다음은 장치가 ACK로 응답하는 동안 보낼 수 있는 데이터입니다. 응답이 NACK이면 이러한 플래그가 설정되지 않습니다.
5 . 전송이 완료되면(또는 예상치 못한 상황이 발생하는 경우) STOP을 보냅니다. 동일한 이름의 플래그가 CR1 레지스터에 설정됩니다.

읽을 때 모든 것이 동일합니다. 레지스터 주소를 쓴 후에만 변경됩니다.
데이터를 쓰는 대신 START가 다시 전송되고(재시작) 최하위 비트가 설정된 주소가 전송됩니다(읽기 기호).
모듈은 장치로부터 데이터를 기다립니다. 다음 바이트를 보내도록 하려면 수신하기 전에 CR1에 ACK 플래그를 설정해야 합니다(수신 후 모듈이 동일한 ACK를 보내도록).
피곤해지면 플래그를 제거하면 장치에 NACK이 표시되고 조용해집니다. 그런 다음 일반적인 방식으로 STOP을 보내고 수신된 데이터를 확인합니다.

다음은 코드 형식으로 동일한 내용입니다.
// 모듈 초기화 void i2c_Init(void) ( uint32_t Clock = 16000000UL; // 모듈 클럭 주파수 (system_stm32f4xx.c는 사용되지 않음) uint32_t Speed ​​​​= 100000UL; // 100 kHz // GPIOB 포트 클로킹 활성화 RCC->AHB1ENR |= RCC_AHB1ENR_GPIOBEN; // PB6, PB9 핀 설정 // 드레인 오픈! GPIOB->OTYPER |= GPIO_OTYPER_OT_6 | GPIO_OTYPER_OT_9; // 풀업은 외부이므로 여기서 구성할 수 없습니다! // 필요한 경우, GPIOB->PUPDR 레지스터를 참조하세요. // 대체 GPIOB 함수의 번호 ->AFR &= ~(0x0FUL<< (6 * 4)); // 6 очистим GPIOB->AFR |= (0x04UL<< (6 * 4)); // В 6 запишем 4 GPIOB->AFR &= ~(0x0FUL<< ((9 - 8) * 4)); // 9 очистим GPIOB->AFR |= (0x04UL<< ((9 - 8) * 4)); // В 9 запишем 4 // Режим: альтернативная функция GPIOB->MODER &= ~((0x03UL<< (6 * 2)) | (0x03UL << (9 * 2))); // 6, 9 очистим GPIOB->모드 |= ((0x02UL<< (6 * 2)) | (0x02UL << (9 * 2))); // В 6, 9 запишем 2 // Включить тактирование модуля I2C1 RCC->APB1ENR |= RCC_APB1ENR_I2C1EN; // 이 시점에서 I2C는 꺼져야 합니다. // 모든 것을 재설정합니다(SWRST == 1, 재설정) I2C1->CR1 = I2C_CR1_SWRST; // PE == 0, 이것이 가장 중요한 것입니다. I2C1->CR1 = 0; // 우리는 RC(16MHz)에서 실행한다고 가정합니다. // 클록 시스템에는 프리스케일러가 없습니다(모두 1) // 우호적인 방법으로 // 모듈의 실제 클록 주파수에서 이 모든 것을 계산해야 합니다 I2C1->CR2 = 시계 / 1000000UL; // 16MHz // 주파수 조정( // Tclk = (1 / Fperiph); // Thigh = Tclk * CCR; // Tlow = Thigh; // Fi2c = 1 / CCR * 2; // CCR = Fperiph / ( Fi2c * 2); uint16_t Value = (uint16_t)(Clock / (Speed ​​​​* 2)); // 최소값 : 4 if(Value< 4) Value = 4; I2C1->CCR = 가치; ) // 상승 시간 제한 설정 // 표준 모드에서 이 시간은 1000ns입니다. // MHz로 표시되는 주파수에 1을 더하기만 하면 됩니다(RM p. 604 참조). I2C1->TRISE = (시계 / 1000000UL) + 1; // 모듈 I2C1->CR1 활성화 |= (I2C_CR1_PE); // 이제 뭔가를 할 수 있습니다 ) // 바이트 보내기 bool i2c_SendByte(uint8_t Address, uint8_t Register, uint8_t Data) ( if(!i2c_SendStart()) return false; // 칩 주소 if(!i2c_SendAddress(Address)) return i2c_SendStop (); // 주소 등록 if(!i2c_SendData(Register)) return i2c_Stop(); // 데이터 if(!i2c_SendData(Data)) return i2c_SendStop(); // 중지! i2c_SendStop(); return true; ) // 수신 바이트 bool i2c_ReceiveByte(uint8_t Address, uint8_t Register, uint8_t * Data) ( if(!i2c_SendStart()) return false; // 칩 주소 if(!i2c_SendAddress(Address)) return i2c_SendStop(); // 등록 주소 if(! i2c_SendData(Register)) return i2c_SendStop(); // 재시작 if(!i2c_SendStart()) return false; // 칩 주소(읽기) if(!i2c_SendAddress(Address | 1)) return i2c_SendStop(); // 바이트 수신 if(!i2c_ReceiveData(Data)) return i2c_SendStop(); // 중지! i2c_SendStop(); return true; ) 사용법: ( uint8_t ID = 0; i2c_Init(); // PD4가 높은 레벨로 설정되어 있고 DAC가 작동 중입니다(어떻게든 완료해야 함) // 주소가 0x94인 장치에 바이트를 전송하여 값이 0x00인 0x00을 등록합니다. i2c_SendByte(0x94, 0x00, 0x00); // 레지스터 0x01(ID)에서 주소 0x94를 가진 장치로부터 변수 버퍼로 바이트를 수신합니다. i2c_ReceiveByte(0x94, 0x01, &ID); )
물론 훈련 예시를 제외하고는 이를 수행할 수 없습니다. 이렇게 빠른 컨트롤러에서는 작업이 완료될 때까지 기다리는 시간이 너무 깁니다.

(HCS08 제품군 마이크로컨트롤러용 개발자 가이드)

I2C 모듈을 제어하기 위해 6개의 특수 기능 레지스터가 사용됩니다.

  • IICC - I2C 모듈의 첫 번째 제어 레지스터입니다.
  • IICC2 - I2C 모듈의 두 번째 제어 레지스터입니다.
  • IICS - I2C 모듈 상태 레지스터;
  • IICF - I2C 모듈 전송 속도 레지스터;
  • IICA - I2C 모듈 주소 레지스터;
  • IICD는 I2C 모듈 데이터 레지스터입니다.

QE 시리즈 MCU에는 2개의 I2C 모듈과 그에 따라 각 유형의 2개의 제어 레지스터가 포함되어 있습니다. 예를 들어 첫 번째 상태 레지스터는 IIC1S이고 두 번째 상태 레지스터는 IIC2S입니다.

11.2.8.1. IICC 제어 레지스터

MK 시리즈 AC용. AW, Dx, EL, GB, GT, JM, LC, QE. Q.G. SG, SH, SL
등록하다 방법 D7 D6 D5 D4 D3 D2 D1 D0
IICC 독서 아이센 IICIE MST 텍사스 TXAK 0 0 0
기록 RSTA
초기화 0 0 0 0 0 0 0 0
비트 설명:
비트 이름 설명 C 언어의 기호
아이센 I2C 모듈 활성화 비트:
0 - I2C 컨트롤러가 비활성화됩니다.
1 - I2C 컨트롤러가 활성화됩니다.
바이센
IICIE I2C의 모듈 인터럽트 활성화 비트:
0 - I2C 요청 인터럽트가 비활성화됩니다.
1 - I2C 요청 인터럽트가 활성화됩니다.
bHCIE
MST I2C 컨트롤러 작동 모드 선택 비트:
0 - I2C 컨트롤러가 슬레이브 모드에서 작동합니다.
1 - I2C 컨트롤러는 마스터 모드에서 작동합니다.
이 비트가 0에서 1로 변경되면 시작 상태가 생성됩니다. 반대로 비트가 1에서 0으로 변경되면 정지 조건이 생성됩니다.
bMST
텍사스 SDA 데이터 라인의 전송 방향 선택 비트:
0 — 라인이 입력에 사용됩니다.
1 - 라인은 출력을 위해 작동합니다.
bTX
TXAK 수신 모드의 승인 비트:
0 - 바이트를 수신한 후 승인 비트가 생성됩니다.
1 - 승인되지 않은 바이트 비트가 생성됩니다.
이 비트는 슬레이브인지 마스터인지에 관계없이 데이터 바이트를 수신한 후 승인 비트의 생성을 제어합니다.
bTXAK
RSTA I2C 모듈이 마스터 모드에서 작동하는 경우 이 비트에 1을 쓰면 시작 - "다시 시작" 상태가 다시 생성됩니다. 브르스타

11.2.8.2. IICS 상태 등록

MK 시리즈 AC, AW, Dx, EL, GB, GT, JM, LC, QE, QG, SG, SH, SL용
등록하다 방법 D7 D6 D5 D4 D3 D2 D1 D0
IICS 독서 TCF IAAS 바쁘다 ARBL 0 SRW IICIF RXAK
기록
초기화 0 0 0 0 0 0 0 0
비트 설명:
비트 이름 설명 C 언어의 기호
TCF 완료 비트를 교환합니다. 1바이트 교환이 완료된 후 설정됩니다.
0 — 교환이 완료되지 않았습니다.
1 - 교환이 완료되었습니다.
TCF 플래그는 IICD 데이터 레지스터를 읽을 때(수신 모드에서) 또는 IICD 데이터 레지스터를 쓸 때(전송 모드에서) 0으로 지워집니다.
bTCF
IAAS 슬레이브 주소 플래그. 장치가 슬레이브 모드에서 작동 중이고 마스터 메시지로 전송된 주소가 IICA 주소 레지스터에 저장된 슬레이브 주소와 동일한 경우 설정됩니다.
IICC 레지스터에 쓸 때 플래그가 지워집니다.
바이아스
바쁘다 통화중 플래그입니다. 이 플래그는 I2C 모듈이 라인의 시작 비트를 인식한 경우 설정됩니다. 모듈이 라인에서 정지 비트를 감지하면 플래그가 지워집니다.
0 - I2C 버스가 무료입니다.
1 - I2C 버스가 사용 중입니다.
바쁨
ARBL 중재 패소 플래그:
0 - I2C 버스 작동에 위반이 없습니다.
1 - 차익거래 손실이 발생합니다. I2C 모듈은 잠시 기다린 후 전송 작업을 다시 시작해야 합니다.
바블
SRW 슬레이브 송신 방향 비트. 이 비트는 주소 필드의 R/W 비트 상태를 나타냅니다.
0 - 슬레이브가 수락합니다. 리더는 슬레이브에게 전송합니다.
1 - 슬레이브가 전송합니다. 리더는 노예로부터 받습니다.
bSRW
IICIF I2C 모듈에 대한 서비스되지 않은 인터럽트 요청의 플래그 TCF, IAAS 또는 ARBL 플래그 중 하나가 설정된 경우 1로 설정됩니다.
0 - 서비스되지 않은 인터럽트가 없습니다.
1 - 서비스되지 않은 인터럽트가 있습니다.
플래그에 1을 쓰면 플래그가 재설정됩니다.
BIICIF
RXAK 마스터 승인 비트:
0 - 슬레이브가 데이터 수신을 확인했습니다.
1 - 슬레이브가 데이터 수신을 승인하지 않았습니다.
이 비트는 교환 타이밍 다이어그램의 ASK 필드 상태를 반영합니다.
brXAK

11.2.8.3. IICA 주소 등록

등록하다 방법 D7 D6 D5 D4 D3 D2 D1 D0
IICA 독서 주소
기록
초기화 0 0 0 0 0 0 0 0

이 레지스터는 개발자가 할당한 7비트 슬레이브 주소를 저장합니다. 이 기기시스템을 개발할 때. 이 주소는 슬레이브가 I2C 버스의 주소 필드에서 수신한 주소 코드와 자동으로 비교됩니다. 주소가 일치하면 IICS 상태 레지스터의 IAAS 비트가 설정됩니다.

11.2.8.4. IICF 전송 속도 레지스터

MK 시리즈 AC, AW, Dx, EL, GB, GT, JM, LC, QE, QG, SG, SH, SL용
등록하다 방법 D7 D6 D5 D4 D3 D2 D1 D0
IICF 독서 다중 ICR
기록
초기화 0 0 0 0 0 0 0 0
비트 설명:

계수 SCL_DIV 및 SDA_HV의 값

ISR SCL_DIV SDA_HV ISR SCL_DIV SDA_HV
0x00 20 7 0x20 160 17
0x01 22 7 0x21 192 17
0x02 24 8 0x22 224 33
0x03 26 8 0x23 256 33
0x04 28 9 0x24 288 49
0x05 30 9 0x25 320 49
0x06 34 10 0x26 384 65
0x07 40 10 0x27 480 65
0x08 28 7 0x28 320 33
0x09 32 7 0x29 384 33
0x0A 36 9 0x2A 448 65
0x0B 40 9 0x2B 512 65
0x0C 44 11 0x2C 576 97
0x0D 48 11 0x2D 640 97
0x0E 56 13 0x2E 768 129
0x0F 68 13 0x2F 960 129
0x10 48 9 0x30 640 65
0x11 56 9 0x31 768 65
0x12 64 13 0x32 896 129
0x13 72 13 0x33 1024 129
0x14 80 17 0x34 1152 193
0x15 88 17 0x35 1280 193
0x16 104 21 0x36 1536 257
0x17 128 21 0x37 1920 257
0x18 80 9 0x38 1280 129
0x19 96 9 0x39 1536 129
0x1A 112 17 0x3A 1792 257
0x1B 128 17 0x3B 2048 257
0x1C 144 25 0x3C 2304 385
0x1D 160 25 0x3D 2560 385
0x1E 192 33 0x3E 3072 513
0x1F 240 33 0x3F 3840 513

이 레지스터는 I2C 교환의 속도 및 타이밍 매개변수를 결정하는 두 개의 비트 필드를 저장합니다. 동기화 신호의 주파수는 다음 공식에 의해 결정됩니다.

I2C 버스의 SDA_hold_time 데이터 정착 시간은 SCL 신호가 0으로 설정되는 순간과 SDA 라인의 데이터가 변경되는 순간 사이의 시간 간격입니다. 전송 속도 레지스터의 ICR 요소에 대한 표의 매개변수 SDA_HV(SDA_Hold_Value)에 의해 할당됩니다.

.

11.2.8.5. IICD 데이터 레지스터

MK 시리즈 AC, AW, Dx, EL, GB, GT, JM, LC, QE, QG, SG, SH, SL용
등록하다 방법 D7 D6 D5 D4 D3 D2 D1 D0
IICD 독서 I2C 데이터
기록
초기화 0 0 0 0 0 0 0 0

I2C 모듈이 마스터 모드에서 작동하는 경우 이 레지스터에 대한 쓰기 작업은 I2C 통신을 시작합니다(단, IICC 제어 레지스터의 통신 방향 비트가 올바르게 설정된 경우, 즉 TX = 1인 경우에만 해당). 프로그램이 데이터 레지스터에 쓰는 시작 상태 다음의 첫 번째 바이트는 슬레이브에 의해 장치 주소로 해석됩니다. 따라서 프로그램은 첫 번째 바이트의 내용을 올바르게 구성해야 합니다. 레지스터 읽기 작업은 I2C를 통해 수신된 마지막 바이트를 반환합니다. 레지스터 읽기 작업은 또한 다음 바이트 수신 시작을 시작하지만 IICC 제어 레지스터의 통신 방향 비트가 올바르게 설정된 경우에만 가능합니다. TX = 0! TX = 1인 경우 레지스터 읽기 작업으로 인해 슬레이브에서 I2C를 통해 새 바이트가 수신되지 않습니다.

I2C 모듈이 슬레이브 모드에서 작동하는 경우, 이 레지스터에 기록된 데이터는 마스터 장치가 이 슬레이브로부터 수신 사이클을 수행할 때 I2C 버스의 SDA 라인으로 전송됩니다. 레지스터 읽기 작업은 마스터로부터 받은 마지막 바이트를 반환합니다.

11.2.8.6. 제어 레지스터 IICC2

MK 시리즈 AC, Dx, EL, JM, QE, SG, SH, SL용
등록하다 방법 D7 D6 D5 D4 D3 D2 D1 D0
IICC 독서 GCAEN ADEXT 0 0 0 AD10 AD9 AD8
기록
초기화 0 0 0 0 0 0 0 0

ARM 아키텍처용 STM32 및 mikroC 컴파일러를 사용한 첫 번째 단계 - 4부 - I2C, pcf8574 및 HD4478 기반 LCD 연결

다음 기사에서는 마이크로 컨트롤러에 연결된 다양한 마이크로 회로에 자주 사용되는 일반적인 i2c 인터페이스 작업에 대해 다루고 싶습니다.

I2C는 두 개의 물리적 연결(공통 와이어 외에)을 통해 작동하는 버스입니다. 인터넷에는 이에 대해 많은 글이 쓰여 있고 Wikipedia에는 ​​좋은 기사가 있습니다. 또한, 버스 동작 알고리즘이 매우 명확하게 설명되어 있습니다. 즉, 버스는 2선식 동기 버스입니다. 최대 127개의 장치가 동시에 버스에 있을 수 있습니다(장치 주소는 7비트이므로 이에 대해서는 나중에 다시 설명하겠습니다). 다음은 MK를 마스터 장치로 사용하여 장치를 i2c 버스에 연결하는 일반적인 다이어그램입니다.


i2c의 경우 모든 장치(마스터와 슬레이브 모두)는 오픈 드레인 출력을 사용합니다. 간단히 말해서, 타이어를 지면으로만 끌어당길 수 있습니다. 높은 버스 레벨은 풀업 저항에 의해 보장됩니다. 이 저항의 값은 일반적으로 4.7 ~ 10kOhm 범위에서 선택됩니다. i2c는 장치를 연결하는 물리적 회선에 매우 민감하므로 정전 용량이 큰 연결(예: 길고 가늘거나 차폐된 케이블)이 사용되는 경우 이 정전 용량의 영향으로 신호 가장자리가 "흐려지고" 간섭을 일으킬 수 있습니다. 정상 작동타이어. 풀업 저항이 작을수록 이 커패시턴스가 신호 에지의 특성에 미치는 영향은 줄어들지만 i2c 인터페이스의 출력 트랜지스터에 대한 로드는 커집니다. 이 저항의 값은 각 특정 구현에 대해 선택되지만 2.2kOhms 이상이어야 합니다. 그렇지 않으면 버스와 함께 작동하는 장치의 출력 트랜지스터를 간단히 태울 수 있습니다.

버스는 SDA(데이터 라인)와 SCL(클럭 신호)의 두 라인으로 구성됩니다. 버스 마스터 장치의 클록, 일반적으로 MK. SCL이 높으면 데이터 버스에서 정보를 읽습니다. SDA 상태는 클럭 신호가 낮을 때만 변경될 수 있습니다.. SCL이 높으면 신호 생성 시 SDA의 신호가 변경됩니다. 시작 (SCL이 높을 때 SDA의 신호는 높음에서 낮음으로 변경됩니다.) 멈추다 - SCL 레벨이 높으면 SDA의 신호가 낮음에서 높음으로 변경됩니다.

별도로, i2c에서는 주소가 7비트 숫자로 지정된다고 해야 합니다. 8 - 최하위 비트는 데이터 전송 방향을 나타냅니다. 0 - 슬레이브가 데이터를 전송함을 의미하고, 1 - 수신함을 의미합니다.. 간단히 말해서 i2c 작업 알고리즘은 다음과 같습니다.

  • SDA 및 SCL에 대한 높은 수준- 버스는 무료이며 일을 시작할 수 있습니다
  • 마스터 리프트 SCL 1로 변경되고 상태가 변경됩니다. S.D.A. 1에서 0까지 - 땅에 끌어당김 - 신호가 형성됨 시작
  • 마스터는 방향 비트(데이터에 대한 데이터)와 함께 7비트 슬레이브 주소를 전송합니다. S.D.A.때 전시된다 SCL땅에 끌어당겨서 풀려났을 때 슬레이브가 읽습니다). 슬레이브가 이전 비트를 "잡을" 시간이 없으면 SCL데이터 버스의 상태를 변경할 필요가 없다는 점을 마스터에게 분명히 알립니다. "아직 이전 상태를 읽고 있습니다." 마스터는 타이어를 풀고 나서 점검합니다. 노예가 그녀를 놓아줬나요?.
  • 8비트의 주소를 전송한 후 마스터는 9번째 클록 사이클을 생성하고 데이터 버스를 해제합니다. 노예가 그의 주소를 듣고 그것을 받아들인다면, 그는 누를 것이다 S.D.A.땅바닥에. 이것이 신호가 형성되는 방식입니다 묻다- 동의합니다. 모든 것이 정상입니다. 노예가 아무것도 이해하지 못하거나 단순히 거기에 없으면 타이어를 누를 사람이 없을 것입니다. 마스터는 타임아웃을 기다렸다가 자신이 이해하지 못했다는 것을 이해할 것입니다.
  • 주소를 전송한 후 방향을 설정했다면 주인에서 노예로(주소의 8비트는 1과 같습니다.) 마스터는 슬레이브의 존재 여부를 확인하는 것을 잊지 않고 데이터를 슬레이브에 전송합니다. 묻다슬레이브 장치가 들어오는 정보를 처리하기를 기다립니다.
  • 마스터가 슬레이브로부터 데이터를 수신하면 마스터 자체가 신호를 생성합니다. 묻다각 바이트를 수신한 후 슬레이브는 해당 바이트의 존재를 제어합니다. 마스터가 특별히 보내지 않을 수도 있습니다. 묻다명령을 보내기 전에 멈추다, 일반적으로 더 이상 데이터를 제공할 필요가 없음을 슬레이브에게 분명히 알립니다.
  • 마스터에서 데이터를 보낸 후(쓰기 모드) 슬레이브에서 데이터를 읽어야 하는 경우, 그러면 마스터가 신호를 다시 생성합니다. 시작 , 읽기 플래그와 함께 슬레이브 주소를 보냅니다. (명령 앞에 있는 경우 시작양도되지 않았습니다 멈추다그런 다음 팀이 구성됩니다 재시작). 마스터-슬레이브 통신 방향을 변경하는데 사용됩니다. 예를 들어 레지스터 주소를 슬레이브에 전달한 다음 슬레이브에서 데이터를 읽습니다.)
  • 슬레이브 작업이 완료되면 마스터는 신호를 생성합니다. 멈추다- 클럭 신호의 높은 레벨에서는 0에서 1로의 데이터 버스 전환을 형성합니다.
STM 32에는 하드웨어로 구현된 i2c 버스 트랜시버가 있습니다. MK에는 이러한 모듈이 2개 또는 3개 있을 수 있습니다. 이를 구성하려면 사용된 MK에 대한 참조에 설명된 특수 레지스터가 사용됩니다.

MicroC에서는 i2c(및 주변 장치)를 사용하기 전에 올바르게 초기화해야 합니다. 이를 위해 다음 기능을 사용합니다(마스터로 초기화).

I2Cn_Init_Advanced(unsigned long: I2C_ClockSpeed, const Module_Struct *module);

  • N- 사용된 모듈의 번호(예: I2C1또는 I2C2.
  • I2C_ClockSpeed- 버스 속도, 100000(100kbs, 표준 모드) 또는 400000(400kbs, 고속 모드). 두 번째는 4배 빠르지만 모든 장치가 이를 지원하는 것은 아닙니다.
  • *기준 치수- 주변 모듈에 대한 포인터(예: &_GPIO_MODULE_I2C1_PB67, 여기서 잊지 말자 코드 어시스턴트 (Ctrl-Space ) 많은 도움이 됩니다.
먼저 버스가 무료인지 확인해 봅시다. 이를 위한 기능이 있습니다. I2Cn_Is_Idle();버스가 무료이면 1을 반환하고 버스에 교환이 있으면 0을 반환합니다.

I2Cn_Start();
어디 N- 마이크로컨트롤러에 사용된 i2c 모듈의 번호. 이 함수는 버스에 오류가 있으면 0을 반환하고 모든 것이 정상이면 1을 반환합니다.

데이터를 슬레이브로 전송하기 위해 다음 기능을 사용합니다.

I2Cn_Write(unsigned char 슬레이브_주소, unsigned char *buf, unsigned long 개수, unsigned long END_mode);

  • N- 사용된 모듈의 번호
  • 노예_주소- 7비트 슬레이브 주소.
  • *버프- 데이터에 대한 포인터 - 바이트 또는 바이트 배열.
  • 세다- 전송된 데이터 바이트 수.
  • END_모드- 데이터를 슬레이브에 전송한 후 해야 할 일, END_MODE_STOP - 신호를 전송하다 멈추다, 또는 END_MODE_RESTART 다시 보내다 시작, 신호 생성 재시작그와의 세션이 끝나지 않았으며 이제 그에게서 데이터를 읽을 것임을 부서에 분명히 알립니다.
슬레이브에서 데이터를 읽으려면 다음 기능을 사용하십시오.

I2Cn_Read(char 슬레이브_주소, char *ptrdata, 부호 없는 긴 개수, 부호 없는 긴 END_mode);

  • N- 사용된 모듈의 번호
  • 노예_주소- 7비트 슬레이브 주소.
  • *버프- 데이터를 수신하는 변수 또는 배열에 대한 포인터(char 또는 short int 유형)
  • 세다- 수신된 데이터 바이트 수.
  • END_모드- 슬레이브로부터 데이터를 받은 후 해야 할 일 - END_MODE_STOP - 신호를 전송하다 멈추다, 또는 END_MODE_RESTART 신호를 보내다 재시작.
MK에 무언가를 연결해 봅시다. 우선, i2c 버스를 통해 제어되는 입력/출력 포트의 확장자인 널리 보급된 PCF8574(A) 마이크로 회로를 살펴보겠습니다. 이 칩에는 물리적 I/O 포트인 내부 레지스터가 하나만 포함되어 있습니다. 즉, 그녀에게 바이트를 전달하면 즉시 그녀의 결론이 노출됩니다. 그것으로부터 바이트를 세면 (전송 시작읽기 플래그가 있는 주소, 신호 다시 시작,데이터를 읽고 마지막으로 신호를 생성합니다. 멈추다) 그러면 출력에 논리적 상태가 반영됩니다. 데이터 시트에 따라 마이크로 회로를 연결해 보겠습니다.


미세 회로 주소는 핀의 상태로 구성됩니다. A0, A1, A2. 미세회로용 PCF8574주소는 다음과 같습니다: 0100A0A1A2. (예를 들어 상위 레벨에 A0, A1, A2가 있으므로 마이크로 회로의 주소는 0b0100입니다. 111 = 0x27). 을 위한 PCF8574A - 0111A0A1A2, 연결 다이어그램을 사용하면 주소가 0b0111이 됩니다. 111 = 0x3F. 예를 들어 A2가 접지에 연결되어 있으면 A2의 주소는 다음과 같습니다. PCF8574A~ 할 것이다 0x3B. 총 16개의 마이크로 회로가 하나의 i2c 버스에 동시에 장착될 수 있으며 각각 8개의 PCF8574A 및 PCF8574가 가능합니다.

무언가를 전송하고, i2c 버스를 초기화하고, 무언가를 PCF8574로 전송해 봅시다.

#define PCF8574A_ADDR 0x3F //PCF8574의 주소 void I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // START 신호 생성 I2C1_Write(PCF8574A_ADDR,&wData, 1, END_MODE_STOP); // 1바이트의 데이터를 전송하고 STOP을 생성 신호) char PCF8574A_reg; // PCF8574에 쓰는 변수 void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // I2C start_ms(25); // 조금 기다리세요 PCF8574A_reg.b0 = 0; // 첫 번째 LED 켜기 PCF8574A_reg.b1 = 1; // 두 번째 LED를 끄는 동안 (1) ( Delay_ms(500); PCF8574A_reg.b0 = ~PCF8574A_reg.b0; PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // LED 상태 반전 I2C_PCF8574_WriteReg (PCF8574A_reg) ; // PCF8574로 데이터 전송 ) )
프로그램을 컴파일하고 실행하여 LED가 교대로 깜박이는 것을 확인합니다.
이유 때문에 LED 음극을 PCF8574에 연결했습니다. 문제는 논리 0이 출력에 공급되면 마이크로 회로가 출력을 정직하게 접지로 끌어 당기지 만 논리 1이 적용되면 100μA 전류 소스를 통해 + 전원에 연결된다는 것입니다. 즉, 출력에서 ​​"정직한" 논리값 1을 얻을 수 없습니다. 그리고 100μA로는 LED를 켤 수 없습니다. 이는 추가 레지스터 없이 PCF8574 출력을 입력으로 구성하기 위해 수행되었습니다. 간단히 출력 레지스터 1에 쓰고(본질적으로 핀 상태를 Vdd로 설정) 간단히 접지로 단락시킬 수 있습니다. 전류 소스는 I/O 확장기의 출력 단계가 "소진"되는 것을 허용하지 않습니다. 다리를 지면으로 당기면 접지전위가 올라가고 논리 0을 읽고, 다리를 +로 당기면 논리 1을 읽습니다. 한편으로는 간단하지만 다른 한편으로는 이러한 마이크로회로를 작업할 때 항상 이것을 기억해야 합니다.


확장 칩의 핀 상태를 읽어 보겠습니다.

#define PCF8574A_ADDR 0x3F //PCF8574의 주소 void I2C_PCF8574_WriteReg(unsigned char wData) ( I2C1_Start(); // START 신호 생성 I2C1_Write(PCF8574A_ADDR, &wData, 1, END_MODE_STOP); // 1바이트의 데이터를 전송하고 STOP을 생성 signal ) void I2C_PCF8574_ReadReg (unsigned char rData) ( I2C1_Start(); // START 신호 생성 I2C1_Read(PCF8574A_ADDR, &rData, 1, END_MODE_STOP); // 1바이트의 데이터를 읽고 STOP 신호 생성) char PCF8574A_reg; //PCF8574에 쓰는 변수 char PCF8574A_out; // 우리가 읽은 변수와 PCF8574 char lad_state; //LED가 켜져 있거나 꺼져 있습니다 void main () ( I2C1_Init_Advanced(400000, &_GPIO_MODULE_I2C1_PB67); // I2C 시작 Delay_ms(25); // 잠시 기다리십시오 PCF8574A_reg.b0 = 0; // 첫 번째 LED 켜기 PCF8574A_reg.b1 = 1; // 두 번째 LED 끄기 PCF8574A_reg.b6 = 1; // 핀 6과 7을 당겨 전원을 공급합니다. PCF8574A_reg.b7 = 1; while (1) ( Delay_ms(100); I2C_PCF8574_WriteReg (PCF8574A_reg); // 데이터 쓰기 to PCF8574 I2C_PCF8574_ReadReg (PCF85 74A_out ); // PCF8574에서 읽습니다. if (~PCF8574A_out.b6) PCF8574A_reg.b0 = ~PCF8574A_reg.b0; // 1 버튼을 누르면 (PCF8574에서 읽은 바이트의 6번째 비트가 0이 됩니다. LED 켜기/끄기) if (~PCF8574A_out .b7) PCF8574A_reg.b1 = ~PCF8574A_reg.b1; // 버튼 2개와 LED 2개와 유사 ) )
이제 버튼을 눌러 LED를 켜거나 끕니다. 마이크로 회로에는 또 다른 출력이 있습니다. 정수. I/O 확장기의 핀 상태가 변경될 때마다 펄스가 생성됩니다. 이를 MK의 외부 인터럽트 입력에 연결합니다(외부 인터럽트를 구성하는 방법과 이를 사용하는 방법은 다음 기사 중 하나에서 설명하겠습니다).

포트 확장기를 사용하여 문자 디스플레이를 연결해 보겠습니다. 이들 중 상당수가 있지만 거의 모두 컨트롤러 칩을 기반으로 구축되었습니다. HD44780그리고 그의 클론. 예를 들어, 저는 LCD2004 디스플레이를 사용했습니다.


해당 제품과 HD44780 컨트롤러에 대한 데이터시트는 인터넷에서 쉽게 찾을 수 있습니다. 우리의 디스플레이를 PCF8574에 연결하고 그녀의 디스플레이를 각각 STM32에 연결해 보겠습니다.

HD44780병렬 게이트 인터페이스를 사용합니다. 데이터는 출력에서 ​​8개(1클럭 주기) 또는 4(2클럭 주기) 게이트 펄스로 전송됩니다. 이자형. (하강 에지에서 디스플레이 컨트롤러가 읽음, 1에서 0으로 전환) 결론 RS데이터를 디스플레이로 보내고 있는지 여부를 나타냅니다( RS = 1)(표시해야 하는 문자는 실제로 ASCII 코드입니다.) 또는 명령( RS = 0). RW데이터 전송, 쓰기 또는 읽기 방향을 나타냅니다. 일반적으로 우리는 디스플레이에 데이터를 쓰므로 ( RW=0). 저항 R6은 디스플레이 대비를 제어합니다. 대비 조정 입력을 접지나 전원에 간단히 연결할 수는 없습니다. 그렇지 않으면 아무것도 표시되지 않습니다.. VT1은 MK 명령에 따라 디스플레이 백라이트를 켜고 끄는 데 사용됩니다. MicroC에는 병렬 인터페이스를 통해 이러한 디스플레이를 작업하기 위한 라이브러리가 있지만 일반적으로 디스플레이에 8개의 다리를 사용하는 데 비용이 많이 들기 때문에 거의 항상 PCF8574를 사용하여 이러한 화면을 작업합니다. (관심 있는 분이 계시다면 병렬 인터페이스를 통해 MicroC에 내장된 HD44780 기반 디스플레이 작업에 대한 기사를 작성하겠습니다.) 교환 프로토콜은 특별히 복잡하지 않습니다(우리는 4개의 데이터 라인을 사용하고 2클럭 주기에 정보를 전송할 것입니다). 이는 다음 타이밍 다이어그램으로 명확하게 표시됩니다.


데이터를 디스플레이로 전송하기 전에 서비스 명령을 전달하여 초기화해야 합니다. (데이터시트에 설명되어 있으며 여기서는 가장 많이 사용되는 것만 제시합니다)

  • 0x28- 4개 라인을 통해 표시기와 통신
  • 0x0C- 이미지 출력 활성화, 커서 표시 비활성화
  • 0x0E- 이미지 출력 활성화, 커서 표시 활성화
  • 0x01- 표시를 지우십시오
  • 0x08- 이미지 출력 비활성화
  • 0x06- 기호가 표시된 후 커서가 익숙한 1곳으로 이동합니다.
이 표시기를 자주 사용해야 하므로 플러그인 라이브러리를 생성하겠습니다. "i2c_lcd.h" . 이 작업을 수행하려면 프로젝트 매니저 헤더 파일 그리고 선택 새 파일 추가 . 헤더 파일을 만들어 보겠습니다.

#define PCF8574A_ADDR 0x3F //PCF8574의 주소 #define DB4 b4 // PCF8574 핀과 표시기 간의 대응 #define DB5 b5 #define DB6 b6 #define DB7 b7 #define EN b3 #define RW b2 #define RS b1 #define BL b0 // 백라이트 제어 #define displenth 20 // 디스플레이 라인의 문자 수 static unsigned char BL_status; // 백라이트 상태(켜짐/꺼짐)를 저장하는 변수 void lcd_I2C_Init(void); // 표시 및 PCF8574 초기화 함수 void lcd_I2C_txt(char *pnt); // 텍스트 줄을 표시합니다. 매개변수는 이 줄에 대한 포인터입니다. void lcd_I2C_int(int pnt); // 정수 변수의 값을 표시합니다. 매개변수는 출력 값입니다. void lcd_I2C_Goto(unsigned short row, unsigned short col); // 커서를 지정된 위치로 이동합니다. 매개변수 row - line(디스플레이에 따라 1에서 2 또는 4까지) 및 col -(1에서 displenth까지)) void lcd_I2C_cls(); // 화면을 지웁니다. void lcd_I2C_backlight(unsigned short int 상태); // 활성화(1을 전송할 때 비활성화 - 0을 전송할 때 디스플레이 백라이트)
이제 우리의 기능을 설명하겠습니다. 다시 한 번 프로젝트 매니저폴더를 마우스 오른쪽 버튼으로 클릭하세요. 출처 그리고 선택 새 파일 추가 . 파일 만들기 "i2c_lcd.с" .

#include "i2c_lcd.h" //헤더 파일 포함 char lcd_reg; //PCF8574로 전송된 데이터의 임시 저장을 위해 등록 void I2C_PCF8574_WriteReg(unsigned char wData) //i2c를 통해 PCF8574 칩으로 데이터를 전송하는 함수 ( I2C1_Start(); I2C1_Write(PCF8574A_ADDR,&wData, 1, END_MODE_STOP); ) void LCD_COMMAND ( char com) //디스플레이에 명령을 보내는 기능( lcd_reg = 0; //임시 레지스터에 0을 씁니다. lcd_reg.BL = BL_status.b0; //백라이트 핀을 저장하는 변수의 값에 따라 설정합니다. 백라이트 상태 lcd_reg.DB4 = com.b4; // 명령의 최상위 4비트를 표시기 데이터 버스로 설정 lcd_reg.DB5 = com.b5; lcd_reg.DB6 = com.b6; lcd_reg.DB7 = com.b7; lcd_reg.EN = 1; //스트로브 출력을 1로 설정 I2C_PCF8574_WriteReg(lcd_reg); //PCF8574 레지스터에 쓰고 실제로 표시기에 데이터를 보냅니다.lay_us(300); //시간 초과를 기다립니다 lcd_reg.EN = 0; / /스트로브 펄스를 0으로 재설정하면 표시기가 데이터를 읽습니다. I2C_PCF8574_WriteReg(lcd_reg); 지연_us(300) ; lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.DB4 = com.b0; //최소 4개에 대해 동일 중요한 비트 lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; lcd_reg.EN = 1; I2C_PCF8574_WriteReg(lcd_reg); Delay_us(300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg(lcd_reg); Delay_us(300); ) void LCD_CHAR (unsigned char com) //표시기로 데이터(ASCII 문자 코드) 전송( lcd_reg = 0; lcd_reg.BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; //문자 전송 RS 비트를 1로 설정하여 명령을 보내는 것과 다릅니다. lcd_reg.DB4 = com.b4; //입력에 가장 중요한 4비트를 설정합니다. lcd_reg.DB5 = com.b5; lcd_reg.DB6 = com.b6; lcd_reg.DB7 = com.b7; I2C_PCF8574_WriteReg(lcd_reg) ; 지연_us(300); lcd_reg.EN = 0; // 스트로브 펄스를 0으로 재설정하면 표시기가 데이터를 읽습니다. I2C_PCF8574_WriteReg(lcd_reg); 지연_us(300); lcd_reg = 0; lcd_reg .BL = BL_status.b0; lcd_reg.EN = 1; lcd_reg.RS = 1; lcd_reg.DB4 = com.b0; //입력에 최하위 4비트 설정 lcd_reg.DB5 = com.b1; lcd_reg.DB6 = com.b2; lcd_reg.DB7 = com.b3; I2C_PCF8574_WriteReg(lcd_reg); Delay_us(300); lcd_reg.EN = 0; I2C_PCF8574_WriteReg(lcd_reg); Delay_us(300); ) void lcd_I2C_Init(void) ( I2C1_Init_Advanced(400000, &_ GPIO_MODULE_I2C1_PB67 ); //MK용 I2c 모듈을 초기화합니다.lay_ms(200) ;lcd_Command(0x28); // 클록 모드당 4비트로 표시 Delay_ms (5); lcd_Command(0x08); //디스플레이에 대한 데이터 출력을 비활성화합니다.lay_ms(5); lcd_Command(0x01); //디스플레이 지우기 Delay_ms (5); lcd_Command(0x06); //기호를 표시한 후 자동 커서 이동을 활성화합니다. Delay_ms (5); lcd_Command(0x0C); //커서를 표시하지 않고 정보 표시를 켭니다.lay_ms(25); ) void lcd_I2C_txt(char *pnt) //문자열을 디스플레이에 출력합니다( unsigned short int i; //임시 문자 배열 인덱스 변수 char tmp_str; //임시 문자 배열, 길이는 디스플레이 길이보다 1 큼 줄은 NULL ASCII 문자 0x00으로 종료되어야 하기 때문에 сiv strncpy(tmp_str, pnt, displenth); //원래 문자열의 displenth 문자만 임시 문자열에 복사합니다. for (i=0; i 이제 새로 생성된 라이브러리를 기본 함수를 사용하여 파일에 연결해 보겠습니다.

#include "i2c_lcd.h" //unsigned int i 헤더 파일 포함; //임시 변수 카운터 void main() ( lcd_I2C_Init(); //디스플레이 초기화 lcd_I2C_backlight (1); //백라이트 켜기 lcd_I2C_txt ("Hello habrahabr"); //줄 표시 while (1) ( Delay_ms( 1000) ; lcd_I2C_Goto (2,1); //라인 2의 문자 1로 이동 lcd_i2c_int (i); //i++ 값 표시; //카운터 증가 ) )

모든 것이 올바르게 조립되면 표시기에 텍스트가 표시되고 매초마다 카운터가 증가하는 것을 볼 수 있습니다. 일반적으로 복잡한 것은 없습니다 :)

다음 기사에서 우리는 i2c 프로토콜과 이를 사용하는 장치를 계속해서 이해할 것입니다. EEPROM 24XX 메모리와 MPU6050 가속도계/자이로스코프를 사용한 작업을 고려해 보겠습니다.

오늘 우리 수술대에 새로운 손님이 왔습니다. 바로 Microchip 제품인 MCP23008-E 포트 확장기입니다. 이는 이름에서 알 수 있듯이 마이크로컨트롤러의 I/O 다리 수가 갑자기 부족해질 경우 이를 늘리기 위한 것입니다. 물론 다리와 출구가 필요하다면 걱정하지 않고 가져갈 수 있습니다. 입력 다리가 필요한 경우 엄격한 논리에 기반한 솔루션이 있습니다. 입력과 출력이 모두 필요하고 입력에 대한 제어된 풀업도 필요하다면 포트 확장기가 아마도 가장 일반적인 솔루션일 것입니다. 장치 가격은 매우 적당합니다. 약 1 달러입니다. 이 기사에서는 AVR 마이크로 컨트롤러를 사용하여 이 마이크로 회로를 제어하는 ​​방법을 자세히 설명하려고 합니다.

먼저, 특성에 대해 조금:

  • 8개의 독립 포트 핀
  • 외부 세계와의 통신을 위한 인터페이스 - I2C(최대 주파수 1.7MHz)
  • 입구를 위한 맞춤형 풀업
  • 특정 입력 상태가 변경되면 다리가 갑자기 움직일 수 있음
  • 마이크로 회로 주소 설정을 위한 3개의 입력(하나의 버스에 8개의 장치를 걸 수 있음)
  • 1.8~5.5V의 작동 전압
  • 낮은 전류 소비
  • 다양한 인클로저 선택 가능(PDIP/SOIC/SSOP/QFN)

저는 I2C 인터페이스를 선호합니다. 적은 수의 배선이 매력적입니다. :-) 그러나 매우 빠른 속도(최대 10MHz)가 필요한 경우 MCP23S08에 있는 SPI 인터페이스를 사용해야 합니다. 내가 이해하는 MCP23S08과 MCP23008의 차이점은 인터페이스와 칩 주소 설정을 위한 다리 수에만 있습니다. 짧은 것을 좋아하는 사람은 누구입니까? mikruhi의 핀아웃은 데이터시트에 있지만 어떤 식으로든 흥미롭지 않으며 여기서는 고려하지 않습니다. 따라서 즉시 이 장치로 작업을 시작하는 방법을 살펴보겠습니다. 모든 작업은 마이크로 회로 레지스터의 특정 데이터를 쓰고 읽는 것으로 귀결됩니다. 다행스럽게도 레지스터가 전혀 많지 않고 11개뿐이었습니다. 레지스터에서 데이터를 쓰고 읽는 것은 매우 간단합니다. 이에 대해 게시해 주세요. 우리가 레지스터에 대해 이야기하고 있는 것이 아니라는 것은 사실이지만 원리는 동일합니다. 이제 남은 것은 어떤 레지스터에서 무엇을 읽을지, 어떤 레지스터에 무엇을 쓸지 파악하는 것입니다. 물론 데이터시트가 이에 도움이 될 것입니다. 데이터 시트에서 우리는 마이크로 회로에 다음과 같은 레지스터가 있음을 알 수 있습니다.

IODIR 레지스터
데이터가 흐르는 방향을 지정합니다. 특정 다리에 해당하는 비트가 1로 설정되면 해당 다리는 입력입니다. 0으로 재설정되면 종료합니다. 간단히 말해서 이 레지스터는 AVR의 DDRx와 유사합니다(AVR에서만 1이 출력이고 0이 입력임).

IPOL 등록
레지스터 비트가 설정되면 해당 레그에 대해 입력 반전이 활성화됩니다. 이는 다리에 로그를 적용하는 경우를 의미합니다. 0이면 GPIO 레지스터에서 1로 간주되고 그 반대도 마찬가지입니다. 이 기능의 유용성은 매우 의심스럽습니다.

GPINTEN 등록
이 레지스터의 각 비트는 특정 포트 핀에 해당합니다. 비트가 설정된 경우 입력으로 구성된 해당 포트 핀이 인터럽트를 일으킬 수 있습니다. 비트가 재설정되면 포트 레그로 무엇을 하든 중단이 발생하지 않습니다. 인터럽트 조건은 다음 두 레지스터에 의해 설정됩니다.

DEFVAL 레지스터
입력용으로 구성된 핀의 현재 값은 이 레지스터와 지속적으로 비교됩니다. 갑자기 현재 값이 이 레지스터에 있는 값과 달라지기 시작하면 인터럽트가 발생합니다. 간단히 말해서 비트가 설정되면 해당 레그에서 레벨이 High에서 Low로 변경될 때 인터럽트가 발생합니다. 비트가 지워지면 상승 에지에서 인터럽트가 발생합니다.

INTCON 등록
이 레지스터의 각 비트는 특정 포트 핀에 해당합니다. 비트가 지워지면 논리 레벨이 반대 레벨로 변경되면 인터럽트가 발생합니다. 비트가 설정되면 인터럽트 발생은 DEFVAL 레지스터의 영향을 받습니다(그렇지 않으면 완전히 무시됩니다).

IOCON 등록
설정 레지스터입니다. 이는 4개의 비트로 구성됩니다.
SEQOP— 비트 제어 주소 자동 증가. 설치된 경우 자동 증가가 비활성화되고, 그렇지 않으면 활성화됩니다. 이를 끄면 매번 주소를 전송할 필요가 없기 때문에 동일한 레지스터의 값을 매우 빠르게 읽을 수 있습니다. 11개 레지스터 모두를 차례로 빠르게 읽어야 하는 경우 자동 증가를 활성화해야 합니다. 레지스터에서 바이트를 읽을 때마다 주소 자체는 1씩 증가하므로 전송할 필요가 없습니다.
DISSLW—이 비트가 무엇인지 아는 사람. 아무리 비틀어도 모든 것이 여전히 작동합니다. 누군가 설명하면 기쁘겠습니다.
하엔— INT 출력 설정 비트. 설정된 경우 핀은 오픈 드레인으로 구성되고, 비트가 재설정되면 INT 레그의 활성 레벨이 INTPOL 비트를 결정합니다.
INTPOL— INT 레그의 활성 레벨을 결정합니다. 설정된 경우 활성 수준은 1이고, 그렇지 않으면 0입니다.

아직 좀 있어요 하엔하지만 이 칩에서는 사용되지 않습니다(MCP23S08의 하드웨어 주소 지정 핀을 켜거나 끕니다).

GPPU 레지스터
입력 풀업을 제어합니다. 비트가 설정되면 100kOhm 저항을 통해 전원 공급 장치에 대한 풀업이 해당 핀에 나타납니다.

INTF 레지스터
인터럽트 플래그 레지스터. 비트가 설정되면 해당 포트 레그가 인터럽트를 발생시켰다는 의미입니다. 물론 필요한 레그에 대한 인터럽트는 GPINTEN 레지스터에서 활성화되어야 합니다.

INTCAP 레지스터
인터럽트가 발생하면 전체 포트가 이 레지스터로 읽혀집니다. 이후에 더 많은 인터럽트가 발생하면 이 레지스터의 내용은 GPIO나 읽을 때까지 새 값으로 덮어쓰이지 않습니다.

GPIO 레지스터
레지스터에서 데이터를 읽을 때 포트 핀의 논리 레벨을 읽습니다. 데이터를 기록할 때 논리적 수준을 설정합니다. 이 레지스터에 쓰면 동일한 데이터가 자동으로 OLAT 레지스터에 기록됩니다.

OLAT 등록
이 레지스터에 데이터를 쓰면 데이터가 포트로 출력됩니다. 거기에서 데이터를 읽으면 실제로 포트 입력에 있는 내용이 아니라 기록된 내용을 읽게 됩니다. 입력을 읽으려면 GPIO만 사용합니다.

제 생각에는 레지스터에 대한 설명은 매우 정상적이며 누구의 마음도 아프게 해서는 안 됩니다. 이제 재미로 포트 확장기에 연결된 4개의 버튼을 폴링하고 해당 상태에 따라 동일한 확장기에 연결된 4개의 해당 LED를 켜거나 끄는 작은 데모를 작성해 보겠습니다. 예제는 중단 없이 최대한 간단하게 진행됩니다. 우리는 단순히 버튼의 상태를 지속적으로 읽고 이 상태를 LED에 표시합니다. 우리 장치의 다이어그램은 다음과 같습니다.

인터럽트 핀은 연결될 필요가 없습니다. 여기서는 필요하지 않지만 리셋 레그용 풀업은 필요합니다! 조임 부족으로 인해 포트 확장기가 주기적으로 재설정된다는 사실을 깨달을 때까지 많은 시간을 보냈습니다. 이제 코드를 살펴보겠습니다. 물론 우리는 에 쓸 것입니다. 간단한 코드:

프로그램라시리텔; const AdrR=%01000001; //읽기 비트가 있는 칩의 주소 const AdrW=%01000000; //비트 레코드가 있는 칩의 주소 var r:byte; ///프로시저는 Dat 변수의 데이터를 주소 Adr의 레지스터에 씁니다. Procedure WriteReg(Dat,Adr:byte); TWI_Start() 시작; TWI_Write(AdrW); TWI_Write(Adr); TWI_Write(Dat); TWI_Stop(); 끝; ///함수는 Adr 주소의 레지스터 값을 반환합니다. Function ReadReg(Adr:byte):byte; var a:바이트; TWI_Start() 시작; TWI_Write(AdrW); TWI_Write(Adr); TWI_시작(); TWI_Write(AdrR); a:=TWI_Read(0); TWI_Stop(); 결과:=a; 끝; TWI_INIT(200000) 시작; ///i2c 초기화 중 WriteReg(%00001111,0x00); //최하위 4비트는 입력, 나머지 4비트는 출력 WriteReg(%00001111,0x06); //에 4개 입력에 대한 풀업 TRUE일 때 Begin을 수행합니다. r:=ReadReg(0x09); //입력 상태를 읽습니다. r:= NOT r; //비트를 반전시켜야 합니다. 그렇지 않으면 버튼을 놓으면 LED가 켜집니다. r:= r shl 4; //왼쪽으로 4비트 이동... WriteReg(r,0x0A); //버튼 상태를 표시합니다. end; 끝.

게시일: 2016년 10월 26일

이전 기사에서는 I 2 C 버스를 마스터로 사용하여 STM32의 작동을 살펴보았습니다. 즉, 그는 리더였으며 센서를 심문했습니다. 이제 STM32를 슬레이브로 만들고 요청에 응답해 보겠습니다. 즉 STM32 자체가 센서로 작동합니다. 주소가 0부터 0xFF까지인 레지스터에 255바이트의 메모리를 할당하고 마스터가 이를 쓰거나 읽을 수 있도록 허용합니다. 예제를 너무 간단하게 만들지 않기 위해 STM32를 I 2 C 인터페이스를 갖춘 아날로그-디지털 변환기로 만들어 보겠습니다. ADC는 8개 채널을 처리합니다. 컨트롤러는 레지스터에서 읽을 때 변환 결과를 마스터에 제공합니다. ADC 변환 결과는 12비트이므로 ADC 채널당 2개의 레지스터(2바이트)가 필요합니다.

i2c_slave.h다음 설정이 포함되어 있습니다.

I2CSLAVE_ADDR– 당사 장치의 주소

ADC_ADDR_START– ADC 변환 결과를 담당하는 레지스터의 시작 주소입니다.

파일에 있음 i2c_slave.c우리는 기능에 가장 관심이 있습니다 get_i2c1_ram그리고 set_i2c1_ram. 기능 get_i2c1_ram레지스터에서 데이터를 읽는 일을 담당합니다. 마스터에 제공된 지정된 주소에서 데이터를 반환합니다. 우리의 경우에는 배열에서 데이터를 읽습니다. i2c1_ram, 그러나 마스터가 ADC 결과에 할당된 범위의 레지스터 주소를 요청하면 ADC 변환 데이터가 전송됩니다.

get_i2c1_ram:

Uint8_t get_i2c1_ram(uint8_t adr) ( //ADC 데이터 if ((ADC_ADDR_START<= adr) & (adr < ADC_ADDR_START + ADC_CHANNELS*2)) { return ADCBuffer; } else { // Other addresses return i2c1_ram; } }

기능 set_i2c1_ram– 마스터로부터 받은 데이터를 지정된 주소의 레지스터에 씁니다. 우리의 경우 데이터는 단순히 배열에 기록됩니다. i2c1_ram. 그러나 이것은 선택 사항입니다. 예를 들어 수표를 추가하고 특정 번호가 특정 주소에 도착하면 몇 가지 작업을 수행할 수 있습니다. 이렇게 하면 마이크로컨트롤러에 다양한 명령을 보낼 수 있습니다.

set_i2c1_ram:

무효 set_i2c1_ram(uint8_t adr, uint8_t val) ( i2c1_ram = val; return; )

초기화는 매우 간단합니다.

Int main(void) ( SetSysClockTo72(); ADC_DMA_init(); I2C1_Slave_init(); while(1) ( ) )

먼저 컨트롤러의 최대 작동 주파수를 설정합니다. I 2 C 버스의 지연을 피해야 할 때 최대 속도가 필요하며, 그런 다음 DMA를 사용하여 ADC 작동을 시작합니다. 에 대한 . 에 대한 . 마지막으로 I 2 C 버스를 다음과 같이 초기화합니다. 노예. 보시다시피 복잡한 것은 없습니다.

이제 STM32 모듈을 Raspberry Pi에 연결해 보겠습니다. ADC 채널에 전위차계를 연결해 보겠습니다. 그리고 컨트롤러에서 ADC 표시기를 읽습니다. I 2 C 버스가 작동하려면 버스의 각 라인에 풀업 저항을 설치해야 한다는 점을 잊지 마십시오.

Raspberry 콘솔에서 우리 장치가 I 2 C 버스에 표시되는지 확인해 보겠습니다.

I2c검출 -y 1

보시다시피 장치 주소는 0x27, 0x4E를 지정했지만. 시간이 나면 왜 이런 일이 일어났는지 생각해 보세요.

I 2 C-슬레이브 장치의 레지스터를 읽으려면 다음 명령을 실행하십시오.

I2cget -y 1 0x27 0x00

어디:
0x27– 장치 주소,
0x00– 레지스터 주소(0x00…0xFF).

I 2 C-슬레이브 장치의 레지스터에 쓰려면 다음 명령을 실행하십시오.

I2cset -y 1 0x27 0xA0 0xDD

드:
0x27– 장치 주소,
0xA0– 등록 주소
0xDD-8비트 데이터(0x00…0xFF)

이전 명령은 레지스터에 숫자 0xDD를 썼습니다. 0xA0(처음 16개 레지스터에 쓸 수 있지만 의미는 없지만 ADC용으로 예약되어 있습니다.) 이제 읽어보자:

I2cget -y 1 0x27 0xA0

ADC 채널 데이터를 읽는 프로세스를 단순화하기 위해 다음 스크립트를 작성했습니다.

#!/usr/bin/env python import smbus import time 버스 = smbus.SMBus(1) 주소 = 0x27 while (1): ADC = (); for i in range(0, 8): LBS = 버스.read_byte_data(주소, 0x00+i*2) MBS = 버스.read_byte_data(주소, 0x00+i*2+1) ADC[i] = MBS*256 + LBS ADC time.sleep(0.2) 인쇄

8개 ADC 채널 모두의 결과를 폴링하여 콘솔에 표시합니다.

비슷한 방식으로 여러 마이크로컨트롤러를 결합할 수 있습니다. 그 중 하나는 Master()이고 다른 하나는 Slave여야 합니다.

나는 당신의 성공을 기원합니다!




맨 위