지난 번의 IDLE IT 를 사용한 DMA 테스트는 UART2에서 한 테스트였고,

이번에는 UART1 에서 테스트를 해 봤다.

수정할 내용만 적어 보면 다음과 같다.

UART1 인터럽트함수의 코드는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void USART1_IRQHandler(void)
{
  /* USER CODE BEGIN USART1_IRQn 0 */
    /* Check for IDLE flag */
    if (USART1->SR & UART_FLAG_IDLE) {         /* We want IDLE flag only */
        /* This part is important */
        /* Clear IDLE flag by reading status register first */
        /* And follow by reading data register */
        volatile uint32_t tmp;                  /* Must be volatile to prevent optimizations */
        tmp = USART1->SR;                       /* Read status register */
        tmp = USART1->DR;                       /* Read data register */
        (void)tmp;                              /* Prevent compiler warnings */
        __HAL_DMA_DISABLE(&hdma_usart1_rx);
    }    
    return;
  /* USER CODE END USART1_IRQn 0 */
  HAL_UART_IRQHandler(&huart1);
  /* USER CODE BEGIN USART1_IRQn 1 */
 
  /* USER CODE END USART1_IRQn 1 */
}
cs
 
UART1 의 DMA 인터럽트는 DMA2_Stream2_IRQHandler 함수를 사용한다.
코드는 다음과 같다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
void DMA2_Stream2_IRQHandler(void)
{
  /* USER CODE BEGIN DMA2_Stream2_IRQn 0 */
    size_t len, tocopy;
    uint8_t* ptr;
    
    /* Check transfer complete flag */
    if (DMA2->LISR & DMA_FLAG_TCIF2_6) {
        DMA2->LIFCR = DMA_FLAG_TCIF2_6;           /* Clear transfer complete flag */
        
        /* Calculate number of bytes actually transfered by DMA so far */
        /**
         * Transfer could be completed by 2 events:
         *  - All data actually transfered (NDTR = 0)
         *  - Stream disabled inside USART IDLE line detected interrupt (NDTR != 0)
         */
        len = DMA_RX_BUFFER_SIZE - DMA2_Stream2->NDTR;
        tocopy = UART_BUFFER_SIZE - Write;      /* Get number of bytes we can copy to the end of buffer */
        
        /* Check how many bytes to copy */
        if (tocopy > len) {
            tocopy = len;
        }
        
        /* Write received data for UART main buffer for manipulation later */
        ptr = DMA_RX_Buffer;
        memcpy(&UART_Buffer[Write], ptr, tocopy);   /* Copy first part */
                
        
        /* Correct values for remaining data */
        Write += tocopy;
        len -= tocopy;
        ptr += tocopy;
        
        /* If still data to write for beginning of buffer */
        if (len) {
            memcpy(&UART_Buffer[0], ptr, len);      /* Don't care if we override Read pointer now */
            Write = len;
        }
        
        /* Prepare DMA for next transfer */
        /* Important! DMA stream won't start if all flags are not cleared first */
        //DMA2->HIFCR = DMA_FLAG_DMEIF1_5 | DMA_FLAG_FEIF1_5 | DMA_FLAG_HTIF1_5 | DMA_FLAG_TCIF1_5 | DMA_FLAG_TEIF1_5;
        DMA2->LIFCR = DMA_FLAG_DMEIF2_6 | DMA_FLAG_FEIF2_6 | DMA_FLAG_HTIF2_6 | DMA_FLAG_TCIF2_6 | DMA_FLAG_TEIF2_6;
        DMA2_Stream2->M0AR = (uint32_t)DMA_RX_Buffer;
        DMA2_Stream2->NDTR = DMA_RX_BUFFER_SIZE;
        __HAL_DMA_ENABLE(&hdma_usart1_rx);
        //DMA1_Stream5->CR |= DMA_SxCR_EN;            /* Start DMA transfer */
    }
    return;
  /* USER CODE END DMA2_Stream2_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart1_rx);
  /* USER CODE BEGIN DMA2_Stream2_IRQn 1 */
 
  /* USER CODE END DMA2_Stream2_IRQn 1 */
}
cs

main() 함수 코드 내용은 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART1_UART_Init();
 
  /* USER CODE BEGIN 2 */
    __HAL_UART_ENABLE_IT(&huart1,UART_IT_IDLE);
    HAL_UART_Receive_DMA(&huart1,DMA_RX_Buffer,DMA_RX_BUFFER_SIZE);
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */
 
  /* USER CODE BEGIN 3 */
        if (Read != Write)
        { 
            __wfi();    // or __wfe();
            //__HAL_UNLOCK(&huart2);
            //__HAL_LOCK(&huart2);
            while (!(USART1->SR & USART_SR_TXE));   /* Wait till finished */
            USART1->DR = UART_Buffer[Read++];
            //HAL_UART_Transmit(&huart2,(uint8_t *)UART_Buffer[Read++],1,1);
            //while (!(USART2->SR & USART_SR_TC));   /* Wait till finished */
            //while (!(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TXE))){};
                    
                    
            if (Read > UART_BUFFER_SIZE) /* Check buffer overflow */
            {     
                    Read = 0;
            }
        }
        //while(HAL_GPIO_ReadPin(B1_GPIO_Port,B1_Pin) == GPIO_PIN_RESET);
        //while(HAL_GPIO_ReadPin(B1_GPIO_Port,B1_Pin) == GPIO_PIN_SET);
        
  }
  /* USER CODE END 3 */
 
}
cs


이전 글에 이어서, 프로그램 동작 테스트를 해 보겠습니다.


CUBEMX에 의해 생성된 기본 코드에서, 추가할 내용은 다음과 같습니다.
[main.c]
1. UART_IT_IDLE 인터럽트 설정.
2. UART RX DMA 설정.

[stm32f4xx_it.c]
1. USART2_IRQHandler 함수에서, HAL_UART_IRQHandler 를 사용하지 않고(너무 느림), 
   IDLE 인터럽트만 처리하도록 수정. IDLE 인터럽트 발생시, DMA rx 인터럽트 강제 발생 후, DMA Disable.
2. DMA1_Stream5_IRQHandler 함수에서 HAL_DMA_IRQHandler 함수를 쓰지 않고,
   FIFO 에서 데이터를 사용자 UART 버퍼로 copy 해온 후, DMA Enable.


다음은 위의 내용에 대한 상세 설명 입니다.
[main.c]
1. UART_IT_IDLE 인터럽트 설정.
2. UART RX DMA 설정.
[기본 변수, 및 define 설정 내용]
1
2
3
4
5
6
7
#define UART_BUFFER_SIZE         1024
#define DMA_RX_BUFFER_SIZE         1024
 
uint8_t UART_Buffer[UART_BUFFER_SIZE];
uint8_t DMA_RX_Buffer[DMA_RX_BUFFER_SIZE];
size_t Write, Read;
 
cs
버퍼 Size 는 대충 잡은 것이니 적당히 수정하시기 바랍니다. 많으면 스택하고 메모리가 많이 들어가니까요.
[설정 코드]
1
2
    __HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);
    HAL_UART_Receive_DMA(&huart2,DMA_RX_Buffer,DMA_RX_BUFFER_SIZE);
cs
링크 건, 블로그는 정말 복잡한데, HAL 로 하니 2줄이면 끝. (CUBEMX가 알아서 설정해 줘서 그렇습니다)

[stm32f4xx_it.c]
1. USART2_IRQHandler 함수에서, HAL_UART_IRQHandler 를 사용하지 않고(너무 느림), 
   IDLE 인터럽트만 처리하도록 수정. IDLE 인터럽트 발생시, DMA rx 인터럽트 강제 발생 후, DMA Disable.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
/**
* @brief This function handles USART2 global interrupt.
*/
void USART2_IRQHandler(void)
{
  /* USER CODE BEGIN USART2_IRQn 0 */
    /* Check for IDLE flag */
    if (USART2->SR & UART_FLAG_IDLE) {         /* We want IDLE flag only */
        /* This part is important */
        /* Clear IDLE flag by reading status register first */
        /* And follow by reading data register */
        volatile uint32_t tmp;                  /* Must be volatile to prevent optimizations */
        tmp = USART2->SR;                       /* Read status register */
        tmp = USART2->DR;                       /* Read data register */
        (void)tmp;                              /* Prevent compiler warnings */
                __HAL_DMA_DISABLE(&hdma_usart2_rx);
 
        //DMA1_Stream5->CR &= ~DMA_SxCR_EN;       /* Disabling DMA will force transfer complete interrupt if enabled */
    }    
    return;
  /* USER CODE END USART2_IRQn 0 */
  HAL_UART_IRQHandler(&huart2);
  /* USER CODE BEGIN USART2_IRQn 1 */
 
  /* USER CODE END USART2_IRQn 1 */
}
cs

위에서 HAL_UART_IRQHandler() 함수 전에 return; 을 사용해주면, 
간단히 이후의 코드 실행을 막을 수 있습니다. ^^

2. DMA1_Stream5_IRQHandler 함수에서 HAL_DMA_IRQHandler 함수를 쓰지 않고,
   FIFO 에서 데이터를 사용자 UART 버퍼로 copy 해온 후, DMA Enable.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
/**
* @brief This function handles DMA1 stream5 global interrupt.
*/
void DMA1_Stream5_IRQHandler(void)
{
  /* USER CODE BEGIN DMA1_Stream5_IRQn 0 */
    size_t len, tocopy;
    uint8_t* ptr;
    
    /* Check transfer complete flag */
    if (DMA1->HISR & DMA_FLAG_TCIF1_5) {
        DMA1->HIFCR = DMA_FLAG_TCIF1_5;           /* Clear transfer complete flag */
        
        /* Calculate number of bytes actually transfered by DMA so far */
        /**
         * Transfer could be completed by 2 events:
         *  - All data actually transfered (NDTR = 0)
         *  - Stream disabled inside USART IDLE line detected interrupt (NDTR != 0)
         */
        len = DMA_RX_BUFFER_SIZE - DMA1_Stream5->NDTR;
        tocopy = UART_BUFFER_SIZE - Write;      /* Get number of bytes we can copy to the end of buffer */
        
        /* Check how many bytes to copy */
        if (tocopy > len) {
            tocopy = len;
        }
        
        /* Write received data for UART main buffer for manipulation later */
        ptr = DMA_RX_Buffer;
        memcpy(&UART_Buffer[Write], ptr, tocopy);   /* Copy first part */
        
        /* Correct values for remaining data */
        Write += tocopy;
        len -= tocopy;
        ptr += tocopy;
        
        /* If still data to write for beginning of buffer */
        if (len) {
            memcpy(&UART_Buffer[0], ptr, len);      /* Don't care if we override Read pointer now */
            Write = len;
        }
        
        /* Prepare DMA for next transfer */
        /* Important! DMA stream won't start if all flags are not cleared first */
                DMA1->HIFCR = DMA_FLAG_DMEIF1_5 | DMA_FLAG_FEIF1_5 | DMA_FLAG_HTIF1_5 | DMA_FLAG_TCIF1_5 | DMA_FLAG_TEIF1_5;
                DMA1_Stream5->M0AR = (uint32_t)DMA_RX_Buffer;
                DMA1_Stream5->NDTR = DMA_RX_BUFFER_SIZE;
                __HAL_DMA_ENABLE(&hdma_usart2_rx);
        //DMA1_Stream5->CR |= DMA_SxCR_EN;            /* Start DMA transfer */
    }
        return;
  /* USER CODE END DMA1_Stream5_IRQn 0 */
  HAL_DMA_IRQHandler(&hdma_usart2_rx);
  /* USER CODE BEGIN DMA1_Stream5_IRQn 1 */
 
  /* USER CODE END DMA1_Stream5_IRQn 1 */
}
cs


이렇게 하면, STM32 의 UART RX 에서 FIFO 가 없어서, 데이터를 놓칠 걱정은 더 이상 하지 않아도 됩니다. ^^

주의할 점이 하나 있는데, IDLE 인터럽트를 걸리도록 데이터를 PC 나 다른 장치에서 송신해야 한다는 점입니다.
파일을 전송하는 테스트를 진행해 본 결과, 
DMA RX Buffer 보다 큰 크기와 설정한 DMA 길이 보다 큰 데이터 스트링을 보낼 경우는 다음과 같이, 
송신하는 측에서 딜레이를 약간 줘야 합니다.

그래야, STM32 에서 DMA 데이터를 USER buff에 copy할 수 있습니다. 
저는 tera term 에서 line 당 지연을 1ms 줬습니다.







main.c 의 main() 에서 받은 데이터가 있으면, 그대로 term에 뿌리도록 해서,
파일을 전송 해 보니, 잘 수신 되었음을 알 수 있었습니다.





또 하나, 정말 모르겠던 부분이 있습니다.
HAL_UART_Transmit 함수로 사용해도 되긴 하는데, 너무 내부 코드가 많아서 바꿔 봤는데,
바꾼 코드가 아무리 해도 실행이 안되서, 앞에 __wfi() 나 __wfe() 를 넣어 보니 잘 돌아가더라고요.
순전히 ,  여러가지 넣어 보면서 허송 세월을 보내 버렸네요.
왜 이렇게 되는지 혹시 아시는분 댓글 부탁합니다. 
1
2
3
__wfi();    // or __wfe();
while (!(USART2->SR & USART_SR_TXE));   /* Wait till finished */
USART2->DR = UART_Buffer[Read++];
cs

위와 동일한 코드
1
HAL_UART_Transmit(&huart2,(uint8_t *)UART_Buffer[Read++],1,1);
cs


전체 main() 함수 내용.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
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();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART2_UART_Init();
 
  /* USER CODE BEGIN 2 */
    __HAL_UART_ENABLE_IT(&huart2,UART_IT_IDLE);
    HAL_UART_Receive_DMA(&huart2,DMA_RX_Buffer,DMA_RX_BUFFER_SIZE);
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */
 
  /* USER CODE BEGIN 3 */
        if (Read != Write)
        { 
            __wfi();    // or __wfe();
            //__HAL_UNLOCK(&huart2);
            //__HAL_LOCK(&huart2);
            while (!(USART2->SR & USART_SR_TXE));   /* Wait till finished */
            USART2->DR = UART_Buffer[Read++];
            //HAL_UART_Transmit(&huart2,(uint8_t *)UART_Buffer[Read++],1,1);
            //while (!(USART2->SR & USART_SR_TC));   /* Wait till finished */
            //while (!(__HAL_UART_GET_FLAG(&huart2, UART_FLAG_TXE))){};
                    
                    
            if (Read > UART_BUFFER_SIZE) /* Check buffer overflow */
            {     
                    Read = 0;
            }
        }
        //while(HAL_GPIO_ReadPin(B1_GPIO_Port,B1_Pin) == GPIO_PIN_RESET);
        //while(HAL_GPIO_ReadPin(B1_GPIO_Port,B1_Pin) == GPIO_PIN_SET);
        
  }
  /* USER CODE END 3 */
 
}
cs


keil 5 로 만든 코드와, cubemx ioc 파일을 첨부합니다.

uart_rx_dma.zip


휴, 밤새 테스트해보다 겨우 됐네요.


이번에 다룰 내용은, STM32F4xx 의 uart Rx 에서 DMA 함수를 사용하면 발생하는 불편함을 수정하는 내용입니다.
DMA를 사용하면, RX data를 수신할 때, 1-Byte마다 인터럽트가 걸리지 않으므로 그 시간만큼 다른 일을 할 수가 있습니다.
일종의 코프로세서라고 말하는 사람도 있습니다. 또는 듀얼코어..

그런데 UART DMA RX 는 HAL 함수에서, 또는 다른 라이브러리 함수에서(다른 함수들은 안써봐서 잘 모름),
지정한 길이 만큼만 받아야 인터럽트가 발생해서,
지정한 길이만큼 받지 않으면 데이터를 갖고 올 수가 없었습니다.

또한 UART RX 시에는 언제, 몇개의 데이터가 들어 올지 미리 알 수가 없는 상황이 대부분이라,
저 같은 경우에는 UART RX DMA를 쓰는 경우는 전혀 없습니다. 쓰려면 TX DMA 를 쓰죠.

예를 들자면 
HAL_UART_Receive_DMA(&huart2,DMA_RX_Buffer,10); 
위와같이 특정 개수 만큼(10개) DMA 로 받겠다고 하면, 
10개 이하는 DMA 인터럽트가 안 떠서 데이터를 못 받고,
10개 이상 받으면, 10개는 받고 나머지 10개 이상은 받지 못하게 됩니다.

이와 같은 점을 수정한 방법이 UART_IDLE 인터럽트를 쓰는 방법입니다.
UART_IDLE 인터럽트는 데이터를 수신하다가 1개 이상 데이터가 수신되지 않을 시 걸리는 인터럽트입니다.
보통 연속으로 데이터가 오다가, 다 보냈을 경우 1개 이상의 데이터가 수신되지 않는 경우가 무조건 발생하게 됩니다.
이 때, UART RX DMA 인터럽트를 강제로 발생시키도록 하는 원리 입니다.

이 내용은, 예전부터 외국 블로그에서 공개한 내용인데 실제로 사용할 수 있도록 테스트를 해 보지 못했습니다.
참고로 한 사이트 주소는 다음과 같습니다.


사용한 H/W 는 ST 의 NUCLEO-F411RE 입니다.
UART만 테스트 하는 거라, 이미 내부 디버거(ST-LINK)에 의해 USB-to-UART 연결된 UART2를 사용했습니다.

H/W 구성 및 CUBEMX 설정은 다음과 같습니다.




이전에 쓴 글([STM32F4xx] I2C DMA 설정 (CUBEMX))에 이어서, 

프로그램에 대해서 설명해 보겠습니다.
I2C DMA 및 인터럽트, 추가로 시퀀셜 제어까지 모두 설명해 보겠습니다.

이번에도, 보안칩 회사의 철통같은 보안으로 메뉴얼 조차도 보안적으로 설명을 좀 빼먹어서,
I2C 시퀀셜 송/수신 함수까지 써 보게 되었습니다. ^^

열심이 공부해서, 보안 칩의 보안을 깨 버리겠습니다. ^^
이미 I2C DEVICE ADDRESS 안 알려줘서 찾아내 버렸습니다. ㅋㅋ

이 보안칩은 저희 사장님의 친구인 사장님이 만든 거라는데, 가장 싼 모델이랍니다.
그래서 인지, 이름도 없고 핀 번호 표시도 하얀 페인트로 칠해져 있어서 알았습니다.
보안상 그런게 아니라, 진짜 이름도 모릅니다. ^^

I2C 의 전송 HAL LIB 함수를 사용했는데, 여기서 사용한 함수는 다음과 같습니다.
모두 인터럽트를 사용하고, 같은 콜백함수(Tx 함수는 Tx 콜백,Rx 함수는 Rx 콜백)를 호출합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// DMA Function
// DMA Transmit Function
HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
// DMA Receive Function
HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
 
// Interrupt Function
// Interrupt Transmit Function
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
// Interrupt Receive Function
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
 
// Interrupt Sequential Function
// Interrupt Sequential Transmit Function
HAL_StatusTypeDef HAL_I2C_Master_Sequential_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions)
// Interrupt Sequential Receive Function
HAL_StatusTypeDef HAL_I2C_Master_Sequential_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions)
cs

지금까지 Sequential 함수는 사용할 일이 없었는데, 보안칩 메뉴얼 때문에 반드시 사용해야 하는 줄 알고 쓰게 됐습니다. ㅠㅠ

보안 칩 메뉴얼에 Transmit 함수는 그냥 IT 함수나 DMA 함수를 쓰면 되는데,
Receive 할 때 Device Address 와 Sub Address를 쓰고 나서 STOP 을 하지 말라고 해서, 방법을 찾아보니 Sequential 함수라는 놈이 있었습니다.
다음은 제가 참고로 본 보안 칩 메뉴얼 중에서, Tx/Rx 송/수신 정보 입니다.





여기 아랫 쪽에, Non STOP 보이시죠. 젠장 욕나온다. ㅜㅜ
나중에 테스트를 해보니까, STOP 하고 읽어도 되는 것이었습니다. DMA 나 일반 IT 함수 쓰면 되는 것이죠.





아뭏든 DMA 함수의 사용 방법은 다음과 같습니다.

송신시, HAL_I2C_Master_Transmit_DMA() 함수를 사용합니다.
함수 프로토타입은 다음과 같습니다.
1
HAL_StatusTypeDef HAL_I2C_Master_Transmit_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
cs
사용예는 다음과 같습니다.
1
HAL_I2C_Master_Transmit_DMA(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_wr_buff,9);
cs

수신시, HAL_I2C_Master_Receive_DMA() 함수를 사용합니다.
함수 프로토타입은 다음과 같습니다.
1
HAL_StatusTypeDef HAL_I2C_Master_Receive_DMA(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
cs
사용예는 다음과 같습니다.
1
HAL_I2C_Master_Receive_DMA(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_rd_buff,8);
cs


기본 IT 함수의 사용 방법은 다음과 같습니다.
송신시, HAL_I2C_Master_Transmit_IT() 함수를 사용합니다.
함수 프로토타입은 다음과 같습니다.
1
HAL_StatusTypeDef HAL_I2C_Master_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
cs
사용예는 다음과 같습니다.
1
HAL_I2C_Master_Transmit_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_wr_buff,1);
cs

수신시, HAL_I2C_Master_Receive_IT() 함수를 사용합니다.
함수 프로토타입은 다음과 같습니다.
1
HAL_StatusTypeDef HAL_I2C_Master_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size)
cs
사용예는 다음과 같습니다.
1
HAL_I2C_Master_Receive_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_rd_buff,8);
cs


Sequential IT 함수의 사용 방법은 다음과 같습니다.
송신시, HAL_I2C_Master_Sequential_Transmit_IT() 함수를 사용합니다.
함수 프로토타입은 다음과 같습니다.
1
HAL_StatusTypeDef HAL_I2C_Master_Sequential_Transmit_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions)
cs
사용예는 다음과 같습니다.
1
HAL_I2C_Master_Sequential_Transmit_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_wr_buff,1,I2C_FIRST_FRAME);
cs

송신시, HAL_I2C_Master_Sequential_Receive_IT() 함수를 사용합니다.
함수 프로토타입은 다음과 같습니다.
1
HAL_StatusTypeDef HAL_I2C_Master_Sequential_Receive_IT(I2C_HandleTypeDef *hi2c, uint16_t DevAddress, uint8_t *pData, uint16_t Size, uint32_t XferOptions)
cs
사용예는 다음과 같습니다.
1
HAL_I2C_Master_Sequential_Receive_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_rd_buff,8,I2C_LAST_FRAME);
cs

Sequential IT 함수에서 좀 설명할게 있는데, 함수의 마지막 입력 파라메터인 XferOptions 가 이 함수의 특징을 좌우합니다.
이 변수의 정의는 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/** @defgroup I2C_XferOptions_definition I2C XferOptions definition
  * @{
  */
#define  I2C_FIRST_FRAME                0x00000001U
#define  I2C_NEXT_FRAME                 0x00000002U
#define  I2C_FIRST_AND_LAST_FRAME       0x00000004U
#define  I2C_LAST_FRAME                 0x00000008U
 
/*
(+) A specific option field manage the different steps of a sequential transfer
(+) Option field values are defined through @ref I2C_XFEROPTIONS and are listed below:
(++) I2C_FIRST_AND_LAST_FRAME: No sequential usage, functionnal is same as associated interfaces in no sequential mode 
(++) I2C_FIRST_FRAME: Sequential usage, this option allow to manage a sequence with start condition, address
                      and data to transfer without a final stop condition
(++) I2C_NEXT_FRAME: Sequential usage, this option allow to manage a sequence with a restart condition, address
                     and with new data to transfer if the direction change or manage only the new data to transfer
                     if no direction change and without a final stop condition in both cases
(++) I2C_LAST_FRAME: Sequential usage, this option allow to manage a sequance with a restart condition, address
                     and with new data to transfer if the direction change or manage only the new data to transfer
                     if no direction change and with a final stop condition in both cases
*/
cs

I2C_FIRST_AND_LAST_FRAME은 시퀀스 없이 사용하는 것과 마찮가지란 말입니다. 그냥 일반 IT 함수죠.
I2C_FIRST_FRAME은 처음 일반 IT 처럼 시작하지만, 마지막에 STOP 이 아닌 상태로 됩니다.
I2C_NEXT_FRAME은 중간에 Restart 상태로 시작하고, 마지막에 STOP 이 아닌상태로 됩니다.
I2C_LAST_FRAME은 중간에 Restart 상태로 시작하고, 마지막에 STOP 상태로 됩니다.


그래서 저는 보안칩 메뉴얼 상, 수신시 패킷 구조가 1번 전송하고 NO STOP 으로 중간에 읽고 끝내기 위해서,
HAL_I2C_Master_Sequential_Transmit_IT() 함수는 I2C_FIRST_FRAME ,
HAL_I2C_Master_Sequential_Receive_IT() 함수는 I2C_LAST_FRAME으로 사용했습니다.
즉 다음과 같은 예처럼 사용했습니다.
아래 내용을 참고하실 때, // wait for Tx Callbackfunction process이 있는데, 
저는 귀찮아서 HAL_Delay()를 써버렸는데, Ready 를 체크한다던지, 콜백함수로 부터 플레그 변수가 변하는 것을 감지해서
완료가 된는지를 검사하셔야 합니다. (그냥 똑같이 주석만 처리하시까봐 알려 드립니다)
저는 테스트 용도로 코드를 만들었기 때문에 HAL_Delay(10); 을 넣었었습니다. ^^ 
1
2
3
4
HAL_I2C_Master_Sequential_Transmit_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_wr_buff,1,I2C_FIRST_FRAME);
// wait for Tx Callbackfunction process
HAL_I2C_Master_Sequential_Receive_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_rd_buff,8,I2C_LAST_FRAME);
// wait for Rx Callbackfunction process
cs

그런데, 나중에 테스트 해보니까.. 중간 상태가 반드시 NO STOP 이 아니라, STOP 이어도 무방했습니다.
그래서 DMA 와 일반 IT 함수를 사용해도 상관이 없었습니다. 아휴.. 회사 일이 좀 늦어졌고, 업무상 필요없는 짓을 한거죠.
그래도 앞으로 쓸 일이 있겠죠. 뭐. ^^
보안 칩에서, 위의 예와 같은 동작을 하는 DMA 코드는 다음과 같습니다.
1
2
3
4
HAL_I2C_Master_Transmit_DMA(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_wr_buff,1);
// wait for Tx Callbackfunction process
HAL_I2C_Master_Receive_DMA(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_rd_buff,8);
// wait for Rx Callbackfunction process
cs
보안 칩에서, 위의 예와 같은 동작을 하는 IT 코드는 다음과 같습니다.
1
2
3
4
HAL_I2C_Master_Transmit_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_wr_buff,1);
// wait for Tx Callbackfunction process
HAL_I2C_Master_Receive_IT(&hi2c1,(uint16_t)(SECU_SL_ADD<<1),i2c_rd_buff,8);
// wait for Rx Callbackfunction process
cs


그리고 인터럽트는 (송/수신이 완료된 시점에서) 일반 IT 함수,시퀀셜 IT 함수DMA 함수 모두 걸리고,
송/수신 완료시 실행되는 콜벡함수는 다음과 같습니다. stm32f4xx_hal_i2c.c 파일 안에 __weak 으로 되어 있는데,
main.c 에다 카피한 후, __weak 지우고 인터럽트에 의해 콜되었을 때 실행할 코드를 넣어 주면 됩니다.
1
2
3
4
5
6
7
8
9
10
11
// Tx INT Complete
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
// insert user code
}
 
// Rx INT Complete
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
// insert user code
}
cs


이번에 사용하는 보안 칩이 I2C로 되어 있어서, I2C 통신에 대해서 써 보겠습니다.


예전에 적은 글이 너무 허접해서, 다른분들이 봐도 도움이 안될 듯 해서 다시 한번 써 봅니다.

회사에 아주 많이 굴러다니는 F4 개발 키트중에 골라 잡으니, STM32F411(NUCLEO-F411RE) 이네요.
일단 요놈에다가 I2C 로 보안칩을 연결했습니다.

다음은 STM32F411 NUCLEO 보드의 핀팹을 CUBEMX에서 설정한 내용입니다.


 
다음은 NUCLEO 보드의 I2C 포트의 위치를 표시한 그림입니다. 개발 보드에서 최대한 모여있는 위치로 , CUBEMX 에서 핀을 선택하고 이동했습니다. UART2 는 USB-to-UART 로 NUCLEO 보드와 연결되어 있어서 디버깅 하기가 좋습니다. ^^





이렇게 I2C 에 보안칩을 연결해 봤습니다. 작아서 , 큰 선들을 연결하기 힘들었습니다.




보안칩 핀 넘버 .





다음은 CUBEMX 의 CLK 설정입니다. NUCLEO 보드의 입력 클럭이 8MHz 이므로, HSE 를 선택하고, 최종 HCLK는 100MHz





이제 I2C 설정을 해 보겠습니다. configuration 에서 I2C1을 클릭.





I2C1 설정 중, Parameter Setting 에서 수정한 것은 Clock speed 입니다. 보안 칩이 400KHz 까지 지원을 한다는데, 
오실로 스코프 파형이 별로라서 50KHz로 설정. ^^





다음은 DMA를 설정 합니다.
I2C1_TX , I2C1_RX 둘 다 선택합니다.




다음은 인터럽트설정 입니다. 여기서 주의할 점은 , I2C1_event interrupt 와 I2C1_error interrupt 2개도 선택해 줘야,
나중에 s/w 에서 DMA 함수가 동작합니다. 왜냐하면, DMA 함수에서 인터럽트 함수를 쓰고 있습니다.





다음은, GPIO 설정입니다. 포트를 PULL UP 해 줍니다. 이렇게 하면, 따로 풀업 저항이 필요 없습니다.




다음은 인터럽트를 설정해 보겠습니다. NVIC 를 클릭 합니다.





인터럽트 설정의 우선 순위를 정합니다. 아직 아무엏게나 번호를 붙이고 있습니다. 
나중에 된통 당해봐야 , 어떻게 번호를 붙이는 줄 알 것 같은데.. 막 나눠 주는 중입니다. ^^





마지막으로 프로젝트 설정입니다. 그냥 저장 위치하고, 프로젝트 이름, 컴파일러 종류 등을 설정해 줍니다.




이렇게 H/W 와 I2C DMA 프로그램의 설정은 완료 되었고, 글이 길어지니까.. I2C S/W 테스트는 다음에 올리겠습니다.


오늘 보안관련 칩 회사에서 I2C I/F 로 된 IC를 받았는데,
I2C Address 도 보안인가 봅니다. ㅠㅠ

저한테는 분명히 Device Address 가 0x7A 라고 해서, 아무리 해도 안되서,
보안(이게 보안이냐?, Device Address 를 찾아내는 작업)을 뚫어 봤습니다. ^^

원리는 I2C 칩은 항상 Device Address 를 보내서 해당 Address가 맞으면,  ACK를 보냅니다.
STM32Fx 의 I2C Tx 인터럽트는 
ACK를 받으면    인터럽트 콜백 함수를 호출하고,
ACK를 못 받으면 인터럽트 콜백 함수를 호출하지 않습니다.
그리고 주소는 7비트이므로, 0x00~0x7F 범위에 있습니다.

이 원리를 이용해서, 0x00~0x7F 범위의 Device Address를 1개씩 보내고 조금 기다리다 ACK 안오면,
다시 Device Address를 1개씩 보내고를 반복 하다보면 ACK가 오는 경우가 바로 이 칩의 Device Address 입니다.

다음 번에 I2C 에 대해서 다시 자세히 설명할 것이라 간단하게 어드레스 찾는 함수 쪽하고 콜백함수만 적어 봅니다.

Tx 콜백 함수 내용
1
2
3
4
5
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    f_add_cmpl = 1;
    printf("Tx compleate~!!\r\n");
}
cs

Device address 찾는 함수.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void I2C_Add_serch(void)
{
    uint8_t i;
    for(i=0;i<0x80;i++)
    {
        f_add_cmpl=0;
        HAL_I2C_Master_Transmit_DMA(&hi2c1,(uint16_t)(i<<1),i2c_wr_buff,1);
        HAL_Delay(10);    
        if (f_add_cmpl == 1)
        {
            printf("Device Address search complete. [%02X]\r\n",i);
            return;
        }
    }
    printf("Device Address search fail.\r\n");
}
cs

이 두개의 함수와 printf() 함수 사용하는 내용 외에는 CUBEMX가 자동으로 만들어 준 것을 썼는데,
몇 줄 안되네요. ^^

이 방법으로 찾은 어드레스는 0x3D 였습니다. 1초도 안걸립니다. ^^





다음은 보안칩 에 잘못된 주소의 응답과 맞게 입력한 주소의 파형입니다.




다음은 전체 소스 입니다. DMA를 사용해 봤습니다. 자세한 내용은 다음에 ..
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
/**
  ******************************************************************************
  * File Name          : main.c
  * Description        : Main program body
  ******************************************************************************
  ** This notice applies to any and all portions of this file
  * that are not between comment pairs USER CODE BEGIN and
  * USER CODE END. Other portions of this file, whether 
  * inserted by the user or by software development tools
  * are owned by their respective copyright owners.
  *
  * COPYRIGHT(c) 2017 STMicroelectronics
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */
 
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f4xx_hal.h"
 
/* USER CODE BEGIN Includes */
#include "string.h"
/* USER CODE END Includes */
 
/* Private variables ---------------------------------------------------------*/
I2C_HandleTypeDef hi2c1;
DMA_HandleTypeDef hdma_i2c1_tx;
DMA_HandleTypeDef hdma_i2c1_rx;
 
UART_HandleTypeDef huart2;
 
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
uint8_t f_add_cmpl=0;
uint8_t i2c_wr_buff[20];
 
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_I2C1_Init(void);
 
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
 
/* USER CODE END PFP */
 
/* USER CODE BEGIN 0 */
#ifdef __GNUC__
 #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
 #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
 
PUTCHAR_PROTOTYPE
{
 HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 11);
 return ch;
}
 
void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    f_add_cmpl = 1;
    printf("Tx compleate~!!\r\n");
}
 
void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
}
 
void I2C_Add_serch(void)
{
    uint8_t i;
    for(i=0;i<0x80;i++)
    {
        f_add_cmpl=0;
        HAL_I2C_Master_Transmit_DMA(&hi2c1,(uint16_t)(i<<1),i2c_wr_buff,1);
        HAL_Delay(10);    
        if (f_add_cmpl == 1)
        {
            printf("Device Address search complete. [%02X]\r\n",i);
            return;
        }
    }
    printf("Device Address search fail.\r\n");
}
/* USER CODE END 0 */
 
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();
 
  /* USER CODE BEGIN Init */
 
  /* USER CODE END Init */
 
  /* Configure the system clock */
  SystemClock_Config();
 
  /* USER CODE BEGIN SysInit */
 
  /* USER CODE END SysInit */
 
  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  MX_DMA_Init();
  MX_USART2_UART_Init();
  MX_I2C1_Init();
 
  /* USER CODE BEGIN 2 */
    I2C_Add_serch();
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while(1)
  {
  /* USER CODE END WHILE */
 
  /* USER CODE BEGIN 3 */
  }
  /* USER CODE END 3 */
 
}
 
/** System Clock Configuration
*/
void SystemClock_Config(void)
{
 
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
    /**Configure the main internal regulator output voltage 
    */
  __HAL_RCC_PWR_CLK_ENABLE();
 
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
  RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
  RCC_OscInitStruct.PLL.PLLM = 4;
  RCC_OscInitStruct.PLL.PLLN = 100;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 4;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_3) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
    /**Configure the Systick interrupt time 
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
 
    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
 
  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 00);
}
 
/* I2C1 init function */
static void MX_I2C1_Init(void)
{
 
  hi2c1.Instance = I2C1;
  hi2c1.Init.ClockSpeed = 50000;
  hi2c1.Init.DutyCycle = I2C_DUTYCYCLE_2;
  hi2c1.Init.OwnAddress1 = 244;
  hi2c1.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
  hi2c1.Init.DualAddressMode = I2C_DUALADDRESS_DISABLE;
  hi2c1.Init.OwnAddress2 = 0;
  hi2c1.Init.GeneralCallMode = I2C_GENERALCALL_DISABLE;
  hi2c1.Init.NoStretchMode = I2C_NOSTRETCH_DISABLE;
  if (HAL_I2C_Init(&hi2c1) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
}
 
/* USART2 init function */
static void MX_USART2_UART_Init(void)
{
 
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    _Error_Handler(__FILE__, __LINE__);
  }
 
}
 
/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA1_Stream0_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream0_IRQn, 10);
  HAL_NVIC_EnableIRQ(DMA1_Stream0_IRQn);
  /* DMA1_Stream1_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 10);
  HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn);
 
}
 
/** Configure pins as 
        * Analog 
        * Input 
        * Output
        * EVENT_OUT
        * EXTI
*/
static void MX_GPIO_Init(void)
{
 
  GPIO_InitTypeDef GPIO_InitStruct;
 
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
  __HAL_RCC_GPIOB_CLK_ENABLE();
 
  /*Configure GPIO pin Output Level */
  HAL_GPIO_WritePin(LD2_GPIO_Port, LD2_Pin, GPIO_PIN_RESET);
 
  /*Configure GPIO pin : B1_Pin */
  GPIO_InitStruct.Pin = B1_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
 
  /*Configure GPIO pin : LD2_Pin */
  GPIO_InitStruct.Pin = LD2_Pin;
  GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
  GPIO_InitStruct.Pull = GPIO_NOPULL;
  GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
  HAL_GPIO_Init(LD2_GPIO_Port, &GPIO_InitStruct);
 
}
 
/* USER CODE BEGIN 4 */
 
/* USER CODE END 4 */
 
/**
  * @brief  This function is executed in case of error occurrence.
  * @param  None
  * @retval None
  */
void _Error_Handler(char * file, int line)
{
  /* USER CODE BEGIN Error_Handler_Debug */
  /* User can add his own implementation to report the HAL error return state */
  while(1
  {
  }
  /* USER CODE END Error_Handler_Debug */ 
}
 
#ifdef USE_FULL_ASSERT
 
/**
   * @brief Reports the name of the source file and the source line number
   * where the assert_param error has occurred.
   * @param file: pointer to the source file name
   * @param line: assert_param error line source number
   * @retval None
   */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
    ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
 
}
 
#endif
 
/**
  * @}
  */ 
 
/**
  * @}
*/ 
 
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
cs

i2c_test.ioc


http://www.muhittinkaplan.com/?p=729











DAC DMA 2CH Sine wave generator "main.c" 코드 내용.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
/**
  ******************************************************************************
  * File Name          : main.c
  * Description        : Main program body
  ******************************************************************************
  *
  * COPYRIGHT(c) 2017 STMicroelectronics
  *
  * Redistribution and use in source and binary forms, with or without modification,
  * are permitted provided that the following conditions are met:
  *   1. Redistributions of source code must retain the above copyright notice,
  *      this list of conditions and the following disclaimer.
  *   2. Redistributions in binary form must reproduce the above copyright notice,
  *      this list of conditions and the following disclaimer in the documentation
  *      and/or other materials provided with the distribution.
  *   3. Neither the name of STMicroelectronics nor the names of its contributors
  *      may be used to endorse or promote products derived from this software
  *      without specific prior written permission.
  *
  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32f4xx_hal.h"
 
/* USER CODE BEGIN Includes */
 
/* USER CODE END Includes */
 
/* Private variables ---------------------------------------------------------*/
DAC_HandleTypeDef hdac;
DMA_HandleTypeDef hdma_dac1;
DMA_HandleTypeDef hdma_dac2;
 
TIM_HandleTypeDef htim2;
 
UART_HandleTypeDef huart2;
 
/* USER CODE BEGIN PV */
/* Private variables ---------------------------------------------------------*/
 
/* USER CODE END PV */
 
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
void Error_Handler(void);
static void MX_GPIO_Init(void);
static void MX_DMA_Init(void);
static void MX_DAC_Init(void);
static void MX_TIM2_Init(void);
static void MX_USART2_UART_Init(void);
                                    
void HAL_TIM_MspPostInit(TIM_HandleTypeDef *htim);
                                
 
/* USER CODE BEGIN PFP */
/* Private function prototypes -----------------------------------------------*/
 
/* USER CODE END PFP */
 
/* USER CODE BEGIN 0 */
#ifdef __GNUC__
 #define PUTCHAR_PROTOTYPE int __io_putchar(int ch)
#else
 #define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)
#endif
 
PUTCHAR_PROTOTYPE
{
 HAL_UART_Transmit(&huart2, (uint8_t *)&ch, 11);
 return ch;
}
 
float sine_rate[32= {0-0.195-0.383-0.555-0.707-0.831,
                                            -0.924-0.981-1-0.981-0.924-0.831
                                            -0.707-0.555-0.383-0.195,
                                             00.1950.3830.5550.7070.831,
                                             0.924,  0.981,  1,  0.981,  0.924,  0.831,
                                             0.707,  0.555,  0.383,  0.195};
 
uint16_t sine_wave_array[32];/* = {2047, 1648, 1264, 910, 600,  345,
                   156, 39,  0,  39,  156,  345,
                   600, 910, 1264, 1648, 2048, 2447,
                   2831, 3185, 3495, 3750, 3939, 4056,
                   4095, 4056, 3939, 3750, 3495, 3185,
                   2831, 2447};*/
 
uint16_t sine_wave_array2[32];/* = {2047, 1648, 1264, 910, 600,  345,
                   156, 39,  0,  39,  156,  345,
                   600, 910, 1264, 1648, 2048, 2447,
                   2831, 3185, 3495, 3750, 3939, 4056,
                   4095, 4056, 3939, 3750, 3495, 3185,
                   2831, 2447};*/
 
/* USER CODE END 0 */
 
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_DMA_Init();
  MX_DAC_Init();
  MX_TIM2_Init();
  MX_USART2_UART_Init();
 
  /* USER CODE BEGIN 2 */
    uint32_t i,shift_phase;
    uint16_t offset_CH1,offset_CH2,period_Hz;
    float amplitude_CH1,amplitude_CH2;
    
    amplitude_CH1 = 1000// 1.5V(1861) , 1.0V(1241) , 0.5V(620) , 0.2(248)
    offset_CH1 = 1861;    // 1.5V
    
    amplitude_CH2 = 300// 1.5V(1861) , 1.0V(1241) , 0.5V(620) , 0.2(248)
    offset_CH2 = 1861;    // 1.5V
    
    period_Hz = 60;    // 60Hz
    
    
  htim2.Init.Period = (uint32_t)(1000000.0 / ((float)period_Hz*32.0));
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
 
    shift_phase = 4;
    for (i=0;i<32;i++)
    {
        sine_wave_array[i] = offset_CH1+(int16_t)(amplitude_CH1*sine_rate[i]);
        sine_wave_array2[i] =  offset_CH2+(int16_t)(amplitude_CH2*sine_rate[(i+shift_phase)%32]);
    }
    
    for(i=0;i<32;i++)
    {
        printf("[%2d] sine rate %4d, [%4.3f]\r\n",i,((int16_t)sine_wave_array[i]-2047),((float)((int16_t)sine_wave_array[i]-2047))/2047);
        //printf("[%2d] sine rate %4d\r\n",i,((int16_t)sine_wave_array[i]-2047));
    }
    
    HAL_TIM_Base_Start(&htim2);
    HAL_TIM_OC_Start(&htim2,TIM_CHANNEL_1);
  //  HAL_DAC_Start(&hdac,DAC1_CHANNEL_1);
    HAL_DAC_Start_DMA(&hdac,DAC_CHANNEL_1,(uint32_t *)sine_wave_array,32,DAC_ALIGN_12B_R);
    HAL_DAC_Start_DMA(&hdac,DAC_CHANNEL_2,(uint32_t *)sine_wave_array2,32,DAC_ALIGN_12B_R);
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
      HAL_GPIO_TogglePin(GPIOD,GPIO_PIN_12);
      HAL_Delay(100);
    
  /* USER CODE END WHILE */
 
  /* USER CODE BEGIN 3 */
 
  }
  /* USER CODE END 3 */
 
}
 
/** System Clock Configuration
*/
void SystemClock_Config(void)
{
 
  RCC_OscInitTypeDef RCC_OscInitStruct;
  RCC_ClkInitTypeDef RCC_ClkInitStruct;
 
    /**Configure the main internal regulator output voltage 
    */
  __HAL_RCC_PWR_CLK_ENABLE();
 
  __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1);
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
  RCC_OscInitStruct.HSIState = RCC_HSI_ON;
  RCC_OscInitStruct.HSICalibrationValue = 16;
  RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
  RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
  RCC_OscInitStruct.PLL.PLLM = 8;
  RCC_OscInitStruct.PLL.PLLN = 180;
  RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2;
  RCC_OscInitStruct.PLL.PLLQ = 2;
  RCC_OscInitStruct.PLL.PLLR = 2;
  if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
  {
    Error_Handler();
  }
 
    /**Activate the Over-Drive mode 
    */
  if (HAL_PWREx_EnableOverDrive() != HAL_OK)
  {
    Error_Handler();
  }
 
    /**Initializes the CPU, AHB and APB busses clocks 
    */
  RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                              |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
  RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
  RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
  RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4;
  RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2;
 
  if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK)
  {
    Error_Handler();
  }
 
    /**Configure the Systick interrupt time 
    */
  HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
 
    /**Configure the Systick 
    */
  HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
 
  /* SysTick_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(SysTick_IRQn, 00);
}
 
/* DAC init function */
static void MX_DAC_Init(void)
{
 
  DAC_ChannelConfTypeDef sConfig;
 
    /**DAC Initialization 
    */
  hdac.Instance = DAC;
  if (HAL_DAC_Init(&hdac) != HAL_OK)
  {
    Error_Handler();
  }
 
    /**DAC channel OUT1 config 
    */
  sConfig.DAC_Trigger = DAC_TRIGGER_T2_TRGO;
  sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
  if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
 
    /**DAC channel OUT2 config 
    */
  if (HAL_DAC_ConfigChannel(&hdac, &sConfig, DAC_CHANNEL_2) != HAL_OK)
  {
    Error_Handler();
  }
 
}
 
/* TIM2 init function */
static void MX_TIM2_Init(void)
{
 
  TIM_ClockConfigTypeDef sClockSourceConfig;
  TIM_MasterConfigTypeDef sMasterConfig;
  TIM_OC_InitTypeDef sConfigOC;
 
  htim2.Instance = TIM2;
  htim2.Init.Prescaler = 90;
  htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
  htim2.Init.Period = 1000;
  htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
  if (HAL_TIM_Base_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
 
  sClockSourceConfig.ClockSource = TIM_CLOCKSOURCE_INTERNAL;
  if (HAL_TIM_ConfigClockSource(&htim2, &sClockSourceConfig) != HAL_OK)
  {
    Error_Handler();
  }
 
  if (HAL_TIM_PWM_Init(&htim2) != HAL_OK)
  {
    Error_Handler();
  }
 
  sMasterConfig.MasterOutputTrigger = TIM_TRGO_UPDATE;
  sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_ENABLE;
  if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK)
  {
    Error_Handler();
  }
 
  sConfigOC.OCMode = TIM_OCMODE_PWM1;
  sConfigOC.Pulse = 50;
  sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
  sConfigOC.OCFastMode = TIM_OCFAST_DISABLE;
  if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK)
  {
    Error_Handler();
  }
 
  HAL_TIM_MspPostInit(&htim2);
 
}
 
/* USART2 init function */
static void MX_USART2_UART_Init(void)
{
 
  huart2.Instance = USART2;
  huart2.Init.BaudRate = 115200;
  huart2.Init.WordLength = UART_WORDLENGTH_8B;
  huart2.Init.StopBits = UART_STOPBITS_1;
  huart2.Init.Parity = UART_PARITY_NONE;
  huart2.Init.Mode = UART_MODE_TX_RX;
  huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
  huart2.Init.OverSampling = UART_OVERSAMPLING_16;
  if (HAL_UART_Init(&huart2) != HAL_OK)
  {
    Error_Handler();
  }
 
}
 
/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA1_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA1_Stream5_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream5_IRQn, 30);
  HAL_NVIC_EnableIRQ(DMA1_Stream5_IRQn);
  /* DMA1_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 30);
  HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn);
 
}
 
/** Configure pins as 
        * Analog 
        * Input 
        * Output
        * EVENT_OUT
        * EXTI
*/
static void MX_GPIO_Init(void)
{
 
  /* GPIO Ports Clock Enable */
  __HAL_RCC_GPIOC_CLK_ENABLE();
  __HAL_RCC_GPIOH_CLK_ENABLE();
  __HAL_RCC_GPIOA_CLK_ENABLE();
 
}
 
/* USER CODE BEGIN 4 */
 
/* USER CODE END 4 */
 
/**
  * @brief  This function is executed in case of error occurrence.
  * @param  None
  * @retval None
  */
void Error_Handler(void)
{
  /* USER CODE BEGIN Error_Handler */
  /* User can add his own implementation to report the HAL error return state */
  while(1
  {
  }
  /* USER CODE END Error_Handler */ 
}
 
#ifdef USE_FULL_ASSERT
 
/**
   * @brief Reports the name of the source file and the source line number
   * where the assert_param error has occurred.
   * @param file: pointer to the source file name
   * @param line: assert_param error line source number
   * @retval None
   */
void assert_failed(uint8_t* file, uint32_t line)
{
  /* USER CODE BEGIN 6 */
  /* User can add his own implementation to report the file name and line number,
    ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
  /* USER CODE END 6 */
 
}
 
#endif
 
/**
  * @}
  */ 
 
/**
  * @}
*/ 
 
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/
 
cs


dac_dma_sig_gen.zip


저도 예전부터 고민하던 내용인데,

UART 는 TX는 고민 거리가 아니죠. 
내보낼 데이터 개수는 미리 알 수 있어서 DMA로 보내면 CPU에 무리를 안주고 보낼 수 있으니까요.

그런데, 다양한 프로토콜이 있어서 길이가 재각각이거나, 데이터가 깨져서 길이(데이터 개수)가 틀려질 경우,
DMA 로 수신시에 받을 데이터의 개수를 알 수 없죠.
DMA는 미리 '몇 개 받겠다' 라고 선언하고 실행하도록 되어 있습니다. - 덜 받거나, 더 받으면 문제가 생기죠.

이와 같은 부분을 해결하는 솔루션이 다음 링크에 나와 있습니다.
시간이 안되서 못 해보는데, 사용할 생각 입니다. ^^ 


STM32F411 에서는 SDIO DMA 로 SD-Card가 잘 읽혔습니다.


방법이 어렵지 않아서 금방 테스트가 끝났는데, STM32F446 에서는 DMA를 사용하면 f_mount() 함수에서 멈춰버렸습니다.
DMA를 사용할 경우 sd_diskio.c 의 SD_read 함수 내의 
BSP_SD_ReadBlocks() 함수를 BSP_SD_ReadBlocks_DMA() 함수로 바꿔주면 됩니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
  * @brief  Reads Sector(s)
  * @param  lun : not used
  * @param  *buff: Data buffer to store read data
  * @param  sector: Sector address (LBA)
  * @param  count: Number of sectors to read (1..128)
  * @retval DRESULT: Operation result
  */
DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count)
{
  DRESULT res = RES_OK;
  /*if(BSP_SD_ReadBlocks((uint32_t*)buff, 
                       (uint64_t) (sector * BLOCK_SIZE), 
                       BLOCK_SIZE, 
                       count) != MSD_OK)*/
  if(BSP_SD_ReadBlocks_DMA((uint32_t*)buff, 
                       (uint64_t) (sector * BLOCK_SIZE), 
                       BLOCK_SIZE, 
                       count) != MSD_OK)
  {
    res = RES_ERROR;
  }
  
  return res;
}
 
/**
  * @brief  Writes Sector(s)
  * @param  lun : not used
  * @param  *buff: Data to be written
  * @param  sector: Sector address (LBA)
  * @param  count: Number of sectors to write (1..128)
  * @retval DRESULT: Operation result
  */
#if _USE_WRITE == 1
DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count)
{
  DRESULT res = RES_OK;
  /*if(BSP_SD_WriteBlocks((uint32_t*)buff, 
                        (uint64_t)(sector * BLOCK_SIZE), 
                        BLOCK_SIZE, count) != MSD_OK)*/
  if(BSP_SD_WriteBlocks_DMA((uint32_t*)buff, 
                        (uint64_t)(sector * BLOCK_SIZE), 
                        BLOCK_SIZE, count) != MSD_OK)
  {
    res = RES_ERROR;
  }
  
  return res;
}
cs


STM32F446 Nucleo 보드에서 DMA를 사용하지 않으면 동작은 잘 되는데, 
가끔 Error이 발생해서 SD-Card 와의 선을 짧게 하니 잘 동작 했습니다.
보드의 아트웍 영향인건지는 확실히 모르겠는데, 배선도 좀 짧으면 좋은 것 같습니다.



이 상태로 13KByte 의 데이터를 읽는데 10ms 정도의 시간이 걸렸으니 
거의 1MB/sec 의 속도밖에 안나와서 SDIO를 왜 쓰나 싶더군요.

그래서 처음부터 다시 프로젝트를 만들어서 동작을 시켜봤는데, 역시나 결과는 DMA 기능은 구현이 안되네요.

그런데, 뭔가 틀려진 점이 있는데, DMA를 안 썼을 때 13KB를 읽는데 10ms 걸렸던 것이 2ms 로 대폭 줄었습니다.
24KB를 읽었더니 3ms 고요. 마치 DMA가 동작되는 듯이 느껴질 정도로 빨라졌지만 DMA는 동작되지 않는 상황.
왜 빨라졌는지 원인은 못 찾았지만 CUBEMX 툴이 아직 안정화가 덜 됐나 봅니다.

빨라진 이유는 제가 코드를 잘 못 만들어서 그렇네요. f_read()함수가 512 Byte씩 읽어야 되는 줄 알고 24K 파일을 
512Byte 단위로 f_read()함수를 이용해 여러번 나눠서 읽었더니 느리고 한번에 24KB를 다 읽으니 빠르네요.
예전에 테스트를 했을 때, 512-Byte 이상 읽으면 안 읽혔었는데 이번에 해 보니 한번에 많은 데이터도 읽기 가능하네요. 

[512 Byte 씩 여러번 읽어서 느렸던 코드내용]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
uint8_t read_f_dat(uint8_t *pf_dat,FIL* fp)
{
    UINT i,testBytes;
    uint32_t f_size;
 
    f_size = 13000;
 
    // #define _MAX_SS 512
    
    for (i=0;i<f_size;i+=_MAX_SS)
    {
        if ((f_size - i) < _MAX_SS)
        {
            res = f_read(fp, &pf_dat[i],(f_size - i), &testBytes);
        }
        else
        {
            res = f_read(fp, &pf_dat[i],_MAX_SS, &testBytes);
        }
    }
}
cs

[한번에 많은 데이터를 읽어서 빨라지도록 수정된 코드]
1
2
3
4
5
6
7
8
9
uint8_t read_f_dat(uint8_t *pf_dat,FIL* fp)
{
    UINT testBytes;    
    uint32_t f_size;
 
    f_size = 13000;
    
    res = f_read(fp, &pf_dat[0],f_size, &testBytes);
}
cs

아뭏든 현재 첨부한 프로젝트는 DMA는 동작이 안되는 것으로 보이고, 계속해서 방법을 찾아보다가 
잘 되면 DMA SDIO-4bit 관련 글을 다시 올려 보겠습니다.


다음은 Nucleo - STM32F446 보드에서 DMA가 안되서 새로 프로젝트를 만들었던 과정입니다.

1. 먼저 MCU를 STM32F446로 선택하고, SDIO,UART2,GPIO 1개 출력(오실로 스코프로 시간이 얼마나 걸리는지 체크를 위해)



2. SDIO 신호와 u-SD-Card를 점퍼선으로 연결.



3. SDIO 를 DMA 로 동작하도록 CUBEMX 에서 설정.



4. sd_diskio.c 파일에서 SD_read/SD_write 함수 안의 BSP_SD_ReadBlocks/BSP_SD_WriteBlocks 함수를 바꾸지 않고 그대로 사용했는데 24KB 읽는데 걸리는 시간이 3ms 걸렸음.



5. 혹시 다 읽힌 것 맞는지 알아보려고 해리포터 소설을 UART로 뿌려보니 제대로 읽혀 있었습니다.
DMA가 동작한 것으로 믿어야겠죠? (제가 미쳤나 봅니다. 이런 상황을 가지고 DMA가 된다고 생각했다니.. 사기꾼이 될 뻔한 것을 글을 다시 보고 수정하면서 정정합니다.)
SDIO는 많은 양을 읽을수록 속도가 점점 더 빨라지는 듯 합니다.

그리고 STM32F411 은 42MHz 로도 DMA가 동작하는데, STM32F446 은 45MHz로 일반 방식에서는 동작하지 않았습니다.
(차후 DMA 구현되면 다시 비교해 볼 예정)

[STM32F4xx 에서 SDIO CLK를 최대(411:42MHz,446:45MHz)로 설정한 코드 내용]
1
2
3
4
5
6
7
8
9
10
11
12
/* SDIO init function */
static void MX_SDIO_SD_Init(void)
{
 
  hsd.Instance = SDIO;
  hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
  hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_ENABLE;
  hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
  hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
  hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
  hsd.Init.ClockDiv = 0;
}
cs

STM32F446 이 DMA가 안된 상태에서 비교를 하니 의미가 많이 없지만, 
또한 411은 SDIO_CLK=42MHz 이고 446은 22.5MHz 이기때문에, 
이 상태에서는 (당연하게도)STM32F411 이 더 빨랐습니다. 

STM32F446(SDIO_CLK:22.5MHz)은 13KB 읽는데 2ms, 24KB 3ms가 걸렸는데, 
STM32F411(SDIO_CLK:42MHz) 은 13KB/1.4ms , 24KB/2ms 밖에 안걸립니다. 

24KB 읽는 것을 기준으로 STM32F411 은 최대 속도 12MB/s , STM32F446 은 최대속도 8MB/s 나오네요.

이 속도는 앞서서도 말씀 드렸듯이 한번에 많이 읽으면 더 빨라집니다.

6. 해리포터 소설을 24KB 만큼 읽어서 출력해 봄.



7. main() 주요 동작 코드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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_DMA_Init();
  MX_SDIO_SD_Init();
  MX_USART2_UART_Init();
  MX_FATFS_Init();
 
  /* USER CODE BEGIN 2 */
    read_TAR_file_DMA();
  /* USER CODE END 2 */
 
  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
  while (1)
  {
  /* USER CODE END WHILE */
 
  /* USER CODE BEGIN 3 */
 
  }
  /* USER CODE END 3 */
 
}
 
/* SDIO init function */
static void MX_SDIO_SD_Init(void)
{
 
  hsd.Instance = SDIO;
  hsd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING;
  hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE;
  //hsd.Init.ClockBypass = SDIO_CLOCK_BYPASS_ENABLE;
  hsd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_DISABLE;
  hsd.Init.BusWide = SDIO_BUS_WIDE_1B;
  hsd.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;
  hsd.Init.ClockDiv = 0;
 
}
 
/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA2_Stream3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 10);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 20);
  HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);
 
}
 
/* USER CODE BEGIN 4 */
void read_TAR_file_DMA(void)
{
     #if _USE_LFN
    TCHAR lfn[_MAX_LFN + 1];
    fno_t.lfname = lfn;
    fno_t.lfsize = sizeof lfn;
    #endif
    
    BYTE testBuffer[24000];
   uint8_t path[13];            
 
    DWORD cnt_rd_Byte=24000;
  UINT testBytes;
    DWORD cnt_i;
    UINT i,cnt_512;
    uint32_t image_offset;
 
 
    //sprintf(path,"1.tar");
    sprintf(path,"aaa.txt");
    printf("%s\n\r",path);
    
    res_t = f_mount(&fs32_t,SD_Path,1);
    printf("SD Mount : res f_mount : %02X\n\r",res_t);
    res_t = f_open(&fil_t, (char*)path, FA_READ); 
    printf("res f_open : %02X, fil_t.fsize : %d\n\r",res_t,fil_t.fsize);
 
    printf("Read Txt File Test\n\r");
    
    GPIOB->ODR ^= GPIO_PIN_5;    
    res_t = f_read(&fil_t, testBuffer, cnt_rd_Byte, &testBytes);
    GPIOB->ODR ^= GPIO_PIN_5;    
    printf("res_t = f_read : %02X\n\r",res_t);
    
    for(i=0;i<24000;i++)
        printf("%c",testBuffer[i]);
}
 
/* USER CODE END 4 */
 
cs



추가로 DMA 인터럽트도 DMA 동작이 안되는 것과 관련이 있을 수도 있다 싶어서 인터럽트 설정 부분도 올려 봅니다.
혹시 왜 DMA가 안되는지 아시는분 알려주시면 대단히 고맙겠습니다.

아래와 같이 인터럽트를 여러가지로 변경해 봤는데, DMA 동작은 여전히 안됐습니다. 
그런데 DMA를 사용하지 않아도 SDIO_CLK 22.5MHz의 속도로써 늦은 속도는 아닌 것 같습니다.
(STM32F411 SDIO_CLK 42MHz(DMA)에서 13KB 전송시 1.4ms 이고, STM32F446 SDIO_CLK 22.5MHz(No DMA) 에서 2ms 걸렸기 때문에)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/** 
  * Enable DMA controller clock
  */
static void MX_DMA_Init(void
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();
 
  /* DMA interrupt init */
  /* DMA2_Stream3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 20);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 30);
  HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);
}
 
static void MX_DMA_Init(void
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();
  /* DMA interrupt init */
  /* DMA2_Stream3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 10);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 20);
  HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);
}
 
static void MX_DMA_Init(void
{
  /* DMA controller clock enable */
  __HAL_RCC_DMA2_CLK_ENABLE();
  /* DMA interrupt init */
  /* DMA2_Stream3_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 00);
  HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
  /* DMA2_Stream6_IRQn interrupt configuration */
  HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 00);
  HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);
}
cs



다음의 소스코드 첨부합니다.

sdio_pure_v1.zip


[이 프로젝트에 사용된 MCU는 NUCLEO-F446RE(STM32F446RET6) 입니다.]


제가 지금까지 타이머 인터럽트를 안다뤘더군요. 

가장 많이 쓰는 기능일텐데, 이제서야 여러 기능들을 합쳐서 프로그램을 짜 넣으려고 하다 보니 빠진 것을 알았습니다.

먼저 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 함수를 호출하도록 되어 있지요.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
  * @brief  Period elapsed callback in non blocking mode 
  * @param  htim: pointer to a TIM_HandleTypeDef structure that contains
  *                the configuration information for TIM module.
  * @retval None
  */
__weak void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* Prevent unused argument(s) compilation warning */
  UNUSED(htim);
  /* NOTE : This function Should not be modified, when the callback is needed,
            the __HAL_TIM_PeriodElapsedCallback could be implemented in the user file
   */
}
cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
  /* Prevent unused argument(s) compilation warning */
    if (htim->Instance == TIM2)
    {
        HAL_GPIO_TogglePin(LED_OUT_GPIO_Port,LED_OUT_Pin);
    }
    else if (htim->Instance == TIM1)
    {
    }
  /* NOTE : This function Should not be modified, when the callback is needed,
            the __HAL_TIM_PeriodElapsedCallback could be implemented in the user file
   */
}
 
 
int main(void)
{
  /* 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_TIM2_Init();
  MX_TIM1_Init();
  MX_USART2_UART_Init();
 
  /* USER CODE BEGIN 2 */
  printf("UART Test...\n\r");
    
  HAL_Delay(500);
  HAL_TIM_Base_Start_IT(&htim2);
    
  while (1)
  {
            HAL_GPIO_TogglePin(LED_OUT_GPIO_Port,LED_OUT_Pin);
  }
 
}
cs


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

그래서 다음과 같이 확인하는 코드가 필요합니다.





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





정확히 10ms 마다 포트가 토글 되네요.
동작 잘 되네요~~^^

다음의 소스 코드를 첨부합니다.


+ Recent posts