쿼드콥터를 만들 때 많은 센서들은 SPI가 아닌 I2C를 사용했다. 그렇기 때문에 많이 사용하지 않은 통신이다.

 

하지만 PID 값을 저장하기 위한 FLASH Memory제어, nrf24l01 통신 모듈을 사용하기 위해서 SPI 통신을 해야 했다.

 

HAL드라이버의 HAL_SPI_Transmit(), HAL_SPI_Receive(), HAL_TransmitReceive() 3개의 함수와 대응되는 3개의 함수를 LL 드라이버로 코드를 작성한 후 W25Q16 FLASH Memory에 데이터를 읽고 쓰는 코드까지 작성해 보겠다.

 

 

SPI 코드 생성 (0)

Parameter Settings
PIN
NRF24L01 operation

SPI Mode는 Full Duplex Master 이며, 데이터를 보내면서 데이터를 받을 수 있는 모드이다.

 

예를 들어 nrf24l01 SPI 통신 과정을 보면 Master에서 Slave로 커맨드를 보낼 때 Slave에서 STATUS register데이터를 Master로 보내 Master가 데이터를 동시에 읽을 수 있다.

 

 

코드 - TXRX (1)

887p

Full Duplex 모드에서 데이터를 쓰고 읽는 과정을 동시에 하는 흐름을 보여준다.

 

데이터시트를 참고하여 코드를 작성했다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void SPI_TransmitReceive(SPI_TypeDef *SPI,uint8_t* output,uint8_t* input, uint16_t size){
    LL_SPI_SetTransferDirection(SPI,LL_SPI_FULL_DUPLEX);
 
    if(!LL_SPI_IsEnabled(SPI)){
        LL_SPI_Enable(SPI);
    }
 
    for(uint16_t i=0;i<size;i++){
        while(!LL_SPI_IsActiveFlag_TXE(SPI));
        LL_SPI_TransmitData8(SPI,output[i]);
 
        while(!LL_SPI_IsActiveFlag_RXNE(SPI));
        input[i]=LL_SPI_ReceiveData8(SPI);
    }
 
    while(LL_SPI_IsActiveFlag_BSY(SPI));
}
 

 

TX와 RX 동시에 주고받을 수 있다.

 

 

코드 - TX ONLY (2)

888p
889p

다른 통신들과 마찬가지로 데이터시트를 분석해서 코드를 작성하기만 하면 된다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void SPI_Transmit(SPI_TypeDef *SPI,uint8_t* output,uint16_t size){
    LL_SPI_SetTransferDirection(SPI,LL_SPI_FULL_DUPLEX);
 
    if(!LL_SPI_IsEnabled(SPI)){
        LL_SPI_Enable(SPI);
    }
 
    for(uint16_t i=0;i<size;i++){
        while(!LL_SPI_IsActiveFlag_TXE(SPI));
        LL_SPI_TransmitData8(SPI,output[i]);
    }
    while(LL_SPI_IsActiveFlag_BSY(SPI));
 
    if(LL_SPI_IsActiveFlag_RXNE(SPI)){
        LL_SPI_ReceiveData8(SPI);
    }
}
 
 

 

Full Duplex 모드에서 데이터를 받지 않고 데이터를 쓰기만 할 때 사용한다.

 

이때 마지막에 SPI_DS Register를 읽어 혹시라도 받은 데이터에 의한 RXNE flag를 RESET시킨다.

 

 

코드 - RX ONLY (3)

 

1
2
3
void SPI_Receive(SPI_TypeDef *SPI,uint8_t* input,uint16_t size){
    SPI_TransmitReceive(SPI,input,input,size);
}
 

 

 

HAL코드를 분석해본 결과 따로 Recieve코드를 만들지 않고 TransmitReceive 함수를 불러 데이터를 받는다.

 

따라서 SPI_TransmitReceive로 Receive 함수를 구현했다.

 

1
2
3
4
5
6
7
8
9
10
11
12
void SPI_Receive(SPI_TypeDef *SPI, uint8_t* input, uint16_t size){
 
    if(!LL_SPI_IsEnabled(SPI)){
            LL_SPI_Enable(SPI);
    }
    LL_SPI_SetTransferDirection(SPI,LL_SPI_SIMPLEX_RX);
    for(uint16_t i=0;i<size;i++){
        while(!LL_SPI_IsActiveFlag_RXNE(SPI));
        input[i]=LL_SPI_ReceiveData8(SPI);
    }
    LL_SPI_SetTransferDirection(SPI,LL_SPI_FULL_DUPLEX);
}
 

 

다른 코드도 작성했다. 하지만 실험 결과 문제점이 존재했는데, Baud Rate가 10MBits/s가 넘어가면 데이터가 밀려 제대로 된 데이터를 얻지 못했다.

 

SPI_Receive() 함수는 첫 번째 코드를 사용하는 것을 추천한다.

 

 

코드 - FLASH MEMORY (4)

spi_flash.c
0.00MB
spi_flash.h
0.00MB
w25q16dv_revi_nov1714_web.pdf
1.31MB

W25Q16

 

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
/* USER CODE BEGIN 2 */
  uint8_t data[10];
 
  printf("start\r\n");
 
  data[0]=0x01;
  data[1]=0x23;
  data[2]=0x45;
  data[3]=0x67;
  data[4]=0x89;
  data[5]=0xaa;
  data[6]=0xbb;
  data[7]=0xcc;
  data[8]=0xdd;
  data[9]=0xee;
  flash_sector_erase(SPI1,0x0000);
  flash_write(SPI1,0x0000,data,10);
 
  data[0]=0xf1;
  data[1]=0x2f;
  data[2]=0xf5;
  data[3]=0x6a;
  data[4]=0x7f;
  data[5]=0x7a;
  data[6]=0xbf;
  data[7]=0x5c;
  data[8]=0x8d;
  data[9]=0xe3;
 
  flash_sector_erase(SPI1,0x1000);
  flash_write(SPI1,0x1000,data,10);
 
  for(int i=0;i<10;i++)
      data[i]=0;
 
  flash_read(SPI1,0x0000,data,10);
  for(int i=0;i<10;i++){
      printf("%.2x ",data[i]);
  }
  printf("\n\n\r");
  for(int i=0;i<10;i++)
        data[i]=0;
 
  flash_read(SPI1,0x1000,data,10);
  for(int i=0;i<10;i++){
      printf("%.2x ",data[i]);
  }
  printf("\n\n\r");
 
  /* USER CODE END 2 */

 

 

현재 사용하고 있는 개발 보드에 장착되어있는 W25Q16 Flash Memory이며 SPI1 핀들과 연결되어 있다.

 

Flash에 데이터를 쓰기 위해서는 해당 섹터를 한번 지워주고 써줘야 한다. 처음 코드를 짰을 때 이 사실을 몰라 몇 시간 동안 헤맨 기억이 난다.

flash_sector_erase()로 섹터를 지우고 flash_write()로 데이터를 쓴 후 두 함수들을 지우고 다시 빌드하여 MCU에 프로그램을 올려도 flash_read() 함수로 데이터를 읽어 값들이 출력된다.

 

작성된 코드를 분석하고 Flash의 데이터시트를 보면서 모르는 부분은 참고하면 될 것이다.

 

 

 

-

헥사콥터를 제작하기 위한 통신 UART, I2C, SPI에 대한 내용이 드디어 끝났다.

SPI 통신은  Flash Memory에 pid값들에 대한 정보를 수정하고 가져오기 위해 많이 사용한다.

또한 nrf24l01 통신 모듈과 통신할 때 사용된다.

인터럽트와 DMA는 실제 코드를 작성할 때 필요하면 추가할 생각이다.

Posted by DDTXRX
,