[ BBB - yocto (12) ] I2C
0) 준비물
1. mpu 6050
2. MPU-6000-Register-Map1.pdf3. MPU-6000-Datasheet1.pdf
노드 속성을 알고 싶다면, 아래 사이트에 들어가서 compatible에 맞는 드라이버를 찾으면 된다.
https://www.kernel.org/doc/Documentation/devicetree/bindings
am33xx의 i2c compatible에 omap4-i2c를 사용하므로 아래 2개 사이트에서 i2c 노드에 대한 설명을 볼 수 있다.
https://www.kernel.org/doc/Documentation/devicetree/bindings/i2c/ti%2Comap4-i2c.yaml
https://www.kernel.org/doc/Documentation/devicetree/bindings/i2c/i2c-omap.txt
1) I2C란?
- i2c는 일종의 통신 프로토콜이다. i2c에는 데이터 버스(SDA)와 클럭신호(SCL)가 존재한다.
- slave와 master가 있다. 통신을 할 때 Master는 하나만 있을 수 있으며 슬레이브 디바이스는 여러개가 존재할 수 있다.
- 정해진 통신 절차를 사용해 통신한다.
- i2c 슬레이브의 주소는 10비트를 사용할 수도 있고, 7비트를 사용할 수 도 있다.
- 한번에 여러 바이트를 읽거나 쓸 수 있다. 아래는 데이터의 읽기/쓰기 시퀀스이다.
1. 1byte 동작
- 여기서 쓰기 비트는0, 읽기 비트는 1에 해당한다. 모든 데이터는 클럭에 맞춰서 읽히거나 쓰여진다.
자 이제 오버레이를 수정하여 핀 멀티플랙싱 설정을 해주자.
/dts-v1/; /plugin/; #include <dt-bindings/pinctrl/am33xx.h> &{/chosen} { overlays{ BB-USER_DEVICE_TREE.kernel = __TIMESTAMP__; }; }; /{ fragment@0 { target = <&am33xx_pinmux>; __overlay__{ uart1_pins: uart1-pins { pinctrl-single,pins = < AM33XX_PADCONF(AM335X_PIN_UART1_RXD, PIN_INPUT, MUX_MODE0) AM33XX_PADCONF(AM335X_PIN_UART1_TXD, PIN_OUTPUT, MUX_MODE0) >; }; i2c1_pins: i2c1-pins { pinctrl-single,pins = < AM33XX_PADCONF(AM335X_PIN_SPI0_CS0, PIN_INPUT_PULLUP, MUX_MODE2) AM33XX_PADCONF(AM335X_PIN_SPI0_D1, PIN_INPUT_PULLUP, MUX_MODE2) >; }; }; }; fragment@1{ target = <&uart1>; __overlay__{ pinctrl-names = "default"; pinctrl-0 = <&uart1_pins>; status = "okay"; }; }; fragment@2{ target = <&i2c1>; __overlay__{ pinctrl-names = "default"; pinctrl-0 = <&i2c1_pins>; status = "okay"; clock-frequency = <400000>; }; }; };
i2c 노드의 새로운 속성 clock-frequency에 대해 볼 수 있다.
i2c 클럭 주파수를 설하는 속성이다.
클럭 주파수는 os가 동작하는 도중에 변경하는 것은 불가능하며, 디바이스 트리의 설정을 계속 따른다.
$ bitbake -c compile -f virtual/kernel $ bitbake -c deploy -f virtual/kernel
오버레이를 컴파일하고, 배포 태스크를 실행해 주자
여기서 input pullup을 사용했는데 회로적으로 open drain을 i2c 디바이스가 사용하기 때문이다.
output pullup의 경우 devices or resources are busy 오류가 뜨니 사용하지 말자. 원인은 버스 충돌로 output pullup이 논리적으로 구현되었기 때문에 버스에 접근하면 무조건 오류가 나는 것으로 보인다.
부트 섹터 1번의 BB-USER_DEVICE_TREE.dtbo를 교체해 주자.
/dev밑의 파일을 보면 i2c-1이 활성화 된 것을 볼 수 있다.
3) 어플단에서 i2c 제어하기
2가지 방식으로 i2c를 제어할 수 있다.
첫번째는 unistd.h의 read/write 함수를 방식이다. 두번째는 ioctl을 사용하고 linux/i2c-dev.h, linux/i2c.h 두가지 헤더를 사용하는 방식이다.
1. unistd.h를 사용하는 방식
- i2c 제어가 read/write 함수를 사용해서 이뤄진다. 아래는 동작 과정이다.
(1) "/dev/i2c-[버스 번호]" 를 open 함수로 열어준다.
(2) ioctl로 slave 주소를 설정한다. (I2C_SLAVE 를 ioctl __request에 넣어주고, 슬레이브 주소를 인자로 넘겨준다.)
(3) read/write 함수를 사용해 읽기 쓰기 동작을 진행해 준다. 파일 디스크립터는 (1)의 값이다.
(4) 읽기 동작의 경우 write 함수를 먼저 사용하여, 슬레이브 주소를 i2c 디바이스에 전송한다. 그 이후 read 함수를 사용해 읽어낼 버퍼 주소와, 버퍼 길이를 전달해 주면 된다.
이때, read를 여러번 쓰지 말고 한번에 쓰도록 한다. (write로 레지스터 주소 쓴 이후, read 함수 여러번 써서 읽는 건 불가능하다.)
(5) 쓰기 동작의 경우, write 함수로 한번에 전송해야 한다. 레지스터 주소 + 쓸 데이터 값을 포함하여 한 버퍼에 집어 넣고 그 버퍼의 크기를 인자로 전달해 준다.
2. i2c.h와 i2c-dev.h를 사용하는 방식
- ioctl 함수만 사용한다. i2c_msg, i2c_rdwr_ioctl_data 구조체를 사용하여 전송한다.
- 구조체 i2c_msg의 맴버
-구조체 i2c_rdwr_ioctl_data의 멤버 변수
-아래는 동작 과정이다.
(1) "/dev/i2c-[버스 번호]" 를 open 함수로 열어준다.
(2) i2c_msg 멤버에서 읽기를 할 것인지, 쓰기를 할 것인지를 flags에 정해주고, addr에는 i2c 주소를, buf에서는 쓸 버퍼의 길이를, len에는 버퍼 길이를 넣어준다.
(3) 읽기 동작의 경우, i2c_msg의 배열 크기를 2로 설정한다.
인덱스0의 i2c_msg에는 flags에 0을 입력해 쓰기 모드로 설정한다. 그리고 buf에는 레지스터 주소가 저장된 버퍼의 주소를, len에는 buf 길이(바이트 단위)를 입력해 준다.
flags에 I2C_M_RD를 넣어 읽기 모드로 설정한다. 그리고 buf에 쓸 주소가 저장될 버퍼를, len에는 버퍼 크기를 입력해준다.
이후 i2c_rdwr_ioctl_data 구조체의 변수에 i2c_msg 주소와 메시지 수를 입력하고 ioctl로 I2C_RDWR을 __request로, 추가 가변 인자로 i2c_rdwr_ioctl_data 구조체 주소를 넘겨주면 된다.
(4) 쓰기 동작의 경우, i2c_msg의 배열 크기를 1로 설정한다.
i2c_msg에는 flags에 0을 입력해 쓰기 모드로 설정한다. 그리고 buf에는 레지스터 주소와 쓸 데이터가 저장된 버퍼의 주소를, len에는 buf 길이(바이트 단위)를 입력해 준다.
이후 i2c_rdwr_ioctl_data 구조체의 변수에 i2c_msg 주소와 메시지 수를 입력하고 ioctl로 I2C_RDWR을 __request로, 추가 가변 인자로 i2c_rdwr_ioctl_data 구조체 주소를 넘겨주면 된다.
4) mpu6050 어플리케이션 구현
아래는 어플리케이션의 동작 과정이다.
1. 슬립 모드 해제
2. 가속도 범위 설정
3. x, y, z + 온도 값 읽기 (burst read 사용, burst read는 2바이트 씩 레지스터 값을 읽는 것을 의미한다.)
https://github.com/leejugy/mpu6050_i2c
레지스터 맵을 보면서 제어해 보는 것을 추천한다.
5) 결과
댓글
댓글 쓰기