페이지

글목록

레이블이 Interrupt인 게시물을 표시합니다. 모든 게시물 표시
레이블이 Interrupt인 게시물을 표시합니다. 모든 게시물 표시

2016년 11월 30일 수요일

[STM32F4xx] Timer Clock source

메뉴얼에서는 못 찾았는데, 구글 검색해 보니 잘 나와 있네요.

이 내용의 원문은 다음과 같습니다.

아무리 메뉴얼을 뒤져봐도 못 찾았던 내용인데, 이 사람은 어디서 찾은 걸까요?
제가 꼭 알고 싶었던 내용입니다. 
타이머를 쓰긴 쓰는데, Clock 소스가 180 MHz 인지, 90MHz 인지, 45MHz 인지 통 모르겠드만...
그래서 오실로 스코프로 찍어보고 알아야만 했거든요. ㅜㅜ

맨 마지막에 APB 번호가 나와 있어서 CubeMX 툴의 Clock Configuration 과 비교해 보면 알 수 있습니다.



TIM2 는 이전에 해봐서 90MHz가 Clock Source 라는 것을 알았고, 만약 TIM1 으로 테스트 하면 Clock Source가 180MHz 겠군요.

클럭은 Cube Mx로 다음과 같이 설정하고,




소스 코드는 아래와 같이 TIM1 Update 인터럽트가 걸릴때 마다 PA5를 토글하도록 만들어 봤습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
 if (htim->Instance == TIM1)
 {
  GPIOA->ODR ^= GPIO_PIN_8;
 }
}
 
int main(void)
{
  HAL_Init();
 
  SystemClock_Config();
 
  MX_GPIO_Init();
  MX_TIM1_Init();
 
  HAL_TIM_Base_Start_IT(&htim1);
 
  while (1)
  {
  }
}
cs


그 결과 PA5 를 오실로 스코프로 찍어 보니, 위의 표와 계산한 값과 같이 10ms 마다 토글하도록 출력이 됩니다.


2016년 11월 5일 토요일

[STM32F4xx] Timer Clock source

메뉴얼에서는 못 찾았는데, 구글 검색해 보니 잘 나와 있네요.

이 내용의 원문은 다음과 같습니다.
https://stm32f4-discovery.net/2014/05/stm32f4-stm32f429-discovery-pwm-tutorial/

아무리 메뉴얼을 뒤져봐도 못 찾았던 내용인데, 이 사람은 어디서 찾은 걸까요?
제가 꼭 알고 싶었던 내용입니다. 
타이머를 쓰긴 쓰는데, Clock 소스가 180 MHz 인지, 90MHz 인지, 45MHz 인지 통 모르겠드만...
그래서 오실로 스코프로 찍어보고 알아야만 했거든요. ㅜㅜ

맨 마지막에 APB 번호가 나와 있어서 CubeMX 툴의 Clock Configuration 과 비교해 보면 알 수 있습니다.



TIM2 는 이전에 해봐서 90MHz가 Clock Source 라는 것을 알았고, 만약 TIM1 으로 테스트 하면 Clock Source가 180MHz 겠군요.

클럭은 Cube Mx로 다음과 같이 설정하고,





소스 코드는 아래와 같이 TIM1 Update 인터럽트가 걸릴때 마다 PA5를 토글하도록 만들어 봤습니다.

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == TIM1)
{
GPIOA->ODR ^= GPIO_PIN_8;
}
}

int main(void)
{
  HAL_Init();

  SystemClock_Config();

  MX_GPIO_Init();
  MX_TIM1_Init();

  HAL_TIM_Base_Start_IT(&htim1);

  while (1)
  {
  }

}

그 결과 PA5 를 오실로 스코프로 찍어 보니, 위의 표와 계산한 값과 같이 10ms 마다 토글하도록 출력이 됩니다.

2016년 11월 3일 목요일

[STM32F4xx] Nucleo 보드 테스트 #18 (TIM2 Update INT : KEIL)

제가 지금까지 타이머 인터럽트를 안다뤘더군요. 
가장 많이 쓰는 기능일텐데, 이제서야 여러 기능들을 합쳐서 프로그램을 짜 넣으려고 하다 보니 빠진 것을 알았습니다.

먼저 CubeMx 툴을 실행.
TIM2 의 Clock Source 를 Internal Clock으로 하여 기능을 살려 놓습니다. 
그리고 TIM2 Update 인터럽트가 제대로 걸리고 있는지 확인을 위해 포트를 토글하려고 PA5를 GPIOOUT으로 설정.


다음은 클럭 설정으로 가서 STM32F446의 시스템 클럭을 180MHz로 가장 높게 맞췄습니다.
참고로 메뉴얼을 봐도 TIM2 의 클럭 소스가 어떤 것인지 몰라서 직접 실험으로 알아본 결과 APB1 Timer Clock(90MHz) 네요.
누가 혹시 이 내용에 대해 자세히 나온 글이 있는지 아시면 알려 주시면 감사하겠습니다. (전 도저히 못 찾겠네요)



configuration 탭으로 가서 TIM2 의 세부 설정을 위해 TIM2를 클릭.


10ms 마다 TIM2 Update 인터럽트가 걸리게 하기 위해, Parameter 설정 탭으로 가서 Period 를 900,000 으로 설정했습니다.
공식은 period count = 0.01(sec) x 90Mhz = 900,000 입니다.


인터럽트를 쓸 것이기 때문에 NVIC Setting으로 가서 인터럽트를 인에이블 체크를 합니다.


메뉴 중, project->Generate Code 를 눌러 컴파일러에 맞게 코드를 생성합니다.



1. HAL_TIM_Base_Start_IT 함수로 타이머 업데이트 인터럽트가 걸리도록 합니다.
2. stm32f4xx_hal_tim.c 의 __weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) 함수에서 
--weak 를 제거하고 main.c 에 옮겨서 TIM2 update 인터럽트가 걸릴때 실행될 코드를 추가하거나 수정합니다.
TIM2 update 인터럽트가 걸릴때 HAL_TIM_PeriodElapsedCallback 함수를 호출하도록 되어 있지요.



그런데 주의할 점은 어떤 Timer 인터럽트가 실행되도 호출되는 callback 함수는 HAL_TIM_PeriodElapsedCallback 입니다.
따라서, HAL_TIM_PeriodElapsedCallback 함수 내에서 어떤 타이머에서 날 불렀는지 확인할 필요가 있습니다.
그래서 다음과 같이 확인하는 코드가 필요합니다.





즉 날 호출한 게, TIM1 이냐? TIM2 냐? 이런 뜻이죠. 
저는 TIM2에 의해 호출되도록 만들었기 때문에, TIM2에 의해 호출되면 A5 핀을 토글하도록 해서 결과는 다음과 같습니다.





2016년 8월 8일 월요일

[STM32F4xx] Nucleo 보드 테스트 #6(I2C 2/2 인터럽트:KEIL)

I2C 통신으로 간단하게 EEPROM(AT24C04) 를 읽기/쓰기 테스트 해 봤습니다.

예전에 PSOC4로 해 놓은 자료가 있으니까, 자세한 설명은 빼고 동작 테스트만 해 보겠습니다.

먼저 I2C 인터럽트를 사용하여 데이터를 보내고 받는 함수들은 다음과 같습니다. 
1. HAL_I2C_Master_Transmit_IT()
   : I2C 인터럽트를 사용하여 데이터를 보내는(Write) 함수
2. HAL_I2C_MasterTxCpltCallback()
   : I2C 인터럽트를 사용하여 데이터를 보내고 다 보내면 인터럽트에 의해 호출되는 함수
3. HAL_I2C_Master_Receive_IT()
   : I2C 인터럽트를 사용하여 데이터를 받는(Receive) 함수
4. HAL_I2C_MasterRxCpltCallback()
   : I2C 인터럽트를 사용하여 정해진 개수의 데이터를 다 받으면 인터럽트에 의해 호출되는 함수

위의 함수들은 stm32f4xx_hal_i2c.c 파일에 정의되어 있습니다. 
HAL_I2C_MasterTxCpltCallback(), HAL_I2C_MasterRxCpltCallback() 과 같은 콜백함수는 __weak 키워드를 없애고
main.c 파일에 복사해서 내부 내용을 바꿔서 쓰면 인터럽트가 걸릴때 마다 콜벡함수를 호출하게 됩니다.

이 파일의 위치는 다음과 같습니다.



친구 돌잔치를 가봐야 해서 main.c 에서 EEPROM 읽고 쓰는 부분을 그림으로 올려 놓겠습니다.


[16 Byte Write 오실로 스코프 파형]

[Word Address Write 오실로 스코프 파형]

[16 Byte Read 오실로 스코프 파형]

소스 파일 첨부.

2016년 8월 4일 목요일

[STM32F4xx] Nucleo 보드 테스트 #4(UART2:KEIL)

이번에는, 이전 소스 코드에, UART2 인터럽트 처리 함수를 추가해 봤습니다.

STM32F4xx 의 UART 데이터 처림 방식에는 3가지가 있습니다.
1. Polling mode
 보내고 받는 동작에서 다른 일을 못하고 상태를 지켜 보고 있어야 해서 효율이 떨어진다.
2. Interrupt mode
 보내고 받는 동작이 완료되면, 인터럽트 함수로 점프해서 특정 동작을 처리하고 돌아온다.
보낼 경우는 몇개의 데이터를 보내는지 알아서 딜레이를 주던가, 주기적으로 상태를 체크하면서 동작 시키면 되는데,
받을 경우에는 가변 길이의 프로토콜일 경우 1개의 데이터가 받아질 때마다 인터럽트 처리를 해야 하는데 
데이터 전송률이 높을 수록, 또 수신 데이터가 많아질 수록 함수를 자주 호출하므로 다른일을 못하게 되어 효율이 떨어진다.
3. DMA mode
가장 효율이 좋은 방식으로, 인터럽트 처리 함수를 호출할 필요가 없이 메모리에 직접 데이터가 받아지므로,
주기적으로 받은 데이터를 링버퍼에 쌓아서 처리하면 된다.
외국에서 DMA방식을 써서 많은 잇점이 있다고 하는 글을 첨부해 봅니다.
DMA allows the UART to store data in memory WITHOUT interrupting the processor until the buffer is full saving lots of overhead.
In my case, I told PDC(Peripheral DMA controller) to store UART data into an buffer (byte array) and specified the length. When UART via PDC filled the buffer the PDC issued an interrupt.
In PDC ISR:
  1. Give PDC new empty buffer
  2. Restart UART PDC (so can collect data while we do other stuff in isr)
  3. memcpy full buffer into RINGBUFFER
  4. Exit ISR
As swineone recommended above, implement DMA and you'll love life.

저는 DMA는 링버퍼 구현 때문에 시간이 좀 걸릴 것 같아서, 
우선은 다른 구현해야할 동작을 먼저 하고 일단은 RX 인터럽트만 동작을 시켜봤습니다.

이전에 Cube툴에서 UART2 인터럽트를 설정해 놓고 코드를 생성했기 때문에,
이전 글에서 첨부한 소스코드에 Rx 인터럽트 처리함수만 추가해서 1바이트 받으면 바로 TX로 뿌려주는 동작만 구현했습니다.

타이머 인터럽트 처리함수처럼 UART인터럽트도 사용자 콜백함수가 있었습니다.
이 함수 역시도 __weak 키워드가 붙어 있고 이것을 사용하려면 사용할 사용자 파일에 복사해서 붙여넣기를 해서
__weak 키워드를 지워주고 함수 안의 내용을 입맛에 맞게 고쳐주면 됩니다.

처음에 할 일은, 설정은 이미 자동으로 코드가 생성되어 완료가 됐고 
1. "내가 uart2 포트(huart2)로 인터럽트 방식을 사용해서 특정 변수에(uart2_rx_ch) 1바이트를 받겠다" 라는 명령을 실행시킵니다.
 HAL_UART_Receive_IT(&huart2,&uart2_rx_ch,1);

2. 위의 코드가 실행되면 uart2로 1바이트의 데이터가 수신될 때마다 인터럽트가 걸리는데,
이 때마다 위의 설명에서 말씀드린 사용자 콜백함수로 점프해서 해당 코드를 실행하고 복귀합니다.
아까 말씀 드린 stm324xx_hal.c 파일에 있던 __weak 키워드가 붙어있는 사용자함수를 main.c 파일에 붙여 넣고 함수 내부 코드를 수정한 함수는 다음과 같습니다.



코드의 내용은 "받은 데이터를 폴링방식으로 TX로 출력하고, 다시 인터럽트로 1바이트 받겠다" 입니다.
일종의 echo 동작이죠.
DMA 까지 필요없는 분들은 이 함수를 수정해서 링버퍼에 쌓아서 쓰시던가 그냥 입맛에 맞게 적당히 고쳐서 쓰시면 되겠습니다.

테스트 결과는 다음과 같습니다.

이상이고, 소스 파일 첨부해 놓겠습니다.

2016년 2월 27일 토요일

[PSOC] PSOC4 GPIO Interrupt Component SCH

이번에는 PSOC4의 GPIO 인터럽트 테스트를 해봤습니다.

PSOC4 GPIO Interrupt 는 핀 내에 Fixed 된 인터럽트가 3가지 있습니다.
1. Rising Edge
2. Falling Edge
3. Both Edge

즉 Edge 트리거만 지원하는군요.

포트 에서 인터럽트 타입을 선택하면 GPIO 콤포넌트에 irq 출력 핀이 1개 나온다.

이 핀에 인터럽트 콤포넌트를 붙여 줘야하고, 인터럽트 콤포넌트에서 다시 다음의 3가지 타입의 입력을 선택한다.
1. DERIVED : 연결된 콤포넌트의 인터럽트 방식은 그대로 가져다 쓴다.
2. RISING EDGE : 연결된 콤포넌트의 인터럽트 펄스 출력의 RIGING EDGE 에서 인터럽트 발생.
3. LEVEL : 연결된 콤포넌트의 HIGH LEVEL 에서 인터럽트가 반복적으로 걸림


여기까지 진행해야 인터럽트 동작 준비가 끝난다.

이번 회사 프로젝트에서는 Level 트리거가 필요한데, 인터럽트를 1개 더 추가해야했다.


현재까지 다른 방법을 찾지 못했네요. 혹시 이 방법 외에 LEVEL 트리거를 할 수 있는 방법 아시는분~ 손?
LEVEL 트리거 인터럽트는 무조건 HIGH LEVEL 에서만 걸리므로 LOW LEVEL 에서 걸리도록 하려면,
UDB 소자인 INVERTER(NOT) 를 1개 추가해야 한다.

2016년 1월 5일 화요일

[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);

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();
     :
}




9 이 프로그램의 압축된 파일은 다음 링크에 올려놓습니다.