안녕하세요, 오랜만입니다.
이번 이슈는 PSOC4 에서 I2C 통신을 하다가, I2CMasterWriteBuf() 함수를 사용할 경우 잘못된 SUB ADDRESS에 데이터를 송신하면 송신한 데이터들이 내부 FIFO 버퍼에 쌓여있다가 정상적인 SUB ADDRESS에 다시 데이터를 쓸 경우 이전에 쌓여있는 데이터가 출력되는 것을 발견했습니다.
이 문제의 발생빈도는 on-chip program 시에는 50% 정도이지만, 디버그 모드에서는 80%이상이었습니다. 즉 항상 발생하는 것은 아니라는 것이지만, 문제가 되기에는 충분합니다.
현재 Cypress에 기술 문의를 요청한 상태이고 빨리 해결되었으면 좋겠네요.
항상 제대로된 어드레스에 데이터를 전송하는 것은 아니라, 이런 경우도 있다는 점을 업체에서 인지하지 못했던 것 같습니다.
글목록
2016년 1월 20일 수요일
[PSOC] PSOC5LP(CY8C5268) 에 USB UART 연결 테스트
안녕하세요, 이번에는 USB UART(VCOM) 을 다뤄 보려 합니다.
USB는 쓰기 어려울 거라 생각하고, 계속 안쓰다가 이번에 더이상 확장할 통신 포트가 없어서 에라 모르겠다하고 하드웨어를 설계해 버렸습니다.
그런데 써보니 정말 좋습니다. 오늘 하루 몇시간 만에, USB UART 와 USB Bootloader , USB Bootloaderble App 까지 테스트를 다 해봤습니다.
테스트는 간단하게 끝났지만, 여기에 글을 올리는 시간이 몇배는 더 걸리는 것 같습니다.
일단은 첫번째로 USB UART 테스트에 관한 글을 올려봅니다.
먼저 PSOC 중에서 USB가 있는 MCU를 준비합니다.
저는 PSOC-5LP 계열의 MCU를 준비했습니다.
물론 USB 콘넥터를 제대로 연결해야 겠지요. +5V, GND, DM, DP 를 USB 케이블에 연결해 둡니다.
[USB cable 연결]
[PSOC-5LP 의 USB 연결 회로도]
이렇게 USB 케이블을 연결했으면 일단 컴퓨터의 USB 포트에 꼽아 두십시요. 꼽으면 USB 포트로부터 +5V를 받아서 PSOC이나 ㄷ다른 IC에 공급할 3.3V 전원도 LDO 레귤레이터에 의해서 공급됩니다.
그럼 다음으로 USB UART 예제 프로그램을 불러와서 프로젝트를 만들어 보겠습니다.
우선 다음 그림과 같이 File-New-Project 순으로 클릭하시면 프로젝트 만들기 메뉴가 나옵니다.
개발키트로 테스트 하는 것이 아니고 회사 프로젝트로 만든 보드로 테스트 하는 것이므로 다음 그림과 같이 Target device 를 선택합니다.
다음으로 코드 예제를 import 할 것이므로 Code Example을 선택하고 Next로 갑니다.
코드 예제가 너무 많으므로 usb 로 검색을 해보면, USB UART 예제가 있으니 선택하고 Next로 갑니다.
필요시에 폴더를 새로 만들거나 지정해서 프로젝트 만들기를 마무리 합니다.
자, 이제 프로젝트가 만들어 졌네요. ^^.
그런데, 제게는 LCD가 없네요. TopDesign.cysch 에서 LCD 삭제합니다.
그런데, PSOC-5LP로 프로젝트를 만들면 기본으로 제가 쓰는 PSOC 모델이 아닌 것이 선택되네요. 그래서 project-Device select 메뉴에서 적당한 PSOC-5LP 장치를 선택합니다.
아... 이런, 옆에 Resource Meter는 컴파일을 다 해야지만 결과가 나오는데, 먼저 붙여 버렸네요. 이 그림을 붙여 넣은 이유는 USB UART 를 사용하는데 UDB가 전혀 안들어간다는 사실을 알려 드리기 위함입니다. 그냥 아주 좋은 통신 포트를 공으로 1개 얻었다 생각하시면 됩니다. ^^
[PSOC Device Select]
다음으로는 아까 LCD 콤포넌트를 지웠기 때문에 예제 소스 코드에 남아있는 LCD 관련 코드에서 컴파일 시에 에러가 날겁니다.
예러를 없애야 하는데, 이상태로 컴파일을 해버립시다. 그러면 에러가 나죠?
다음처럼 에러가 난 부분을 // 로 주석처리해서 다 막아버리세요.
그리고 나서 다시 컴파일을 하면 에러가 싹 사라지고, 에러 없이 컴파일이 완료 됩니다.
에러가 없으므로 3번 프로그래밍 아이콘 버튼을 눌러 프로그램을 PSOC에 다운로드 합니다.
물론 JTAG를 연결한 상태로 하셔야겠죠. ^^
다운로드 되면 바로 프로그램이 실행 되는데, 띠용 소리 나면서 디바이스 드라이버를 자동으로 찾아서 가상 시리얼 포트가 잡힙니다.
Cypress USB UART(COM24) 로 잡혔군요. 프로그램 잘 동작하는 것 같습니다. ^^
어떤 때는, 드라이버가 자동으로 안잡히는 분들이 있을 겁니다. 이 때는, " C:\Program Files (x86)\Cypress " 경로로 드라이버 찾기를 지정해 주면 알아서 찾아 깔아 주고 USB UART 가 잡힐 것입니다.
이 프로그램은 Rxd 로 데이터를 받으면 Txd로 그대로 ECHO 해 주는 프로그램이라서, 터미날을 1개 열어서 해당 COM PORT에 연결한 후, 데이터를 보내보면 에코로 돌아오는 것을 볼 수 있습니다. 예... 프로그램 아주 잘 돌아가고 있네요.
또 하나, USB 니까 속도도 빠르겠죠? TERA TERM 터미날 프로그램의 최대 속도인 921.6Kbps로 설정을 바꿔봤습니다.
데이터를 키보드로 두드려서 보냈더니 echo가 제대로 오는 것을 볼 수 있네요.
^^ 당신은 거의 1Mbps 속도의 UART를 득템하셨습니다.
2016년 1월 6일 수요일
[PSOC] Psoc4 GPIO INPUT (IN/OUT Test 2/2)
이번에는 입력 테스트를 해 보겠습니다.
1. 먼저 Digital Input Pin [v2.10] 콤포넌트를 선택해서 cysch 파일에 넣습니다.
이름은 적당히 지어 줍니다. 저는 SW_IN_1,SW_IN_2,SW_IN_3 이라고 지었고, 나중에 컴파일하면 자동으로 입력함수인 SW_IN_1_Read() ,SW_IN_2_Read() ,SW_IN_3_Read() 함수가 생깁니다.
2. 입력 핀 설정 중에서 , HW connection 옵션을 제거합니다.
또한 핀 Drive mode 에서 resistive pullup으로 내부 풀업을 걸어 줍니다.
3. cydwr 파일에서 핀을 회로도에 맞게 지정을 해 줍니다.
PSOC4 IC 의 입력핀 회로도와 이에 연결된 SW 회로도 첨부합니다.
5. 이제 컴파일을 하면 자동으로 SW_IN_x_Read() 함수가 생기는데 이것을 사용해서 포트값을 읽으면 됩니다.
코드의 예는 다음과 같습니다.
for(;;)
{
if (f_50ms)
{
f_50ms = 0;
sw_stat_old = sw_stat_new;
sw_stat_new = (((SW_IN_1_Read()<<0 b="" ead="" x07="">0>
if (sw_stat_new != sw_stat_old)
{
UART_1_UartPutString("switch stat = ");
ser_tx_buf[0] = htoa(sw_stat_new);
ser_tx_buf[1] = '\n';
ser_tx_buf[2] = '\r';
ser_tx_buf[3] = '\0';
user_UART_PutString(ser_tx_buf);
}
}
/* Place your application code here. */
}
1. 먼저 Digital Input Pin [v2.10] 콤포넌트를 선택해서 cysch 파일에 넣습니다.
이름은 적당히 지어 줍니다. 저는 SW_IN_1,SW_IN_2,SW_IN_3 이라고 지었고, 나중에 컴파일하면 자동으로 입력함수인 SW_IN_1_Read() ,SW_IN_2_Read() ,SW_IN_3_Read() 함수가 생깁니다.
2. 입력 핀 설정 중에서 , HW connection 옵션을 제거합니다.
또한 핀 Drive mode 에서 resistive pullup으로 내부 풀업을 걸어 줍니다.
3. cydwr 파일에서 핀을 회로도에 맞게 지정을 해 줍니다.
PSOC4 IC 의 입력핀 회로도와 이에 연결된 SW 회로도 첨부합니다.
5. 이제 컴파일을 하면 자동으로 SW_IN_x_Read() 함수가 생기는데 이것을 사용해서 포트값을 읽으면 됩니다.
코드의 예는 다음과 같습니다.
for(;;)
{
if (f_50ms)
{
f_50ms = 0;
sw_stat_old = sw_stat_new;
sw_stat_new = (((SW_IN_1_Read()<<0 b="" ead="" x07="">0>
if (sw_stat_new != sw_stat_old)
{
UART_1_UartPutString("switch stat = ");
ser_tx_buf[0] = htoa(sw_stat_new);
ser_tx_buf[1] = '\n';
ser_tx_buf[2] = '\r';
ser_tx_buf[3] = '\0';
user_UART_PutString(ser_tx_buf);
}
}
/* Place your application code here. */
}
2016년 1월 5일 화요일
[PSOC] Psoc4 GPIO OUTPUT (IN/OUT Test 1/2)
PSOC4 에서의 GPIO IN/OUT TEST
이전 게시물에서 LED를 사용하는 GPIO OUT을 이미 테스트 했는데,
아무 설명을 하지 못해서 INPUT 테스트와 같이 다뤄 보겠습니다.
PSOC IC에 SW를 3개 달아서, 이전에 만들어 놓았던 타이머 인터럽트를 이용해서 50ms 마다 Switch 입력을 읽어서 값이 바뀔 때마다 스위치 값을 UART로 출력하는 내용입니다.
GPIO 출력은 이전에 해뒀던 내용인데, 100ms 마다 포트값을 토글하는 내용입니다.
먼저 OUTPUT을 설명드리겠습니다.
1. PSOC Creator 의 cysch 에서 Digital Output Pin [v2.10] 콤포넌트를 그려 넣습니다. 핀 이름은 적당하게 고치면 되는데 이름에 따라 함수가 자동으로 만들어집니다.
저는 LED_1,LED_2,LED_3 라고 정했는데, 출력 함수 이름은 자동으로 LED_1_Write() , LED_2_Write() , LED_3_Write() 으로 만들어 집니다.
2. OUTPUT PIN 콤포넌트를 마우스로 더블클릭하면 핀 설정이 나오는데, HW connection 체크를 해제 해 주십시요. 이것은 내부에서 다른 콤포넌트와 연결 하지 않을 경우에 해당되며, 이 옵션을 해제하지 않으면 에러가 납니다.
3. cydwr 파일에서 PSOC4 IC에 OUTPUT으로 사용할 핀을 지정
PSOC4 IC 와 연결된 출력 핀과 여기에 연결된 LED 회로도 그림을 아래 첨부합니다.
4. 소스 코드에서 포트 이름에 따라 자동 생성된 LED_x.c 안의 LED_x_Write() 함수로 비트를 ON(1),OFF(0) 하면 됩니다.
이전에 만든 타이머 인터럽트 코드 안에 있는 출력함수 예는 다음과 같습니다.
CY_ISR(Timer_INT_Handler)
{
uint32 InterruptHpn;
static uint8_t LED_1_stat=0,LED_2_stat=0,LED_3_stat=0;
/* Check interrupt source and clear Inerrupt */
InterruptHpn = Timer_1_GetInterruptSourceMasked();
if (InterruptHpn == Timer_1_INTR_MASK_CC_MATCH)
{
Timer_1_ClearInterrupt(Timer_1_INTR_MASK_CC_MATCH);
}
else
{
cnt_50ms++;
if (cnt_50ms > 49)
{
f_50ms = 1;
}
LED_1_stat = 1-LED_1_stat;
LED_2_stat = 1-LED_2_stat;
LED_3_stat = 1-LED_3_stat;
LED_1_Write(LED_1_stat);
LED_2_Write(LED_2_stat);
LED_3_Write(LED_3_stat);
Timer_1_ClearInterrupt(Timer_1_INTR_MASK_TC);
}
}
이전 게시물에서 LED를 사용하는 GPIO OUT을 이미 테스트 했는데,
아무 설명을 하지 못해서 INPUT 테스트와 같이 다뤄 보겠습니다.
PSOC IC에 SW를 3개 달아서, 이전에 만들어 놓았던 타이머 인터럽트를 이용해서 50ms 마다 Switch 입력을 읽어서 값이 바뀔 때마다 스위치 값을 UART로 출력하는 내용입니다.
GPIO 출력은 이전에 해뒀던 내용인데, 100ms 마다 포트값을 토글하는 내용입니다.
먼저 OUTPUT을 설명드리겠습니다.
1. PSOC Creator 의 cysch 에서 Digital Output Pin [v2.10] 콤포넌트를 그려 넣습니다. 핀 이름은 적당하게 고치면 되는데 이름에 따라 함수가 자동으로 만들어집니다.
저는 LED_1,LED_2,LED_3 라고 정했는데, 출력 함수 이름은 자동으로 LED_1_Write() , LED_2_Write() , LED_3_Write() 으로 만들어 집니다.
2. OUTPUT PIN 콤포넌트를 마우스로 더블클릭하면 핀 설정이 나오는데, HW connection 체크를 해제 해 주십시요. 이것은 내부에서 다른 콤포넌트와 연결 하지 않을 경우에 해당되며, 이 옵션을 해제하지 않으면 에러가 납니다.
3. cydwr 파일에서 PSOC4 IC에 OUTPUT으로 사용할 핀을 지정
PSOC4 IC 와 연결된 출력 핀과 여기에 연결된 LED 회로도 그림을 아래 첨부합니다.
4. 소스 코드에서 포트 이름에 따라 자동 생성된 LED_x.c 안의 LED_x_Write() 함수로 비트를 ON(1),OFF(0) 하면 됩니다.
이전에 만든 타이머 인터럽트 코드 안에 있는 출력함수 예는 다음과 같습니다.
CY_ISR(Timer_INT_Handler)
{
uint32 InterruptHpn;
static uint8_t LED_1_stat=0,LED_2_stat=0,LED_3_stat=0;
/* Check interrupt source and clear Inerrupt */
InterruptHpn = Timer_1_GetInterruptSourceMasked();
if (InterruptHpn == Timer_1_INTR_MASK_CC_MATCH)
{
Timer_1_ClearInterrupt(Timer_1_INTR_MASK_CC_MATCH);
}
else
{
cnt_50ms++;
if (cnt_50ms > 49)
{
f_50ms = 1;
}
LED_1_stat = 1-LED_1_stat;
LED_2_stat = 1-LED_2_stat;
LED_3_stat = 1-LED_3_stat;
LED_1_Write(LED_1_stat);
LED_2_Write(LED_2_stat);
LED_3_Write(LED_3_stat);
Timer_1_ClearInterrupt(Timer_1_INTR_MASK_TC);
}
}
[PSOC] PSOC4 Timer Interrupt Test
PSOC4 로 Timer Interrupt 를 회사에서 만든 보드로 테스트를 해 봤습니다.
역시 PSOC은 쉽네요.^^
UART는 기본으로 넣었고, 나중에 추가할 I2C도 일단은 H/W 콤포넌트로 추가했습니다.
PSOC4는 UART,SPI,I2C 등의 Serial 통신 모듈은 3 중에 1개를 써도, Serial 통신 모듈 총 2개 중 1개씩 감소합니다. 물론 UDB로 더 만들 수 있지만 UDB가 많이 들어갑니다.
저는 UART 1개, I2C 1개 기본 serial 통신 모듈을 2개 다 사용했네요.
PSOC4에서 타이머 인터럽트 사용하는 내용이 카페에 없어서, 자료를 만들어서 놓습니다.
프로그램시 타이머는 기본으로 사용하는 요소지요.
PSOC4는 타이머를 기본으로 4개를 지원합니다. 더 필요하면 UDB를 사용해서 만들어 넣으면 되는데, UDB가 4개 밖에 없어서 꼭 필요할 때를 위해서 아껴서 쓰는 것이 좋습니다.
<프로그램 테스트 계획>
- 타이머 인터럽트에 의해 주기적(100ms)으로 LED를 토글함.
1. 먼저 TopDesign.cysch 파일에 UART,I2C,Timer 콤포넌트를 추가합니다.
안보여서, 콤포넌트 쪽을 자세히 볼 수 있는 그림을 올립니다.
PSOC Creator 에서 컴파일을 해 보면 우측 옆 태에 Resource Meter를 보면 메모리나 콤포넌트, UDB 의 사용량이 나옵니다.
2. 다음은 Timer 의 설정입니다.
10KHz 의 클럭을 넣었으니 1 주기를 100ms로 맞출려면 1000 번 카운트해야 하므로 Timer 콤포넌트의 period 를 1000으로 수정합니다.
3. Interrupt 는 On terminal count 에 체크하는데, 타이머 끝에서 Interrupt가 발생된다는 의미입니다. Run Mode 는 Continuous 로 계속 반복됩니다. (위 그림 보시면 이해 되겠죠?)
4. System 의 Interrupt 콤포넌트를 Timer Interrupt 출력에 붙입니다.
5. 핀을 회로도에 맞게 지정합니다.
제가 사용하는 보드에는 PSOC4와 다음과 같이 연결되어 있습니다.
- UART RxD=P.4 , TxD=P0.5
- I2C SCL=P4.0 , SDA=P4.1
- LED_1=P2.4 , LED_2=P2.5 , LED_3=P2.6
[회로도] - 잘 안보이지만 연결은 위의 내용처럼 되어 있습니다.
cydwr 파일에서 핀을 위의 내용 처럼 지정합니다.
여기까지 H/W 구성은 마무리 되었습니다.
6. 이제 프로그램을 수정합시다.
먼저 Main() 함수에서 Timer 인터럽트 함수를 지정합니다. 나중에 설정된 주기마다, 이 인터럽트 함수가 실행됩니다.
/* Enable the Interrupt component connected to interrupt */
TC_CC_ISR_StartEx(Timer_INT_Handler);
main()
{
:
Timer_1_Start();
:
}
역시 PSOC은 쉽네요.^^
UART는 기본으로 넣었고, 나중에 추가할 I2C도 일단은 H/W 콤포넌트로 추가했습니다.
PSOC4는 UART,SPI,I2C 등의 Serial 통신 모듈은 3 중에 1개를 써도, Serial 통신 모듈 총 2개 중 1개씩 감소합니다. 물론 UDB로 더 만들 수 있지만 UDB가 많이 들어갑니다.
저는 UART 1개, I2C 1개 기본 serial 통신 모듈을 2개 다 사용했네요.
PSOC4에서 타이머 인터럽트 사용하는 내용이 카페에 없어서, 자료를 만들어서 놓습니다.
프로그램시 타이머는 기본으로 사용하는 요소지요.
PSOC4는 타이머를 기본으로 4개를 지원합니다. 더 필요하면 UDB를 사용해서 만들어 넣으면 되는데, UDB가 4개 밖에 없어서 꼭 필요할 때를 위해서 아껴서 쓰는 것이 좋습니다.
<프로그램 테스트 계획>
- 타이머 인터럽트에 의해 주기적(100ms)으로 LED를 토글함.
1. 먼저 TopDesign.cysch 파일에 UART,I2C,Timer 콤포넌트를 추가합니다.
안보여서, 콤포넌트 쪽을 자세히 볼 수 있는 그림을 올립니다.
PSOC Creator 에서 컴파일을 해 보면 우측 옆 태에 Resource Meter를 보면 메모리나 콤포넌트, UDB 의 사용량이 나옵니다.
10KHz 의 클럭을 넣었으니 1 주기를 100ms로 맞출려면 1000 번 카운트해야 하므로 Timer 콤포넌트의 period 를 1000으로 수정합니다.
3. Interrupt 는 On terminal count 에 체크하는데, 타이머 끝에서 Interrupt가 발생된다는 의미입니다. Run Mode 는 Continuous 로 계속 반복됩니다. (위 그림 보시면 이해 되겠죠?)
4. System 의 Interrupt 콤포넌트를 Timer Interrupt 출력에 붙입니다.
5. 핀을 회로도에 맞게 지정합니다.
제가 사용하는 보드에는 PSOC4와 다음과 같이 연결되어 있습니다.
- UART RxD=P.4 , TxD=P0.5
- I2C SCL=P4.0 , SDA=P4.1
- LED_1=P2.4 , LED_2=P2.5 , LED_3=P2.6
[회로도] - 잘 안보이지만 연결은 위의 내용처럼 되어 있습니다.
cydwr 파일에서 핀을 위의 내용 처럼 지정합니다.
여기까지 H/W 구성은 마무리 되었습니다.
6. 이제 프로그램을 수정합시다.
먼저 Main() 함수에서 Timer 인터럽트 함수를 지정합니다. 나중에 설정된 주기마다, 이 인터럽트 함수가 실행됩니다.
/* Enable the Interrupt component connected to interrupt */
TC_CC_ISR_StartEx(Timer_INT_Handler);
7. Timer 인터럽트 함수를 정의합니다.
매 주기 마다 LED 1,2,3 을 토글합니다.
CY_ISR(Timer_INT_Handler)
{
uint32 InterruptHpn;
static uint8_t LED_1_stat=0,LED_2_stat=0,LED_3_stat=0;
/* Check interrupt source and clear Inerrupt */
InterruptHpn = Timer_1_GetInterruptSourceMasked();
if (InterruptHpn == Timer_1_INTR_MASK_CC_MATCH)
{
Timer_1_ClearInterrupt(Timer_1_INTR_MASK_CC_MATCH);
}
else
{
LED_1_stat = 1-LED_1_stat; // Toggle
LED_2_stat = 1-LED_2_stat; // Toggle
LED_3_stat = 1-LED_3_stat; // Toggle
LED_1_Write(LED_1_stat);
LED_2_Write(LED_2_stat);
LED_3_Write(LED_3_stat);
Timer_1_ClearInterrupt(Timer_1_INTR_MASK_TC);
}
}
8. Main() 함수에서 Timer 를 시작하면, 다음처럼 주기적으로 LED 3개가 토글되어 깜박입니다.
main()
{
:
Timer_1_Start();
:
}
2015년 12월 28일 월요일
[PSOC] PSOC4 I2C I/F with AT24C04(EEPROM) - Read (3/3)
EEPROM posting 마지막인, AT24C04 READ 동작 테스트입니다.
AT24xx IC의 READ 동작 방식은 3가지가 있습니다.
1. RANDOM BYTE READ
2. SEQUENTIAL READ
3. CURRENT ADDRESS READ
이 중, 테스트 결과 미리 말씀드릴 내용이 있는데,
3. Current Address Read 의 경우 Byte Write 와 Random Byte Read 를 실행한 다음에는 Current Address 값이 읽히는 것이 아니라 Current Address 위치의 데이터 값이 읽힙니다.
제가 잘 못한 것이 있는지 모르겠으나 여러번 테스트 해 봐도, 그렇네요.
Page Write 나 Sequential Read를 수행한 후에는 Address가 제대로 읽히네요.
또 1가지, Page의 마지막 1 바이트를 Random Byte Read로 읽은 후에는 Current Address Read가 제대로 동작합니다.
정리해보면 Current Address Read 명령은 다음과 같은 경우 동작한다.
1. Page Write 이후
2. Sequential Read 이후
3. Page의 마지막 데이터를 Random Byte Read한 이후
추가로 테스트해 본 결과, 전원 리셋 이후 AT24C04를 처음에 Current Address Read를 수행하면 0xFF 가 읽히고 그 다음에 계속 읽어 보아도 0xFF만 계속 읽히는 군요. 데이터가 읽히는 건지 current Address 가 계속 0xFF 고 자동으로 증가한다던 Address 가 증가를 안하는 건지 확인이 안되네요.
[1. RANDOM BYTE READ]
RANDOM BYTE READ 함수는 다음과 같이 정의했습니다.
uint8_t EEP_24C04_Read_Byte(uint32_t slaveAddress,uint8_t wordAddress)
{
uint8_t rtn_val;
I2C_1_I2CMasterWriteBuf(slaveAddress, &wordAddress, 1, I2C_1_I2C_MODE_COMPLETE_XFER );
for(;;)
{
if(0u != (I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_WR_CMPLT))
{
break;
}
}
I2C_1_I2CMasterReadBuf(slaveAddress, &rtn_val, 1, I2C_1_I2C_MODE_COMPLETE_XFER);
for(;;)
{
if(0u != (I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_RD_CMPLT))
{
break;
}
}
return rtn_val;
}
데이터시트를 보면 Device Address 와 Word Address 를 Dummy Write 하라고 했는데, 이 동작에서 Dummy 라는 말이 문제가 있습니다. Dummy 는 쓰레기 또는 아무 의미 없는 동작을 말하는데 테스트 결과, Dummy로 넣은 Word Address 에 의해 Word Address 에 해당하는 데이터가 출력됩니다. 이것은 Dummy 라는 표현이 옳지 않은 것 같습니다.
어쨓든 Device Address(/Write) + Word Address 를 Write한 후에 I2C_1_I2CMasterReadBuf() 함수로 데이터를 1개 읽으면 됩니다.
동작 테스트 예로써, 다음과 같은 코드를 실행해 봤습니다.
i=0;
i2c_buffer[i++] = EEP_24C04_Read_Byte(0x52,0x14);
Device Address : 0x52 , Word Address : 0x14 에 해당하는 데이터를 1개 읽는 동작입니다.
오실로스코프로 측정한 결과는 다음과 같습니다.
0x04가 읽혔네요. 이건 맞습니다. 이전에 제가 Word Address 0x10~0x1F 에 데이터 0x00~0x0F를 Write 했으니, Word Address 0x14 에 해당하는 데이터는 0x04 가 맞는 거죠.
UART로 뿌려 보니 0x04가 나오는 군요.
[2. SEQUENTIAL READ]
SEQUENTIAL READ 동작은 RANDOM BYTE READ 에서 1 BYTE만 읽던 것에 추가로 여러바이트를 읽으면 됩니다.
Device Address(/Write) + Word Address 를 Write한 후에 I2C_1_I2CMasterReadBuf() 함수로 데이터를 여러개 읽으면 됩니다.
동작시퀀스는 다음과 같습니다.
제가 만든 함수 원형은 다음과 같습니다.
void EEP_24C04_Read_Sequential(uint32_t slaveAddress,uint8_t wordAddress,uint8_t *RdData,uint32_t cnt)
{
I2C_1_I2CMasterWriteBuf(slaveAddress, &wordAddress, 1, I2C_1_I2C_MODE_COMPLETE_XFER );
for(;;)
{
if(0u != (I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_WR_CMPLT))
{
break;
}
}
I2C_1_I2CMasterReadBuf(slaveAddress, (uint8 *) RdData, cnt, I2C_1_I2C_MODE_COMPLETE_XFER);
for(;;)
{
if(0u != (I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_RD_CMPLT))
{
break;
}
}
}
테스트 동작 코드의 예는 다음과 같습니다.
EEP_24C04_Read_Sequential(0x52,0x10,i2c_buffer,16);
Device Address : 0x52 , Word Address : 0x10부터 16개의 데이터를 순차적으로 읽는 동작입니다.
오실로 스코프로 측정한 결과는 다음과 같습니다.
안보여서 확대해 본 그림은 다음과 같습니다.
앞부분만 분석해 본 결과 제대로 송/수신이 되고 있네요.
0x52(Device Address)+/Write+ACK ,
0x10(Word Address)+ACK,
0x52(Device Address)+Read+ACK ,
0x00+ACK , 0x01+ACK , 0x02+ACK~..
UART로 출력한 결과 다음과 같습니다.
제대로 읽혀졌음이 확인됐습니다.
[3. Current Address Read]
현재 어드레스를 읽는 동작으로 Random Byte Read에서 Write Code(Device address+Wordaddress) 를 뺀 것 과 같습니다.
함수 정의는 다음과 같습니다.
uint8_t EEP_24C04_Read_Address(uint32_t slaveAddress)
{
uint8_t rtn_val;
I2C_1_I2CMasterReadBuf(slaveAddress, &rtn_val, 1, I2C_1_I2C_MODE_COMPLETE_XFER);
for(;;)
{
if(0u != (I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_RD_CMPLT))
{
break;
}
}
return rtn_val;
}
맨 처음 문제를 제기한 것 처럼, Page Write 나 Sequential Read 일 때만 제대로 동작합니다. 다른 의미로는 페이지의 마지막 데이터를 Access한 다음에만 동작한다고도 할 수 있겠네요.
동작 시퀀스는 다음과 같습니다.
오실로 스코프로 측정한 결과 입니다.
Word Address 0x21이 읽히네요.
다음은 UART로 출력한 결과 입니다. UART에는 0x20 이 읽히는데, 위의 스코프 데이터와 관련없이 보십시요. 위의 파형은 Current Address Read만 1번 더 실행해서 측정하다 보니 Address 가 자동으로 +1 되어 0x21이 되어 버렸습니다.
자, 이것으로 I2C 기초 공부를 마치겠습니다. 혹시 Current Address Read 의 애매한 부분을 아시면 댓글 남겨 주시기 바랍니다. ^^
그리고 PSOC Creator 예제 파일추가 링크 입니다.
2015년 12월 27일 일요일
[PSOC] PSOC4 I2C I/F with AT24C04(EEPROM) - Write (2/3)
AT24C04 의 Write 테스트시,
Write 는 2가지 방식이 있습니다.
1. Byte Write
1-Byte 단위로 특정 주소에 Write 하는 방식입니다.
2. Page Write
Page 단위(AT24C01,02 : 8 Byte, 04,08,16 : 16 Byte)로 특정 주소에 Write 하는 방식입니다. 16-Byte를 넘어서 쓰게되면 다음의 주소에 써지는 것이 아니라 맨 처음의 주소로 돌아가서 OverWrite 되니 주의하여야 합니다.
테스트 방법은 Write를 한 후, 다시 메모리를 읽어서 UART로 Terminal에 출력해서 확인해 봤습니다. 추가로 오실로 스코프로 세부 내용을 확인해 봤습니다.
PSOC Creator 에서 C로 프로그램을 할 때, 자동으로 만들어지는 I2C Master용 함수들을 이용해서 인터럽트에 의해 동작이 이루어 집니다.
인터럽트를 자세히 알 필요는 없었습니다. 그냥 UART 하듯이 간단하게 PSOC Creator에 의해 준비가 잘 되어 있어서 다른 MCU 다루기 보다는 너무 쉽더군요.
다만, 조금 헤맨 부분은 AT24Cxx IC에 Write 명령을 보내면 Write Cycle Time 이 있어서 5ms 동안 어떤 동작도 하지 않아서 Error가 발생한다는 점입니다. 이 때는 Error를 무시하고 주기적으로 상태를 확인하는 명령을 보내고 상태를 수신하거나 5ms를 기다렸다가 다음 동작을 수행하면 됩니다. 그런데 이 때, 상태 확인하는 레지스터같은 것이 AT24Cxx에는 없어서 확인하는 방법은 읽어서 값이 제대로 써졌는지 확인하는 방법밖에 없는듯 합니다.
저는 그냥 5ms 딜레이를 주고 다음 동작을 하도록 프로그래밍 했습니다.
요새 나오는 Flash Memory인 W25Q128 같은 경우는 Write Cycle Time에도 상태 확인하는 명령은 먹었는데, AT24xx IC는 하도 오래전에 나오고 초기에 만들어진 제품이라 그런지 명령이 몇개 없고 간단했습니다.
< 1. WRITE BYTE >
먼저 PSOC 에서 Write Byte 프로그램을 해 보겠습니다.
함수를 조합해서 1개 만들었는데, 별 거 없습니다.
void EEP_24C04_Write_Byte(uint32_t slaveAddress,uint8_t wordAddress,uint8_t WrData)
{
uint8_t i2c_Data[2];
i2c_Data[0] = wordAddress;
i2c_Data[1] = WrData;
I2C_1_I2CMasterWriteBuf(slaveAddress, (uint8 *) i2c_Data, 2, I2C_1_I2C_MODE_COMPLETE_XFER );
for(;;)
{
if(0u != (I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_WR_CMPLT))
{
break;
}
}
CyDelayUs(5000); // Write Cycle Time (5ms)
}
Write 는 2가지 방식이 있습니다.
1. Byte Write
1-Byte 단위로 특정 주소에 Write 하는 방식입니다.
2. Page Write
Page 단위(AT24C01,02 : 8 Byte, 04,08,16 : 16 Byte)로 특정 주소에 Write 하는 방식입니다. 16-Byte를 넘어서 쓰게되면 다음의 주소에 써지는 것이 아니라 맨 처음의 주소로 돌아가서 OverWrite 되니 주의하여야 합니다.
테스트 방법은 Write를 한 후, 다시 메모리를 읽어서 UART로 Terminal에 출력해서 확인해 봤습니다. 추가로 오실로 스코프로 세부 내용을 확인해 봤습니다.
PSOC Creator 에서 C로 프로그램을 할 때, 자동으로 만들어지는 I2C Master용 함수들을 이용해서 인터럽트에 의해 동작이 이루어 집니다.
인터럽트를 자세히 알 필요는 없었습니다. 그냥 UART 하듯이 간단하게 PSOC Creator에 의해 준비가 잘 되어 있어서 다른 MCU 다루기 보다는 너무 쉽더군요.
다만, 조금 헤맨 부분은 AT24Cxx IC에 Write 명령을 보내면 Write Cycle Time 이 있어서 5ms 동안 어떤 동작도 하지 않아서 Error가 발생한다는 점입니다. 이 때는 Error를 무시하고 주기적으로 상태를 확인하는 명령을 보내고 상태를 수신하거나 5ms를 기다렸다가 다음 동작을 수행하면 됩니다. 그런데 이 때, 상태 확인하는 레지스터같은 것이 AT24Cxx에는 없어서 확인하는 방법은 읽어서 값이 제대로 써졌는지 확인하는 방법밖에 없는듯 합니다.
저는 그냥 5ms 딜레이를 주고 다음 동작을 하도록 프로그래밍 했습니다.
요새 나오는 Flash Memory인 W25Q128 같은 경우는 Write Cycle Time에도 상태 확인하는 명령은 먹었는데, AT24xx IC는 하도 오래전에 나오고 초기에 만들어진 제품이라 그런지 명령이 몇개 없고 간단했습니다.
< 1. WRITE BYTE >
먼저 PSOC 에서 Write Byte 프로그램을 해 보겠습니다.
함수를 조합해서 1개 만들었는데, 별 거 없습니다.
void EEP_24C04_Write_Byte(uint32_t slaveAddress,uint8_t wordAddress,uint8_t WrData)
{
uint8_t i2c_Data[2];
i2c_Data[0] = wordAddress;
i2c_Data[1] = WrData;
I2C_1_I2CMasterWriteBuf(slaveAddress, (uint8 *) i2c_Data, 2, I2C_1_I2C_MODE_COMPLETE_XFER );
for(;;)
{
if(0u != (I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_WR_CMPLT))
{
break;
}
}
CyDelayUs(5000); // Write Cycle Time (5ms)
}
PSOC에서 기본으로 제공되는 함수인 I2C_1_I2CMasterWriteBuf 로 AT24C04 의 Device address 를 slaveAddress 에 넣어 주고 wordaddress 와 Write할 데이터(1-Byte)를 순서대로 I2C_data 에 넣어주고, 마지막은 mode 인데 전송시 START,STOP 파형을 결정하는 옵션입니다. 이 IC에서는 I2C_1_I2C_MODE_COMPLETE_XFER 외에는 쓸 일이 없더군요.
모드 설명은 자동으로 생성되는 (component 이름이 I2C_1 일 때) I2C_1_I2C.h 에 정의되어 있습니다.
/* "Mode" constants for MasterWriteBuf() or MasterReadBuf() function */
#define I2C_1_I2C_MODE_COMPLETE_XFER (0x00u) /* Full transfer with Start and Stop */
#define I2C_1_I2C_MODE_REPEAT_START (0x01u) /* Begin with a ReStart instead of a Start */
#define I2C_1_I2C_MODE_NO_STOP (0x02u) /* Complete the transfer without a Stop */
Device Address 를 0x52(기본 세팅은 0x50 인데, Address Select 기능 테스트를 위해 A1을 High로 하여 0x52로 바꿨음)설정하고, Word Address 0x1F 에 데이터 1 Byte 인 0x3A를 쓰는 예는 다음과 같습니다.
EEP_24C04_Write_Byte(0x52,0x1F,0x3A);
AT24xx 데이터 시트에서 Byte Write 펄스 시퀀스는 다음과 같습니다.
오실로 스코프로 측정한 파형은 다음과 같습니다. 이런..ㅜㅜ 아래 그림은 0x10에 0xBB를 Write 한 것이네요. 바쁜 관계로 그림 편집없이 제대로 된 파형만 추가하겠습니다.
노란색이 SCL 이고 빨강색이 SDA인데 출력 결과가 데이터 시트와 일치합니다.
아직 I2C Read 설명을 안했는데, 이미 만들어 놓은 Read 명령으로 읽어서 UART로 뿌린 결과 제대로 써졌음을 확인했습니다.
< 2. WRITE PAGE >
다음은 Page 단위(AT24C04 에서 부터는 16-Byte)로 Write 하는 방법을 알아보겠습니다.
이건 간단합니다. 위 Write Byte 에서 Word Address 이후에 1-Byte만 보내던 것을, 연속으로 16-Byte 데이터를 보내면 됩니다.
Page Write 함수도 1개 만들어 보았는데,
입력변수로 배열 변수를 받고,
몇 개 보낼 것인지 cnt를 전달해 주는 것이
Byte Write 함수와의 차이입니다.
먼저 전달할 1-Byte(uint8_t) 타입의 입력 배열 값에 쓰고자 하는 값을 넣고 이 함수에 전달해 주면 I2C 통신에 의해 Page Write 동작이 실행됩니다.
함수 정의는 다음과 같습니다.
void EEP_24C04_Write_Page(uint32_t slaveAddress,uint8_t wordAddress,uint8_t *WrData,uint32_t cnt)
{
uint8_t i2c_Data[20];
uint32_t i;
i2c_Data[0] = wordAddress; // 1st data : WordAddress (8-bit : 0x00~0xFF)
for (i=0;i
{
i2c_Data[1+i] = WrData[i]; // 2nd~17 data(16 byte data) : page write data[0]~[15]
}
I2C_1_I2CMasterWriteBuf(slaveAddress, (uint8 *) i2c_Data, (cnt+1), I2C_1_I2C_MODE_COMPLETE_XFER );
for(;;)
{
if(0u != (I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_WR_CMPLT))
{
break;
}
}
CyDelayUs(5000); // Write Cycle Time (5ms)
}
실제 코드에 적용한 예는 다음과 같습니다.
for (j=0,i=0x00;j<20 i="" j="" p=""> {
i2c_buffer[j] = i;
}
EEP_24C04_Write_Page(0x52,0x10,i2c_buffer,16);
배열 변수인 i2c_buffer[j]에 수차적으로 0x00~0x0F 를 넣고,
device Address = 0x52, word Address = 0x10, cnt = 16 파라메터를 넣어 EEP_24C04_Write_Page() 함수를 동작 시키면 Address 0x10 부터 0x1F 에 0x00,0x01~0x0F 형태로 데이터가 써(Write)집니다.
이 때, 16개 이상 Write 하면 17번째 부터 WordAddress 0x10 에 OverWrite 되니 주의하시기 바랍니다.
아까 설명을 안한 부분이 있는데, Write시 I2C_1_I2CMasterWriteBuf() 함수가 실행되면 여기서 데이터를 다 쓰는 것이 아니라, Tx 버퍼에 넣고 인터럽트에 의해 Write가 동작하는 중이라 if(0u != (I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_WR_CMPLT)) 코드에 의해 송신이 완료될 때까지 기다리는 것입니다.
여기는 Master 인 PSOC4에서 데이터만 다 보낸 것이고, AT24Cxx 에서 Write cycle 타임이 있어 EEPROM에 다 써질 때 까지 기다려야 합니다. 이 때는 아무 동작을 안합니다. 메뉴얼을 보면 5ms 라고 나와 있어서 5ms 기다리고 끝냅니다.
CyDelayUs(5000); // Write Cycle Time (5ms)
다음은 Datasheet에 있는 Page Write 동작 시퀀스 입니다.
다음은 위의 코드에 의해 동작시 I2C 통신라인을 오실로 스코프로 측정한 결과 입니다.
안보여서 좀 더 확대해 보면 다음과 같습니다.
데이터가 제대로 나가고 있네요. ^^
실제로 잘 써 졌는지 읽어서 UART로 출력해 보면 다음과 같습니다.
^^ 잘 써졌네요. 동작 검증 끝.
20>
다음은 Page 단위(AT24C04 에서 부터는 16-Byte)로 Write 하는 방법을 알아보겠습니다.
이건 간단합니다. 위 Write Byte 에서 Word Address 이후에 1-Byte만 보내던 것을, 연속으로 16-Byte 데이터를 보내면 됩니다.
Page Write 함수도 1개 만들어 보았는데,
입력변수로 배열 변수를 받고,
몇 개 보낼 것인지 cnt를 전달해 주는 것이
Byte Write 함수와의 차이입니다.
먼저 전달할 1-Byte(uint8_t) 타입의 입력 배열 값에 쓰고자 하는 값을 넣고 이 함수에 전달해 주면 I2C 통신에 의해 Page Write 동작이 실행됩니다.
함수 정의는 다음과 같습니다.
void EEP_24C04_Write_Page(uint32_t slaveAddress,uint8_t wordAddress,uint8_t *WrData,uint32_t cnt)
{
uint8_t i2c_Data[20];
uint32_t i;
i2c_Data[0] = wordAddress; // 1st data : WordAddress (8-bit : 0x00~0xFF)
for (i=0;i
{
i2c_Data[1+i] = WrData[i]; // 2nd~17 data(16 byte data) : page write data[0]~[15]
}
I2C_1_I2CMasterWriteBuf(slaveAddress, (uint8 *) i2c_Data, (cnt+1), I2C_1_I2C_MODE_COMPLETE_XFER );
for(;;)
{
if(0u != (I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_WR_CMPLT))
{
break;
}
}
CyDelayUs(5000); // Write Cycle Time (5ms)
}
실제 코드에 적용한 예는 다음과 같습니다.
for (j=0,i=0x00;j<20 i="" j="" p=""> {
i2c_buffer[j] = i;
}
EEP_24C04_Write_Page(0x52,0x10,i2c_buffer,16);
device Address = 0x52, word Address = 0x10, cnt = 16 파라메터를 넣어 EEP_24C04_Write_Page() 함수를 동작 시키면 Address 0x10 부터 0x1F 에 0x00,0x01~0x0F 형태로 데이터가 써(Write)집니다.
이 때, 16개 이상 Write 하면 17번째 부터 WordAddress 0x10 에 OverWrite 되니 주의하시기 바랍니다.
아까 설명을 안한 부분이 있는데, Write시 I2C_1_I2CMasterWriteBuf() 함수가 실행되면 여기서 데이터를 다 쓰는 것이 아니라, Tx 버퍼에 넣고 인터럽트에 의해 Write가 동작하는 중이라 if(0u != (I2C_1_I2CMasterStatus() & I2C_1_I2C_MSTAT_WR_CMPLT)) 코드에 의해 송신이 완료될 때까지 기다리는 것입니다.
여기는 Master 인 PSOC4에서 데이터만 다 보낸 것이고, AT24Cxx 에서 Write cycle 타임이 있어 EEPROM에 다 써질 때 까지 기다려야 합니다. 이 때는 아무 동작을 안합니다. 메뉴얼을 보면 5ms 라고 나와 있어서 5ms 기다리고 끝냅니다.
CyDelayUs(5000); // Write Cycle Time (5ms)
다음은 Datasheet에 있는 Page Write 동작 시퀀스 입니다.
다음은 위의 코드에 의해 동작시 I2C 통신라인을 오실로 스코프로 측정한 결과 입니다.
안보여서 좀 더 확대해 보면 다음과 같습니다.
데이터가 제대로 나가고 있네요. ^^
실제로 잘 써 졌는지 읽어서 UART로 출력해 보면 다음과 같습니다.
^^ 잘 써졌네요. 동작 검증 끝.
20>
Psoc creator Source 파일은 마지막 [PSOC] PSOC4 I2C I/F with AT24C04(EEPROM) - Read (3/3) 에 올려 놓을 예정입니다.
피드 구독하기:
글 (Atom)





















































