wtorek, 26 września 2023

STM32H7 - Cordic

W tym poście chciałbym opisać sposób działania cooprocesora Cordic.


Cordic jest to cooprocesor, który wspomaga wykonywanie operacji matematycznych. Z powodzeniem może być wykorzystywany do wykonywania obliczeń na funkcjach trygonometrycznych oraz hiperbolicznych. 

CubeMx:


Konfiguracja CORDIC w CubeMx jest mało skomplikowana. Należy go uruchomić:


I właściwie na tym się kończy jego konfiguracja. Dodatkowo można włączyć przerwania oraz DMA. 

Po wygenerowaniu projektu. Inicjalizacja CORDIC'a wygląda następująco:

  1. static void MX_CORDIC_Init(void)
  2. {
  3.   /* USER CODE BEGIN CORDIC_Init 0 */
  4.   /* USER CODE END CORDIC_Init 0 */
  5.   /* USER CODE BEGIN CORDIC_Init 1 */
  6.   /* USER CODE END CORDIC_Init 1 */
  7.   hcordic.Instance = CORDIC;
  8.   if (HAL_CORDIC_Init(&hcordic) != HAL_OK)
  9.   {
  10.     Error_Handler();
  11.   }
  12.   /* USER CODE BEGIN CORDIC_Init 2 */
  13.   /* USER CODE END CORDIC_Init 2 */
  14. }

Do tego dochodzi konfiguracja DMA:

  1. void HAL_CORDIC_MspInit(CORDIC_HandleTypeDef* hcordic)
  2. {
  3.   if(hcordic->Instance==CORDIC)
  4.   {
  5.   /* USER CODE BEGIN CORDIC_MspInit 0 */
  6.   /* USER CODE END CORDIC_MspInit 0 */
  7.     /* Peripheral clock enable */
  8.     __HAL_RCC_CORDIC_CLK_ENABLE();
  9.  
  10.     /* CORDIC DMA Init */
  11.     /* CORDIC_RD Init */
  12.     hdma_cordic_rd.Instance = DMA1_Stream2;
  13.     hdma_cordic_rd.Init.Request = DMA_REQUEST_CORDIC_READ;
  14.     hdma_cordic_rd.Init.Direction = DMA_PERIPH_TO_MEMORY;
  15.     hdma_cordic_rd.Init.PeriphInc = DMA_PINC_DISABLE;
  16.     hdma_cordic_rd.Init.MemInc = DMA_MINC_ENABLE;
  17.     hdma_cordic_rd.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  18.     hdma_cordic_rd.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  19.     hdma_cordic_rd.Init.Mode = DMA_NORMAL;
  20.     hdma_cordic_rd.Init.Priority = DMA_PRIORITY_LOW;
  21.     hdma_cordic_rd.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  22.     if (HAL_DMA_Init(&hdma_cordic_rd) != HAL_OK)
  23.     {
  24.       Error_Handler();
  25.     }
  26.  
  27.     __HAL_LINKDMA(hcordic,hdmaIn,hdma_cordic_rd);
  28.  
  29.     /* CORDIC_WR Init */
  30.     hdma_cordic_wr.Instance = DMA1_Stream3;
  31.     hdma_cordic_wr.Init.Request = DMA_REQUEST_CORDIC_WRITE;
  32.     hdma_cordic_wr.Init.Direction = DMA_MEMORY_TO_PERIPH;
  33.     hdma_cordic_wr.Init.PeriphInc = DMA_PINC_DISABLE;
  34.     hdma_cordic_wr.Init.MemInc = DMA_MINC_ENABLE;
  35.     hdma_cordic_wr.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  36.     hdma_cordic_wr.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  37.     hdma_cordic_wr.Init.Mode = DMA_NORMAL;
  38.     hdma_cordic_wr.Init.Priority = DMA_PRIORITY_LOW;
  39.     hdma_cordic_wr.Init.FIFOMode = DMA_FIFOMODE_DISABLE;
  40.     if (HAL_DMA_Init(&hdma_cordic_wr) != HAL_OK)
  41.     {
  42.       Error_Handler();
  43.     }
  44.     __HAL_LINKDMA(hcordic,hdmaOut,hdma_cordic_wr);
  45.   /* USER CODE BEGIN CORDIC_MspInit 1 */
  46.   /* USER CODE END CORDIC_MspInit 1 */
  47.   }
  48. }

Powyższe konfigurację pozwalają na skonfigurowanie podstawowych peryferiów. Dalej należy skonfigurować rodzaje obsługiwanych funkcji w strukturze CORDIC_ConfigTypeDef:

  1. typedef struct
  2. {
  3.   uint32_t   Function;     /*!< Function
  4.                                 This parameter can be a value of @ref CORDIC_Function */
  5.  
  6.   uint32_t   Scale;        /*!< Scaling factor
  7.                                 This parameter can be a value of @ref CORDIC_Scale */
  8.  
  9.   uint32_t   InSize;       /*!< Width of input data
  10.                                 This parameter can be a value of @ref CORDIC_In_Size */
  11.  
  12.   uint32_t   OutSize;      /*!< Width of output data
  13.                                 This parameter can be a value of @ref CORDIC_Out_Size */
  14.  
  15.   uint32_t   NbWrite;      /*!< Number of 32-bit write expected for one calculation
  16.                                 This parameter can be a value of @ref CORDIC_Nb_Write */
  17.  
  18.   uint32_t   NbRead;       /*!< Number of 32-bit read expected after one calculation
  19.                                 This parameter can be a value of @ref CORDIC_Nb_Read */
  20.  
  21.   uint32_t   Precision;    /*!< Number of cycles for calculation
  22.                                 This parameter can be a value of @ref CORDIC_Precision_In_Cycles_Number */
  23.  
  24. } CORDIC_ConfigTypeDef;

Pierwszym parametrem jest rodzaj obsługiwanych funkcji:

  1. #define CORDIC_FUNCTION_COSINE      (0x00000000U)                                                          /*!< Cosine */
  2. #define CORDIC_FUNCTION_SINE        ((uint32_t)(CORDIC_CSR_FUNC_0))                                        /*!< Sine */
  3. #define CORDIC_FUNCTION_PHASE       ((uint32_t)(CORDIC_CSR_FUNC_1))                                        /*!< Phase */
  4. #define CORDIC_FUNCTION_MODULUS     ((uint32_t)(CORDIC_CSR_FUNC_1 | CORDIC_CSR_FUNC_0))                    /*!< Modulus */
  5. #define CORDIC_FUNCTION_ARCTANGENT  ((uint32_t)(CORDIC_CSR_FUNC_2))                                        /*!< Arctangent */
  6. #define CORDIC_FUNCTION_HCOSINE     ((uint32_t)(CORDIC_CSR_FUNC_2 | CORDIC_CSR_FUNC_0))                    /*!< Hyperbolic Cosine */
  7. #define CORDIC_FUNCTION_HSINE       ((uint32_t)(CORDIC_CSR_FUNC_2 | CORDIC_CSR_FUNC_1))                    /*!< Hyperbolic Sine */
  8. #define CORDIC_FUNCTION_HARCTANGENT ((uint32_t)(CORDIC_CSR_FUNC_2 | CORDIC_CSR_FUNC_1 | CORDIC_CSR_FUNC_0))/*!< Hyperbolic Arctangent */
  9. #define CORDIC_FUNCTION_NATURALLOG  ((uint32_t)(CORDIC_CSR_FUNC_3))                                        /*!< Natural Logarithm */
  10. #define CORDIC_FUNCTION_SQUAREROOT  ((uint32_t)(CORDIC_CSR_FUNC_3 | CORDIC_CSR_FUNC_0))  

Dostępne są funkcje sin, cos, faza, modulo, arcus tangens, funckje hiperboliczne, algorytm naturalny czy pierwiastek.

Kolejnym parametrem jest skalowanie:

  1. /* Scale factor value 'n' implies that the input data have been multiplied
  2.    by a factor 2exp(-n), and/or the output data need to be multiplied by 2exp(n). */
  3. #define CORDIC_SCALE_0              (0x00000000U)
  4. #define CORDIC_SCALE_1              ((uint32_t)(CORDIC_CSR_SCALE_0))
  5. #define CORDIC_SCALE_2              ((uint32_t)(CORDIC_CSR_SCALE_1))
  6. #define CORDIC_SCALE_3              ((uint32_t)(CORDIC_CSR_SCALE_1 | CORDIC_CSR_SCALE_0))
  7. #define CORDIC_SCALE_4              ((uint32_t)(CORDIC_CSR_SCALE_2))
  8. #define CORDIC_SCALE_5              ((uint32_t)(CORDIC_CSR_SCALE_2 | CORDIC_CSR_SCALE_0))
  9. #define CORDIC_SCALE_6              ((uint32_t)(CORDIC_CSR_SCALE_2 | CORDIC_CSR_SCALE_1))
  10. #define CORDIC_SCALE_7              ((uint32_t)(CORDIC_CSR_SCALE_2 | CORDIC_CSR_SCALE_1 | CORDIC_CSR_SCALE_0))
 
Ten parametr pozwala na zwiększenie zakresu. Wykonuje on przesunięcie wartości w prawo. 

Następnie InSize, czyli rozmiar danych wejściowych:

  1. #define CORDIC_INSIZE_32BITS       (0x00000000U)             /*!< 32 bits input data size (Q1.31 format) */
  2. #define CORDIC_INSIZE_16BITS       CORDIC_CSR_ARGSIZE        /*!< 16 bits input data size (Q1.15 format) */

OutSize, rozmiar danych wyjściowych:

  1. #define CORDIC_OUTSIZE_32BITS      (0x00000000U)             /*!< 32 bits output data size (Q1.31 format) */
  2. #define CORDIC_OUTSIZE_16BITS      CORDIC_CSR_RESSIZE        /*!< 16 bits output data size (Q1.15 format) */

NbWrite, czyli ilość danych odczytanych w formacie 32 bitowym lub 2x16 bitów, drugie ustawienie przechowuje dwa 32 bitowe dane. 

  1. /*!< One 32-bits write containing either only one 32-bit data input (Q1.31 format), or two 16-bit data input (Q1.15 format) packed in one 32 bits Data */
  2. #define CORDIC_NBWRITE_1           (0x00000000U)  
  3.  
  4. /*!< Two 32-bit write containing two 32-bits data input (Q1.31 format) */
  5. #define CORDIC_NBWRITE_2           CORDIC_CSR_NARGS    

NbRead, czyli ilość danych odczytanych w formacie 32 bitowym lub 2x16 bitów, drugie ustawienie przechowuje dwa 32 bitowe dane. 

  1.  /*!< One 32-bits read containing either only one 32-bit data output (Q1.31 format), or two 16-bit data output (Q1.15 format) packed in one 32 bits Data */
  2. #define CORDIC_NBREAD_1            (0x00000000U)
  3.  
  4.  /*!< Two 32-bit Data containing two 32-bits data output (Q1.31 format) */
  5. #define CORDIC_NBREAD_2            CORDIC_CSR_NRES

Precision, czyli dokładność. Ilość cykli jakie cooprocesor będzie wykonywał w celu osiągnięcia wybranej dokładności.

  1. #define CORDIC_PRECISION_1CYCLE
  2. #define CORDIC_PRECISION_2CYCLES
  3. #define CORDIC_PRECISION_3CYCLES
  4. #define CORDIC_PRECISION_4CYCLES
  5. #define CORDIC_PRECISION_5CYCLES
  6. #define CORDIC_PRECISION_6CYCLES
  7. #define CORDIC_PRECISION_7CYCLES
  8. #define CORDIC_PRECISION_8CYCLES
  9. #define CORDIC_PRECISION_9CYCLES
  10. #define CORDIC_PRECISION_10CYCLES
  11. #define CORDIC_PRECISION_11CYCLES
  12. #define CORDIC_PRECISION_12CYCLES
  13. #define CORDIC_PRECISION_13CYCLES
  14. #define CORDIC_PRECISION_14CYCLES
  15. #define CORDIC_PRECISION_15CYCLES

Zwracana parametry z funkcji:

  1. typedef enum
  2. {
  3.   HAL_CORDIC_STATE_RESET     = 0x00U,  /*!< CORDIC not yet initialized or disabled */
  4.   HAL_CORDIC_STATE_READY     = 0x01U,  /*!< CORDIC initialized and ready for use   */
  5.   HAL_CORDIC_STATE_BUSY      = 0x02U,  /*!< CORDIC internal process is ongoing     */
  6.   HAL_CORDIC_STATE_ERROR     = 0x03U   /*!< CORDIC error state                     */
  7. } HAL_CORDIC_StateTypeDef;

Do obsługi, biblioteka HAL udostępnia następujące funkcje:

  1. HAL_StatusTypeDef HAL_CORDIC_Init(CORDIC_HandleTypeDef *hcordic);
  2. HAL_StatusTypeDef HAL_CORDIC_DeInit(CORDIC_HandleTypeDef *hcordic);
  3. //----------------------------------------------------------------------
  4. void HAL_CORDIC_MspInit(CORDIC_HandleTypeDef *hcordic);
  5. void HAL_CORDIC_MspDeInit(CORDIC_HandleTypeDef *hcordic);
  6. //----------------------------------------------------------------------
  7. HAL_StatusTypeDef HAL_CORDIC_Configure(CORDIC_HandleTypeDef *hcordic, const CORDIC_ConfigTypeDef *sConfig);
  8. HAL_StatusTypeDef HAL_CORDIC_Calculate(CORDIC_HandleTypeDef *hcordic, const int32_t *pInBuff, int32_t *pOutBuff, uint32_t NbCalc, uint32_t Timeout);
  9. HAL_StatusTypeDef HAL_CORDIC_CalculateZO(CORDIC_HandleTypeDef *hcordic, const int32_t *pInBuff, int32_t *pOutBuff, uint32_t NbCalc, uint32_t Timeout);
  10. HAL_StatusTypeDef HAL_CORDIC_Calculate_IT(CORDIC_HandleTypeDef *hcordic, const int32_t *pInBuff, int32_t *pOutBuff, uint32_t NbCalc);
  11. HAL_StatusTypeDef HAL_CORDIC_Calculate_DMA(CORDIC_HandleTypeDef *hcordic, const int32_t *pInBuff, int32_t *pOutBuff, uint32_t NbCalc, uint32_t DMADirection);

Na samym początku musimy uzupełnić strukturę konfiguracyjną:

Dalej można wykonywać obliczenia, czy to z użyciem funkcji standardowej, bądź w przerwaniach, bądź z użyciem DMA.

Przy porównywaniu wyników należy pamiętać, że będą one odbierać od wartości. Z tego powodu należy dobrać deltę, czyli wartość o jaką wynik obliczony będzie się różnił od wartości zadanej. 

Dane zapisane są w formacie Q1.31. Gdzie długość danych wynosi 32 bity. Jeden bit wartość znaku, 31 bitów wartość ułamkowa. Funkcje sinus oraz cosinus przyjmują wartości z przedziału od 1 do -1. Wobec tego w tym formacie, będzie można uzyskać największą dokładność. 


Do obliczeń sinusa i cosinusa wykorzystam przygotowaną tabelę kątów:

  1. static float ANGLES_Raw[128] =
  2. {
  3.         0, 0.015625, 0.03125, 0.046875, 0.0625, 0.078125,
  4.         0.09375, 0.109375, 0.125, 0.140625, 0.15625,
  5.         0.171875, 0.1875, 0.203125, 0.21875, 0.234375,
  6.         0.25, 0.265625, 0.28125, 0.296875, 0.3125,
  7.         0.328125, 0.34375, 0.359375, 0.375, 0.390625,
  8.         0.40625, 0.421875, 0.4375, 0.453125, 0.46875,
  9.         0.484375, 0.5, 0.515625, 0.53125, 0.546875,
  10.         0.5625, 0.578125, 0.59375, 0.609375, 0.625,
  11.         0.640625, 0.65625, 0.671875, 0.6875, 0.703125,
  12.         0.71875, 0.734375, 0.75, 0.765625, 0.78125,
  13.         0.796875, 0.8125, 0.828125, 0.84375, 0.859375,
  14.         0.875, 0.890625, 0.90625, 0.921875, 0.9375,
  15.         0.953125, 0.96875, 0.984375,
  16.  
  17.         -1, -0.984375, -0.96875, -0.953125, -0.9375,
  18.         -0.921875, -0.90625, -0.890625, -0.875, -0.859375,
  19.         -0.84375, -0.828125, -0.8125, -0.796875, -0.78125,
  20.         -0.765625, -0.75, -0.734375, -0.71875, -0.703125,
  21.         -0.6875, -0.671875, -0.65625, -0.640625, -0.625,
  22.         -0.609375, -0.59375, -0.578125, -0.5625, -0.546875,
  23.         -0.53125, -0.515625, -0.5, -0.484375, -0.46875,
  24.         -0.453125, -0.4375, -0.421875, -0.40625, -0.390625,
  25.         -0.375, -0.359375, -0.34375, -0.328125, -0.3125,
  26.         -0.296875, -0.28125, -0.265625, -0.25, -0.234375,
  27.         -0.21875, -0.203125, -0.1875, -0.171875, -0.15625,
  28.         -0.140625, -0.125, -0.109375, -0.09375, -0.078125,
  29.         -0.0625, -0.046875, -0.03125, -0.015625
  30. };

Do zamiany wartości float na Q1.31 wykorzystuję następującą funkcję:

  1. static inline int f32_to_q31(double input)
  2. {
  3.     const float Q31_MAX_F = 0x0.FFFFFFp0F;
  4.     const float Q31_MIN_F = -1.0F;
  5.  
  6.     return (int)roundf(scalbnf(fmaxf(fminf(input, Q31_MAX_F), Q31_MIN_F), 31));
  7. }

Zamiana w drugą stroną umożliwia makro:

  1. #define q31_to_f32(x) ldexp((int32_t)x, -31)

Opisana wyżej tablica po zmianie na format Q1.31 przyjmuje następującą postać:

  1. static int32_t ANGLES_Q1_31[128] = {
  2.         0x00000000, 0x02000000, 0x04000000, 0x06000000, 0x08000000,
  3.         0x0a000000, 0x0c000000, 0x0e000000, 0x10000000, 0x12000000,
  4.         0x14000000, 0x16000000, 0x18000000, 0x1a000000, 0x1c000000,
  5.         0x1e000000, 0x20000000, 0x22000000, 0x24000000, 0x26000000,
  6.         0x28000000, 0x2a000000, 0x2c000000, 0x2e000000, 0x30000000,
  7.         0x32000000, 0x34000000, 0x36000000, 0x38000000, 0x3a000000,
  8.         0x3c000000, 0x3e000000, 0x40000000, 0x42000000, 0x44000000,
  9.         0x46000000, 0x48000000, 0x4a000000, 0x4c000000, 0x4e000000,
  10.         0x50000000, 0x52000000, 0x54000000, 0x56000000, 0x58000000,
  11.         0x5a000000, 0x5c000000, 0x5e000000, 0x60000000, 0x62000000,
  12.         0x64000000, 0x66000000, 0x68000000, 0x6a000000, 0x6c000000,
  13.         0x6e000000, 0x70000000, 0x72000000, 0x74000000, 0x76000000,
  14.         0x78000000, 0x7a000000, 0x7c000000, 0x7e000000, 0x80000000,
  15.         0x82000000, 0x84000000, 0x86000000, 0x88000000, 0x8a000000,
  16.         0x8c000000, 0x8e000000, 0x90000000, 0x92000000, 0x94000000,
  17.         0x96000000, 0x98000000, 0x9a000000, 0x9c000000, 0x9e000000,
  18.         0xa0000000, 0xa2000000, 0xa4000000, 0xa6000000, 0xa8000000,
  19.         0xaa000000, 0xac000000, 0xae000000, 0xb0000000, 0xb2000000,
  20.         0xb4000000, 0xb6000000, 0xb8000000, 0xba000000, 0xbc000000,
  21.         0xbe000000, 0xc0000000, 0xc2000000, 0xc4000000, 0xc6000000,
  22.         0xc8000000, 0xca000000, 0xcc000000, 0xce000000, 0xd0000000,
  23.         0xd2000000, 0xd4000000, 0xd6000000, 0xd8000000, 0xda000000,
  24.         0xdc000000, 0xde000000, 0xe0000000, 0xe2000000, 0xe4000000,
  25.         0xe6000000, 0xe8000000, 0xea000000, 0xec000000, 0xee000000,
  26.         0xf0000000, 0xf2000000, 0xf4000000, 0xf6000000, 0xf8000000,
  27.         0xfa000000, 0xfc000000, 0xfe000000
  28. };

Obliczenia wartości wykonujemy w następujący sposób (bez użycia DMA):

  1. MX_CORDIC_Init();
  2. /* USER CODE BEGIN 2 */
  3. Cordic_Config_t.Function = CORDIC_FUNCTION_SINE;
  4. Cordic_Config_t.Scale = CORDIC_SCALE_0;
  5. Cordic_Config_t.InSize = CORDIC_INSIZE_32BITS;
  6. Cordic_Config_t.OutSize = CORDIC_OUTSIZE_32BITS;
  7. Cordic_Config_t.NbRead = CORDIC_NBWRITE_1;
  8. Cordic_Config_t.NbWrite = CORDIC_NBREAD_1;
  9. Cordic_Config_t.Precision = CORDIC_PRECISION_8CYCLES;
  10.  
  11. if (HAL_CORDIC_Configure(&hcordic, &Cordic_Config_t) != HAL_OK)
  12. {
  13.   Error_Handler();
  14. }
  15.  
  16. if (HAL_CORDIC_Calculate(&hcordic,
  17.             ANGLES_Q1_31,
  18.             aCalculatedSinQ1_31,
  19.             128,
  20.             CORDIC_DMA_DIR_IN_OUT) != HAL_OK)
  21. {
  22.    Error_Handler();
  23. }
  24.  
  25. while (HAL_CORDIC_GetState(&hcordic) != HAL_CORDIC_STATE_READY)
  26. { }

W przykładzie udostępnionym przez firmę ST stosowana jest dodatkowo funkcja:

  1. SCB_CleanInvalidateDCache_by_Addr((uint32_t*)&aCalculatedSinQ1_31, sizeof(aCalculatedSinQ1_31));

Jej zadaniem jest czyszczenie pamięci cache. Jest ona konieczna podczas korzystania z DMA. 

Obliczone dane są następujące:

  1. static int32_t aCalculatedSinQ1_31[128] =
  2. {
  3.         0x00000600, 0x0647da00, 0x0c8bd600, 0x12c81300,
  4.         0x18f8b700, 0x1f19fa00, 0x25280600, 0x2b1f3200,
  5.         0x30fbc900, 0x36ba1c00, 0x3c56b400, 0x41ce2100,
  6.         0x471cec00, 0x4c3fdd00, 0x5133c500, 0x55f5a000,
  7.         0x5a827600, 0x5ed77900, 0x62f20600, 0x66cf8400,
  8.         0x6a6d9900, 0x6dca0800, 0x70e2ca00, 0x73b5ec00,
  9.         0x7641ac00, 0x78848200, 0x7a7d0700, 0x7c29fa00,
  10.         0x7d8a5a00, 0x7e9d4e00, 0x7f623100, 0x7fd88300,
  11.         0x7fffff00, 0x7fd88300, 0x7f623200, 0x7e9d4f00,
  12.         0x7d8a5b00, 0x7c29f900, 0x7a7d0800, 0x78848200,
  13.         0x7641a800, 0x73b5ec00, 0x70e2cb00, 0x6dca0600,
  14.         0x6a6d9900, 0x66cf8400, 0x62f20400, 0x5ed77900,
  15.         0x5a827a00, 0x55f5a100, 0x5133c400, 0x4c3fde00,
  16.         0x471ced00, 0x41ce2000, 0x3c56b500, 0x36ba1d00,
  17.         0x30fbc400, 0x2b1f3200, 0x25280500, 0x1f19f300,
  18.         0x18f8b400, 0x12c81000, 0x0c8bd300, 0x0647d900,
  19.         0xfffffa00, 0xf9b82600, 0xf3742a00, 0xed37ed00,
  20.         0xe7074900, 0xe0e60600, 0xdad7fa00, 0xd4e0ce00,
  21.         0xcf043700, 0xc945e400, 0xc3a94c00, 0xbe31df00,
  22.         0xb8e31400, 0xb3c02300, 0xaecc3b00, 0xaa0a6000,
  23.         0xa57d8a00, 0xa1288700, 0x9d0dfa00, 0x99307c00,
  24.         0x95926700, 0x9235f800, 0x8f1d3600, 0x8c4a1400,
  25.         0x89be5400, 0x877b7e00, 0x8582f900, 0x83d60600,
  26.         0x8275a600, 0x8162b200, 0x809dcf00, 0x80277d00,
  27.         0x80000100, 0x80277d00, 0x809dce00, 0x8162b100,
  28.         0x8275a500, 0x83d60700, 0x8582f800, 0x877b7e00,
  29.         0x89be5800, 0x8c4a1400, 0x8f1d3500, 0x9235fa00,
  30.         0x95926700, 0x99307c00, 0x9d0dfc00, 0xa1288700,
  31.         0xa57d8600, 0xaa0a5f00, 0xaecc3c00, 0xb3c02200,
  32.         0xb8e31300, 0xbe31e000, 0xc3a94b00, 0xc945e300,
  33.         0xcf043c00, 0xd4e0ce00, 0xdad7fb00, 0xe0e60d00,
  34.         0xe7074c00, 0xed37f000, 0xf3742d00, 0xf9b82700,
  35. };

Całość można następnie zamienić na format float lub porównywać bezpośrednio z wartościami w formacie Q1..31.

Jak widać dane mają odchyłkę od wartości referencyjnych. W celu dokładnej weryfikacji trzeba dobrać dopuszczalny poziom błędu.

Z wykorzystaniem DMA całość wygląda następująco:

  1. Cordic_Config_t.Function = CORDIC_FUNCTION_SINE;
  2. Cordic_Config_t.Scale = CORDIC_SCALE_0;
  3. Cordic_Config_t.InSize = CORDIC_INSIZE_32BITS;
  4. Cordic_Config_t.OutSize = CORDIC_OUTSIZE_32BITS;
  5. Cordic_Config_t.NbRead = CORDIC_NBWRITE_1;
  6. Cordic_Config_t.NbWrite = CORDIC_NBREAD_1;
  7. Cordic_Config_t.Precision = CORDIC_PRECISION_6CYCLES;
  8.  
  9. if (HAL_CORDIC_Configure(&hcordic, &Cordic_Config_t) != HAL_OK)
  10. {
  11.     Error_Handler();
  12. }
  13.  
  14. if (HAL_CORDIC_Calculate_DMA(&hcordic, ANGLES_Q1_31, aCalculatedSinQ1_31,
  15.       sizeof(ANGLES_Q1_31)/sizeof(ANGLES_Q1_31[0]), CORDIC_DMA_DIR_IN_OUT) != HAL_OK)
  16. {
  17.     Error_Handler();
  18. }
  19.  
  20. while (HAL_CORDIC_GetState(&hcordic) != HAL_CORDIC_STATE_READY) { }
  21. SCB_CleanInvalidateDCache_by_Addr((uint32_t*)&aCalculatedSinQ1_31, sizeof(aCalculatedSinQ1_31));

Należy tutaj jeszcze pamiętać o poprawnej inicjalizacji DMA:

  1. void HAL_CORDIC_MspInit(CORDIC_HandleTypeDef* hcordic)
  2. {
  3.   if(hcordic->Instance==CORDIC)
  4.   {
  5.     /* Peripheral clock enable */
  6.     __HAL_RCC_CORDIC_CLK_ENABLE();
  7.  
  8.     /* CORDIC DMA Init */
  9.     /* CORDIC_WRITE Init */
  10.     hdma_cordic_write.Instance = DMA1_Stream0;
  11.     hdma_cordic_write.Init.Request = DMA_REQUEST_CORDIC_WRITE;
  12.     hdma_cordic_write.Init.Direction = DMA_MEMORY_TO_PERIPH;
  13.     hdma_cordic_write.Init.PeriphInc = DMA_PINC_DISABLE;
  14.     hdma_cordic_write.Init.MemInc = DMA_MINC_ENABLE;
  15.     hdma_cordic_write.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  16.     hdma_cordic_write.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  17.     hdma_cordic_write.Init.Mode = DMA_NORMAL;
  18.     hdma_cordic_write.Init.Priority = DMA_PRIORITY_LOW;
  19.     if (HAL_DMA_Init(&hdma_cordic_write) != HAL_OK)
  20.     {
  21.       Error_Handler();
  22.     }
  23.  
  24.     __HAL_LINKDMA(hcordic,hdmaIn,hdma_cordic_write);
  25.  
  26.     /* CORDIC_READ Init */
  27.     hdma_cordic_read.Instance = DMA1_Stream1;
  28.     hdma_cordic_read.Init.Request = DMA_REQUEST_CORDIC_READ;
  29.     hdma_cordic_read.Init.Direction = DMA_PERIPH_TO_MEMORY;
  30.     hdma_cordic_read.Init.PeriphInc = DMA_PINC_DISABLE;
  31.     hdma_cordic_read.Init.MemInc = DMA_MINC_ENABLE;
  32.     hdma_cordic_read.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD;
  33.     hdma_cordic_read.Init.MemDataAlignment = DMA_MDATAALIGN_WORD;
  34.     hdma_cordic_read.Init.Mode = DMA_NORMAL;
  35.     hdma_cordic_read.Init.Priority = DMA_PRIORITY_LOW;
  36.     if (HAL_DMA_Init(&hdma_cordic_read) != HAL_OK)
  37.     {
  38.       Error_Handler();
  39.     }
  40.  
  41.     __HAL_LINKDMA(hcordic,hdmaOut,hdma_cordic_read);
  42.   }
  43. }

Dokumentacja:


Youtube - Cordic In Practice