페이지

글목록

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

2016년 10월 23일 일요일

[STM32F4xx] Nucleo 보드 테스트 #16 (mem to GPIO DMA:KEIL)

이번에는 memory to GPIO 를 DMA로 출력하는 테스트를 할 예정입니다.

타이머 1개를 트리거 신호로 사용하여 65535개까지 연속으로 데이터를 출력할 수 있으며,
속도는 10MHz 내외로 나옵니다.

생각보다 빠르지 않습니다만, 데이터 버스로 사용한다면 80Mbit/s 의 속도가 나오므로 꽤 나오죠?
저는 동시에 여러개의 SPI 버스의 데이터를 송신하는 용도로 사용하려고 테스트를 진행중입니다.
(예전에 테스트를 어느정도 해 놓고, 정리 차원에서 카페에 글을 올리는 중입니다.)

예전에 테스트한 보드는 Nucleo STM32F411 보드에서 했었고,
이번에는 Nucleo STM32F446 보드에서 테스트할 예정입니다.

참고로 한 내용은 st 홈페이지의 AN4666 입니다.

예제 프로그램을 위의 링크된 사이트에서 다운 받을 수 있는데, 회원을 가입해야 했던 것 같습니다.

STM32F476G Discovery 보드의 예제인데, TIM2 의 CH1을 DMA 트리거 소스로 프로그램 되어 있습니다.
하지만 STM32F411,STM32F446 에서는 TIM2/CH1 을 DMA 소스로 했을 때 트리거 신호가 나오지 않는 문제가 발생했습니다.
아무리 해도 안되서 TIM1/CH1 을 트리거 소스로 사용하니 트리거 신호가 동작했습니다.
원인은 모르겠으나, TIM1 과 TIM2 의 큰 차이는 Repetition Count 가 있고 없고의 차이인데 확실히 트리거 신호가 안나오는 이유는 모르겠지만, TIM1/CH1 은 동작합니다. (원인을 알고 계시면 알려 주시면 감사하겠습니다)

다음은, STM32CubeMX 툴로 프로젝트를 만드는 과정을 그림으로 올려 보겠습니다.

먼저 Pinout 탭에서 사용할 기능을 설정합니다.
NuCleo 보드에서 클럭을 8MHz Bypass 틀락과 32.768 KHz 크리스탈을 사용하므로 RCC를 그에 맞게 설정하고,
트리거 소스로 사용할 TIM1 의 Clock Source 를 Internal Clock, 출력 채널을 PWM Generation CH1 으로 설정합니다.
이 때, GPIO 출력으로 사용할 포트는 CubeMX 툴에 표시되지 않습니다.


실제 Nucleo 보드의 출력핀은 다음과 같습니다.

다음은 큐브 툴에서 Clock을 설정합니다. 입력 클럭 8MHz,32.768 KHz 클럭을 소스로 시스템 클럭은 STM32F446 의 최대 클럭인 180MHz 로 설정을 했습니다. 180MHz 를 쓰고 엔터키만 치만 나머지를 자동으로 맞춰주니 골치아파할 필요가 없습니다.


다음은 TIM1 주기와 듀티 및 DMA 설정을 위해서 CubeMX 툴의 configuration 탭을 선택하고 TIM1 을 클릭합니다.

parameter Setting 에서 제 경험상 가장 빠른 출력 주기인 7로 주기를 설정하고, High 로 유지할 펄스는 2로 설정합니다.
주기를 더 빠르게 하면 트리거 출력이 나오다 없어지거나 처음부터 안나옵니다.

다음은 DMA Setting 입니다.
수정할 부분은 Direction 을 Memory to Peripheral 로 고치고, 저는 Data Width를 Byte로 해서 출력 하려고 Byte로 설정했습니다.

이렇게 하고 나서 Keil compiller 프로젝트 형식으로 소스 코드를 만들기 위해 project/Setting 메뉴에서 Toolchain / IDE 옵션을 MDK-ARM V5 로 선택합니다.

그런 다음, project/Generate Code 를 클릭하면 Keil 프로젝트가 만들어 집니다.

main.c 의 main() 함수에 추가한 내용은 아래 붉은색 네모의 내용이 다 입니다.

그런데 가장 중요한 부분은 stm32f4xx_it.c 에 있는 DMA IRQHandler 함수에 TIM1 을 멈추게 하는 코드를 넣어야 한다는 점입니다.
저는 예제를 받아서 수정을 한 것이 아니고, 참고로 해서 CubeMX 툴에서 다시 만들었기 때문에 main.c 파일만 보고 수정된 부분을 비교해서 수정을 했기 때문에 stm32f4xx_it.c 파일을 수정해야 한다는 생각을 못하고 프로그램을 실행하니 아무리 해도 동작이 안되었습니다. 원인은 이 코드를 수정하지 않아서 트리거 클락이 끝없이 나가는 것이 문제였습니다.

그리고 앞뒤고 트리거 클럭은 더미 클럭이 나가니, GPIO를 사용할 경우 주의하시기 바랍니다. SPI로 동작 시킬 경우에는 데이터에 /CS 데이터를 실어서 출력하면 더미데이터를 신경 안 쓰고 사용할 수 있을 것 같습니다.
더미 데이터는 클럭이 빠를수록 더 많이 나옵니다.

다음은 첨부된 프로젝트 파일의 실행 결과 입니다.




2016년 8월 11일 목요일

[STM32F4xx] Nucleo 보드 테스트 #11(GPIO 병렬 출력:KEIL)

이번에는 GPIO에 여러핀을 동시에 출력하는 방법을 알아보겠습니다.

핀 설정도 동시에 할 순 있는데, 좀 귀찮아서 핀설정은 각 핀마다 했습니다.

이제 제가 프로젝트를 시작해야 해서, 프로젝트에서 사용해야 할 핀들을 다 포함시킨 상태에서
GPIO 병렬 포트로 사용할 핀을 지정을 해 봤습니다.

GPIO 설정은 Cube 툴에서 할 수 없으니까, 핀이 표시가 되지 않아서 따로 사용할 핀을 정리해 봤습니다.

그리고 현재 사용한 Nucleo 보드에서 핀헤더의 위치도 표시해 봤습니다.


자.. 그럼 핀 설정은 정리해 보자면,
DATA[7:2] : PA15,14,13,12,11,10
DATA[1:0] : PC7,6
SYNC CLK : PB5

핀 설정 코드는 1핀마다 설정을 했더니 너무 길어서 소스코드 첨부해 놓을 테니 참고하시기 바람니다.
main.c 파일 안에 static void MX_GPIO_Init(void) 함수 보시면 되겠습니다.

이렇게 설정을 했습니다. 간단히 설명을 해 보자면, SYNC CLK 의 Rising Edge 마다 8비트 데이터를 출력 하는 테스트 입니다.
8비트 병렬 통신을 구현할 생각이고, STM 칩으로 8비트 데이터를 보내고 SYNC 출력을 1번 내보내는 겁니다.

8번 출력(0x01->0x02->0x04->0x08->0x10->0x20->0x40->0x80) 을 반복해서 내보내도록 했습니다.

다음은 main.c 의 main()함수의 주요 동작 코드입니다.

  for (i=0;i<8 8="" i="" span="">
  {
par_data[i] = (1&lt;&lt; i);
      // (0x01-&gt;0x02-&gt;0x04-&gt;0x08-&gt;0x10-&gt;0x20-&gt;0x40-&gt;0x80) 데이터 생성
  }

  while (1)
  {
par_ReAssemble(par_data,8);
       // (0x01-&gt;0x02-&gt;0x04-&gt;0x08-&gt;0x10-&gt;0x20-&gt;0x40-&gt;0x80) 데이터 출력 + SYNC_CLK 출력
HAL_Delay(1);
  }



현재 핀을 여러용도록 사용했더니, 출력할 포트가 2개로 나뉘어 버려서 8비트 데이터를 재정렬하도록 프로그램을 만들었습니다.
재정렬 함수의 코드는 다음과 같습니다.
void par_ReAssemble(uint8_t *par_data,uint16_t cnt_data)
{
uint16_t i;
for (i=0;i
{
// DATA[7:2] : PA15,14,13,12,11,10
GPIOA-&gt;ODR = (((uint32_t)par_data[i]) &lt;&lt; 8)&amp;0xFC00;
// DATA[1:0] : PC7,6
GPIOC-&gt;ODR = (((uint32_t)par_data[i]) &lt;&lt; 6)&amp;0x00C0;
//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_SET);
//HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_RESET);
GPIOB-&gt;BSRR = GPIO_PIN_5;
GPIOB-&gt;BSRR = (GPIO_PIN_5&lt;<16 16="" div="">
}
}

위의 함수에서 GPIOA-&gt;ODR 이 보이시죠?
이 레지스터가 각각 16비트폭으로서, 병렬 포트를 출력하도록 하는 레지스터입니다.
쉬프트 연산과 마스크 연산을 해서 병렬포트 출력 레지스터에 값을 쓰면 해단 핀들로 0또는 1이 출력 됩니다.

1비트 출력은 2가지 방법이 있습니다.
1. HAL 드라이버에서 제공하는 함수를 사용.
HAL_GPIO_WritePin(GPIOB, GPIO_PIN_5,GPIO_PIN_SET);
2. 직접 레지스터에 쓰기.
GPIOB-&gt;BSRR = GPIO_PIN_5;
GPIOB-&gt;BSRR = (GPIO_PIN_5&lt;<16 16="" div="">

HAL_GPIO_WritePin 함수는 누구나 인식하기 쉬워서 보면 아시겠고,
BSRR 레지스터는 32비트 구조인데, 1을 해당 비트에 써주면 하위 16비트는 SET, 상위 16비트는 RESET 기능을 합니다.

위에서 2가지 방법이 있다고 했는데, 사실은 HAL.. 함수에서 BSRR 레지스터를 사용하고 있어서 본래는 1가지 방법입니다.
그런데 제가 왜 레지스터에 집접 썼는가 하면 HAL.. 함수가 너무 느려서 입니다. 
테스트를 해 본 결과, 레지스터를 사용한 방법보다 2배나 느리더군요.

다음은 같은 동작을 수행했을 때, 왼쪽이 HAL 함수, 오른쪽이 레지스터에 직접 쓴 방법을 사용한 결과 입니다.

다음은 8비트로 1바이트를 보냈을 때, 직접 속도를 계산해 봤습니다. 


나중에 설명을 하려고 했는데, 먼저 해 버렸네요.
이게 제가 알려드리고자 한 핵심이고 앞으로 설명할 나머지는 크게 중요하진 않습니다.

사용자의 편의성이냐? 속도냐? 둘 중 하나를 선택하시면 됩니다.

그런데.. 저 파형이 링잉이 발생하는게 거슬리네요. 파형이 원래 그런것인지 오실로 스코프가 문제인지.. 궁금하네요. 내일 오실로 스코프 바꿔서 테스트 해 봐야겠습니다.


다음은 HAL 함수를 사용해서 프로그램을 돌려 본 결과 입니다.
검증을 위해 4채널 오실로 스코프를 이용해서 1로 예상되는 포인트만 찍었습니다. 9개의 신호를 한번에 찍을 수가 없어서 3개의 프로브를 나눠서 측정을 했습니다.

[ SYNC_CLK 와 DATA 0,1,2 ]

[ SYNC_CLK 와 DATA 2,3,4 ]

[ SYNC_CLK 와 DATA 5,6,7 ]


다행이 예상대로 잘 나왔네요.

그럼 이만 마치겠습니다.

소스 코드 첨부 합니다.

2016년 8월 2일 화요일

[STM32F4xx] Nucleo 보드 테스트 (GPIO 출력)

이번에는 GPIO 출력 방법에 대해서 알아 보겠습니다.

GPIO 출력을 위해서는 먼저 GPIO 를 초기화 해야하는데, 
STM 계열의 문법은, 먼저 다음과 같은 구조체로 GPIO 변수를 선언하는 군요.

main.c 파일에서 global 구조체 함수로 다음과 같이 정의를 합니다.
static GPIO_InitTypeDef  GPIO_InitStruct;

GPIO_InitTypeDef 라는 구조체 형식은 다음과 같습니다.
/** 
  * @brief GPIO Init structure definition  
  */ 
typedef struct
{
  uint32_t Pin;       /*!< Specifies the GPIO pins to be configured.
                           This parameter can be any value of @ref GPIO_pins_define */

  uint32_t Mode;      /*!< Specifies the operating mode for the selected pins.
                           This parameter can be a value of @ref GPIO_mode_define */

  uint32_t Pull;      /*!< Specifies the Pull-up or Pull-Down activation for the selected pins.
                           This parameter can be a value of @ref GPIO_pull_define */

  uint32_t Speed;     /*!< Specifies the speed for the selected pins.
                           This parameter can be a value of @ref GPIO_speed_define */

  uint32_t Alternate;  /*!< Peripheral to be connected to the selected pins. 
                            This parameter can be a value of @ref GPIO_Alternate_function_selection */
}GPIO_InitTypeDef;


제가 갖고 있는 Nucleo 보드에서 LED가 PA5 에 연결되어 있어서 핀을 출력으로 설정해 봤습니다.
main.c 파일 내에, uart 테스트에 사용했던 프로젝트에서 사용된 초기화 함수에 PA5 설정에 대한 코드를 추가했습니다.
다음의 빨간 색으로 된 부분이 추가된 코드입니다. 

__HAL_RCC_GPIOA_CLK_ENABLE() 함수는 원래 부터 uart 테스트 프로젝트에 있던건데 아마도 Uart 포트가 PA2,3 이라 포트를 사용하기 위해서 Clock Enable이 필요한 듯 합니다.

static void MX_GPIO_Init(void)
{

  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOA_CLK_ENABLE();

  /* -2- Configure PA05 IO in output push-pull mode to
         drive external LED */
  GPIO_InitStruct.Pin = GPIO_PIN_5;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_PULLUP;
  GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
  HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); 
}


다음으로 main()함수에 출력으로 설정된 LED GPIO 를 Toggle 하고 딜레이 함수로 100ms 기다리고를 반복하는 코드를 추가했습니다.

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

  /* Configure the system clock */
  SystemClock_Config();

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_USART2_UART_Init();

  /* USER CODE BEGIN 2 */

HAL_UART_Transmit(&huart2,"UART2 Test~!!! \n\r",17,0xFFFF);
//HAL_StatusTypeDef HAL_UART_Transmit(UART_HandleTypeDef *huart, uint8_t *pData, uint16_t Size, uint32_t Timeout)
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */

  /* USER CODE BEGIN 3 */
    HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
    
    /* Insert delay 100 ms */
    HAL_Delay(100);

  }
  /* USER CODE END 3 */

}


자동으로 리셋이 되지 않아서, 프로그램이 잘 안됐나 했는데...
리셋 버튼을 눌러주니 동작하기 시작하네요.

uart2 테스트와 gpio 테스트 파일을 첨부(공유)함니다.

2016년 6월 3일 금요일

[PSOC] PSOC4 SPI 핀 <-> GPIO 핀

SPI 기능으로 설정된 핀을 잠시 GPIO 기능의 핀으로 바꿨다가 다시 SPI로 되돌리는 테스트를 했습니다.

이런 경우는 보통 드문데, 핀이 모자라서 하게 되었습니다.

SPI TopDesign 탭의 내용은 다음과 같습니다.




SPI -> GPIO 동작 방법은 다음과 같습니다.
1. SPI component STOP.
2. SPI 핀 모드를 GPIO 출력인 경우 Strong Drive 로 설정
3. SPI핀 기능을 GPIO 핀으로 변경
4. GPIO 기능 처럼 핀을 프로그램에서 사용.
    SPIFLASH_Stop();
    SPIFLASH_miso_m_SetDriveMode(SPIFLASH_miso_m_DM_STRONG);
    
    SPIFLASH_SET_HSIOM_SEL(*(reg32 *)SPIFLASH_miso_m__0__HSIOM, SPIFLASH_miso_m__0__HSIOM_MASK,
                                   SPIFLASH_miso_m__0__HSIOM_SHIFT, SPIFLASH_HSIOM_GPIO_SEL);

        SPIFLASH_miso_m_Write(1); CyDelayUs(1);
        SPIFLASH_miso_m_Write(0); CyDelayUs(1);
        SPIFLASH_miso_m_Write(1); CyDelayUs(1);

GPIO -> SPI 동작 방법은 다음과 같습니다.
1. SPI핀 기능을 GPIO 핀으로 변경
2. SPI component START.
    SPIFLASH_SET_HSIOM_SEL(*(reg32 *)SPIFLASH_miso_m__0__HSIOM, SPIFLASH_miso_m__0__HSIOM_MASK,
                                   SPIFLASH_miso_m__0__HSIOM_SHIFT, SPIFLASH_HSIOM_SPI_SEL);
    SPIFLASH_Start();

2016년 5월 5일 목요일

[PSOC] PSOC5 CAPsense 기능으로 부트로더 셀렉트, APP

@@ 술먹으면서 프로그램 짜다가 더이상은 안되서 글을 못쓰고 동영상을 올려보겠습니다.

부트로더를 동작하게 하려고 할 때, 터치 스위치를 이용하려고 했다가 
특정 스위치를 누르고 부트로더로 진입하려고 했었는데, 안되네요. 뭔가 실수가 있겠죠.

시간이 없어서 그냥 2초 내에 터치를 누르면 부트로더로 진입하도록 프로그램했습니다.

에구... USB_UART 기능도 나중에 손봐야 하는데, 현재는 버튼을 누르면 USB_UART 로 진입하도록 했습니다.
나중에는 아무 때나 USB_UART를 사용할 수 있게 할 예정입니다.(시간이 너무 없어서요. 그런데 난 술먹고 취해버렸군요. ㅜㅜ)

터치 스위치로 부트로더로 진입해서, 터치 스위치로 USB 통신 모드로 또 진입하고, 터치 스위치로 IO 제어를 하는 것을 
동영상으로 찍어서 보여 드리겠습니다. 이젠 자러 갑니다. ^^


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년 2월 6일 토요일

[dialog] I/O control, UART Tx Write Test with DSPS (DA14580) (2/3)

이번에는 지난 번에 만들어 놓은 Timer callbak 함수 내에 일정한 주기로 포트를 읽어서 입력된 포트값의 상태를 읽어서 UART에 Write 하는 동작을 테스트 해 보겠습니다.

먼저, 입력 포트를 추가합니다.

periph.c 의 GPIO_reservations 함수에 다음을 추가합니다. Expert Kit 의 스위치는 2개가 있는데, P0_6 과 P1_1 이어서 이 2개의 포트를 추가합니다.

void GPIO_reservations(void)
{
   RESERVE_GPIO(SW0, GPIO_PORT_0, GPIO_PIN_6, PID_GPIO);    // SW IN(K2)
   RESERVE_GPIO(SW1, GPIO_PORT_1, GPIO_PIN_1, PID_GPIO);    // SW IN(K1)
   
   RESERVE_GPIO(LED, LED_PORT, LED_PIN, PID_GPIO);    // LED OUT

   RESERVE_GPIO( UART1_TX, UART1_TX_PORT, UART1_TX_PIN, PID_UART1_TX);  
   RESERVE_GPIO( UART1_RX, UART1_RX_PORT, UART1_RX_PIN, PID_UART1_RX);
#if (UART_HW_FLOW_ENABLED) 
   RESERVE_GPIO( UART1_RTS, UART1_RTS_PORT, UART1_RTS_PIN, PID_UART1_RTSN);  
   RESERVE_GPIO( UART1_CTS, UART1_CTS_PORT, UART1_CTS_PIN, PID_UART1_CTSN);
#endif /*UART_HW_FLOW_ENABLED*/
}

set_pad_functions 함수에도 코드를 추가합니다.
void set_pad_functions(void)        // set gpio port function mode
{
   GPIO_ConfigurePin(GPIO_PORT_0, GPIO_PIN_6, INPUT_PULLUP, PID_GPIO, true);
   GPIO_ConfigurePin(GPIO_PORT_1, GPIO_PIN_1, INPUT_PULLUP, PID_GPIO, true);

   GPIO_ConfigurePin(LED_PORT, LED_PIN, OUTPUT, PID_GPIO, false);

    GPIO_ConfigurePin( UART1_TX_PORT, UART1_TX_PIN, OUTPUT, PID_UART1_TX, false );
    GPIO_ConfigurePin( UART1_RX_PORT, UART1_RX_PIN, INPUT_PULLUP, PID_UART1_RX, false );
#if (UART_HW_FLOW_ENABLED) 
    GPIO_ConfigurePin( UART1_RTS_PORT, UART1_RTS_PIN, OUTPUT, PID_UART1_RTSN, false );
    GPIO_ConfigurePin( UART1_CTS_PORT, UART1_CTS_PIN, INPUT_PULLUP, PID_UART1_CTSN, false );
#endif /*UART_HW_FLOW_ENABLED*/
}



다음으로, 지난번에 만들어 두었던 타이머 인터럽트에 의해 1초마다 콜백되는 timer0_general_user_callback_function 함수에 포트를 읽어서 UART로 송신하는 프로그램을 추가해 보겠습니다.

UART에 Write 하는 방법은 사실 좀 오랫동안 찾아 해메다가 알아냈습니다. 좀 편법적인듯도 하지만 동작은 잘 되네요. DSPS 프로그램은 버퍼가 2개 있는데 1)BLE 쪽 송/수신 버퍼 와 2)UART 송/수신 버퍼 입니다. DSPS 동작 특성은 BLE 로 받으면 무조건 UART로 보내게 되어 있으므로 BLE 수신 버퍼에 데이터를 Write 하면 UART에 출력됩니다. 이 버퍼는 링버퍼로 이루어져 있는데 아무 때나, 버퍼에 최대 용량 이내로 데이터를 넣어주면 UART로 출력되고, 다 나가면 버퍼는 비워집니다.

쉽게 말하면, da14580을 BLE에서 데이터가 들어온 것으로 속이는 것입니다.
BLE 수신시 링버퍼에 데이터를 넣는(push) 함수가 app_ble_push() 입니다.
이 함수에 전송할 데이터와 데이터 갯수를 전달해 주면 UART에 출력됩니다.

void timer0_general_user_callback_function(void) 
{
   static uint8_t n = 0;
   static uint32_t cnt_100ms = 0;
   uint8_t try_test[100];
   uint16_t port_Input=0;

   cnt_100ms++;
    // when pass  10 * 100ms
    if ( 10 == n ) 
    {
      port_Input = GPIO_GetPinStatus(GPIO_PORT_0,GPIO_PIN_6) + ((GPIO_GetPinStatus(GPIO_PORT_1,GPIO_PIN_1))<<1 span="">

      sprintf((char *)try_test,"time = %8d ms , Port state : %01X\n\r\0",(cnt_100ms*100),port_Input);

      app_ble_push(try_test,size_user_str(try_test));
       n = 0;
        if (GPIO_GetPinStatus(LED_PORT, LED_PIN))
        {
            GPIO_SetInactive(LED_PORT, LED_PIN);
        }
        else
        {
            GPIO_SetActive(LED_PORT, LED_PIN);
        }
     }
     n++;
}

자 그럼, 출력된 UART 를 터미날로 볼까요?


버튼 상태가 잘 읽히고, DA14580을 속여서 UART로 데이터가 잘 송신되고 있습니다. ^^

[dialog] IO control with da14580 DSPS profile (1/3)

안녕하세요, 이번에는 Dialog 의 DA14580 BLE chip으로 이미 만들어져 있는 DSPS 프로파일 프로젝트에 Timer,UART에 데이터 송신,BLE에 데이터 송신,Port Out/Input 테스트를 진행하겠습니다.

추가로 BLE로 DA14580이 직접 데이터를 수신하여 특정 명령어에 반응하도록 테스트 해 보겠습니다.

저는 DA14580 developement kit Expert 를 갖고 있습니다. (basic kit 가 더 좋은 것 같습니다. JTAG도 내장되어있고 SPI FLASH도 있고 싸고요. ^^. Expert 는 참고로 130만원. )

이 테스트는 JTAG로 그냥 Debug 모드로 SRAM에 올려서 테스트 했습니다.
UART에 점퍼 셋팅해 놓았고요. 이 외에 특이한 사항은 없습니다.

좀 프로그램이 다듬어지지 않았고, 걍 테테스트입니다. ^^

테스트 보드 사진입니다. (점퍼가 없어져서 선으로 이은 것 뿐, 특이 사항 없슴.)


먼저 GPIO 출력포트를 DSPS 기본 예제에 추가해 보겠습니다.

1. periph_setup.c 에 있는 void GPIO_reservations(void) 함수에 코드를 추가했습니다.

#if DEVELOPMENT_DEBUG

void GPIO_reservations(void)
{
RESERVE_GPIO(LED, LED_PORT, LED_PIN, PID_GPIO);    // LED OUT

RESERVE_GPIO( UART1_TX, UART1_TX_PORT, UART1_TX_PIN, PID_UART1_TX);
RESERVE_GPIO( UART1_RX, UART1_RX_PORT, UART1_RX_PIN, PID_UART1_RX);

#if (UART_HW_FLOW_ENABLED)
 RESERVE_GPIO( UART1_RTS, UART1_RTS_PORT, UART1_RTS_PIN, PID_UART1_RTSN);
RESERVE_GPIO( UART1_CTS, UART1_CTS_PORT, UART1_CTS_PIN, PID_UART1_CTSN);
#endif /*UART_HW_FLOW_ENABLED*/
}
#endif //DEVELOPMENT_DEBUG

(Dialog 기본프로그램은  GPIO 포트를 사용하려할때, GPIO_reservations에서 reserve 하더군요. )



2. set_pad_functions() 함수도 바꿔 줬습니다.

void set_pad_functions(void)        // set gpio port function mode
{
    GPIO_ConfigurePin(LED_PORT, LED_PIN, OUTPUT, PID_GPIO, false);

    GPIO_ConfigurePin( UART1_TX_PORT, UART1_TX_PIN, OUTPUT, PID_UART1_TX, false );
    GPIO_ConfigurePin( UART1_RX_PORT, UART1_RX_PIN, INPUT_PULLUP, PID_UART1_RX, false );
#if (UART_HW_FLOW_ENABLED)
    GPIO_ConfigurePin( UART1_RTS_PORT, UART1_RTS_PIN, OUTPUT, PID_UART1_RTSN, false );
    GPIO_ConfigurePin( UART1_CTS_PORT, UART1_CTS_PIN, INPUT_PULLUP, PID_UART1_CTSN, false );
#endif /*UART_HW_FLOW_ENABLED*/
}


자, 이렇게 설정하고 Timer Interrupt 동작을 한번 테스트 해 볼까요?

periph.c  의 periph_init 함수를 다음과 같이 수정합니다.
- 타이머 인터럽트 발생 주기를 100ms(RELOAD_100MS=20000)로 설정하고 타이머 인터럽트를 활성화합니다.

void periph_init(void)
{
// Power up peripherals' power domain
    SetBits16(PMU_CTRL_REG, PERIPH_SLEEP, 0);
    while (!(GetWord16(SYS_STAT_REG) & PER_IS_UP)) ;
//rom patch
patch_func();
//Init pads
set_pad_functions();
   
 SetBits16(CLK_PER_REG, UART1_ENABLE, 1);    // enable clock - always @16MHz
    // baudr=9-> 115k2
    // mode=3-> no parity, 1 stop bit 8 data length
 uart_sps_init(UART_SPS_BAUDRATE, 3); //exact baud rate defined in uart.h

   // Enable the pads
SetBits16(SYS_CTRL_REG, PAD_LATCH_EN, 1);

#if (EXT_SLEEP_ENABLED)
    app_scheduler_reinit();
#endif //EXT_SLEEP_ENABLED

    // Stop timer for enter settings
    timer0_stop();
    // register callback function for SWTIM_IRQn irq
    timer0_register_callback(timer0_general_user_callback_function);
    // Enable TIMER0 clock
    set_tmr_enable(CLK_PER_REG_TMR_ENABLED);
    // Sets TIMER0,TIMER2 clock division factor to 8, so TIM0 Fclk is F = 16MHz/8 = 2Mhz
    set_tmr_div(CLK_PER_REG_TMR_DIV_8);
    // clear PWM settings register to not generate PWM
    timer0_set_pwm_high_counter(NO_PWM);
    timer0_set_pwm_low_counter(NO_PWM);
    
    timer0_set_pwm_on_counter(RELOAD_100MS);
    timer0_enable_irq();
    timer0_start();


#if ((UART_HW_FLOW_ENABLED) && !defined(GPIO_DRV_IRQ_HANDLING_DISABLED))
    GPIO_RegisterCallback(GPIO0_IRQn, gpio0_callback);
    if(GPIO_GetPinStatus(UART1_CTS_PORT,UART1_CTS_PIN)==FALSE)
    {
        GPIO_EnableIRQ(UART1_CTS_PORT, UART1_CTS_PIN, GPIO0_IRQn, 0, 1, 0);
    }
    else if(GPIO_GetPinStatus(UART1_CTS_PORT,UART1_CTS_PIN)==TRUE)
    {
        GPIO_EnableIRQ(UART1_CTS_PORT, UART1_CTS_PIN, GPIO0_IRQn, 1, 0, 0);
    }
#endif //((UART_HW_FLOW_ENABLED) && !defined(GPIO_DRV_IRQ_HANDLING_DISABLED))
}

이렇게 하면  타이머 인터럽트가 걸리면 void timer0_general_user_callback_function(void) 함수를 호출하게 됩니다.


다음으로 timer0_general_user_callback_function 함수의 내용 입니다.
void timer0_general_user_callback_function(void)
{
    static uint8_t n = 0;
    static uint32_t cnt_100ms = 0;
uint8_t try_test[100];
uint16_t port_Input=0;

cnt_100ms++;
    // when pass  10 * 100ms
    if ( 10 == n )
    {
        n = 0;
        if (GPIO_GetPinStatus(LED_PORT, LED_PIN))
        {
            GPIO_SetInactive(LED_PORT, LED_PIN);
        }
        else
        {
            GPIO_SetActive(LED_PORT, LED_PIN);
        }
     }
     n++;
}


자, 이렇게 하면 어떻게 될까요?
LED_PORT 의 LED_PIN 이 토글하겠죠? 

정확히 말하면 타이머 인터럽트가 100ms 마다 발생하면, timer0_general_user_callback_function 함수가 호출되어 static 변수 n이 1씩 증가하다가 n이 10이되면(1초마다) 현재 LED에 연결되어 있는 GPIO 포트를 읽어서 1이면 0, 0이면 1로 토글하는 동작을 합니다.

이렇게 해서, DSPS에 Timer 와 GPIO 출력까지 추가해 봤습니다.

예제 소스 파일은 마지막(3/3)에 추가 하겠습니다.

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="">
           
            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);
    }
}