前言
CRC(Cyclic redundancy check)即循環冗餘校驗是一種雜湊函式,通常用於通訊,用以讓接收方確認資料是否正確。
多數的 STM32 家族都有內建 CRC 計算單元,本篇要來介紹如何使用。
正文
首先一樣以 Nucleo-F446RE 做示範。
首先建立一個 PIO 的專案,選擇 Framework 爲「libopencm3」,並在 src/
資料夾中新增並開啓 main.c
。
完整程式
1/**
2 * @file main.c
3 * @brief CRC example for STM32 Nucleo-F446RE.
4 */
5
6#include <libopencm3/stm32/rcc.h>
7#include <libopencm3/stm32/gpio.h>
8#include <libopencm3/stm32/usart.h>
9#include <libopencm3/stm32/crc.h>
10#include <libopencm3/cm3/nvic.h>
11
12#define USART_BAUDRATE (9600)
13
14#define RCC_USART_TXRX_GPIO (RCC_GPIOA)
15#define GPIO_USART_TXRX_PORT (GPIOA)
16#define GPIO_USART_TX_PIN (GPIO2) /* ST-Link (Arduino-D1). */
17#define GPIO_USART_RX_PIN (GPIO3) /* ST-Link (Arduino-D0). */
18#define GPIO_USART_AF (GPIO_AF7) /* Table-11 in DS10693. */
19
20static void rcc_setup(void)
21{
22 rcc_periph_clock_enable(RCC_USART_TXRX_GPIO);
23 rcc_periph_clock_enable(RCC_USART2);
24 rcc_periph_clock_enable(RCC_CRC);
25}
26
27static void usart_setup(void)
28{
29 /* Set USART-Tx and Rx pin to alternate function. */
30 gpio_mode_setup(GPIO_USART_TXRX_PORT,
31 GPIO_MODE_AF,
32 GPIO_PUPD_NONE,
33 GPIO_USART_TX_PIN | GPIO_USART_RX_PIN);
34
35 gpio_set_af(GPIO_USART_TXRX_PORT,
36 GPIO_USART_AF,
37 GPIO_USART_TX_PIN | GPIO_USART_RX_PIN);
38
39 /* Config USART params. */
40 usart_set_baudrate(USART2, USART_BAUDRATE);
41 usart_set_databits(USART2, 8);
42 usart_set_stopbits(USART2, USART_STOPBITS_1);
43 usart_set_parity(USART2, USART_PARITY_NONE);
44 usart_set_flow_control(USART2, USART_FLOWCONTROL_NONE);
45 usart_set_mode(USART2, USART_MODE_TX_RX);
46
47 /* Setup interrupt. */
48 usart_enable_rx_interrupt(USART2); /* Enable receive interrupt. */
49 nvic_enable_irq(NVIC_USART2_IRQ);
50
51 usart_enable(USART2);
52}
53
54int main(void)
55{
56 rcc_setup();
57 usart_setup();
58
59 usart_send_blocking(USART2, 'C');
60 usart_send_blocking(USART2, 'R');
61 usart_send_blocking(USART2, 'C');
62 usart_send_blocking(USART2, '\r');
63 usart_send_blocking(USART2, '\n');
64
65 while (1)
66 { }
67 return 0;
68}
69
70/**
71 * @brief USART2 Interrupt service routine.
72 */
73void usart2_isr(void)
74{
75 usart_disable_rx_interrupt(USART2);
76 crc_reset(); /* Resets the CRC calculation unit and sets the data register to 0xFFFF FFFF. */
77
78 uint8_t data[4];
79 for (int i = 0; i < 4; i++)
80 {
81 data[i] = usart_recv_blocking(USART2);
82 }
83
84 uint32_t comb = data[3] + (data[2] << 8) + (data[1] << 16) + (data[0] << 24);
85 uint32_t result = crc_calculate(comb);
86
87 usart_send_blocking(USART2, (result >> 24) & 0xFF);
88 usart_send_blocking(USART2, (result >> 16) & 0xFF);
89 usart_send_blocking(USART2, (result >> 8) & 0xFF);
90 usart_send_blocking(USART2, result & 0xFF);
91
92 USART_SR(USART2) &= ~USART_SR_RXNE; /* Clear 'Read data register not empty' flag. */
93 usart_enable_rx_interrupt(USART2);
94}
分段說明
CRC 計算
1/**
2 * @brief USART2 Interrupt service routine.
3 */
4void usart2_isr(void)
5{
6 usart_disable_rx_interrupt(USART2);
7 crc_reset(); /* Resets the CRC calculation unit and sets the data register to 0xFFFF FFFF. */
8
9 uint8_t data[4];
10 for (int i = 0; i < 4; i++)
11 {
12 data[i] = usart_recv_blocking(USART2);
13 }
14
15 uint32_t comb = data[3] + (data[2] << 8) + (data[1] << 16) + (data[0] << 24);
16 uint32_t result = crc_calculate(comb);
17
18 usart_send_blocking(USART2, (result >> 24) & 0xFF);
19 usart_send_blocking(USART2, (result >> 16) & 0xFF);
20 usart_send_blocking(USART2, (result >> 8) & 0xFF);
21 usart_send_blocking(USART2, result & 0xFF);
22
23 USART_SR(USART2) &= ~USART_SR_RXNE; /* Clear 'Read data register not empty' flag. */
24 usart_enable_rx_interrupt(USART2);
25}
CRC 計算單元的使用方式很單純,因此我直接寫在 USART2 的 ISR 中。
但 ISR 執行後,先禁能 USART2 的中斷,以方便之後連續讀取 4 Byte 的資料。
以 crc_reset()
重設 CRC 單元,並將資料暫存器重置爲 0xFFFF FFFF
(即 CRC Init = 0xFFFF FFFF
)。
以 for
迴圈連續接收 4 Byte 的資料,並使用 crc_calculate()
將要計算的資料寫入 CRC 的資料暫存器,該函式會自行 Blocking 直到 CRC 計算完就會回傳結果。
最後再將結果用 USART2 傳出,再重新致能其中斷以等待下次接收。
多環境程式(F446RE + F103RB)
由於 STM32F1 的部分函式不同,所以 F103RB 沒辦法直接使用上面的 F446RE 的程式。
以下列出主要的差異部分,也就是 GPIO 的部分。完整的程式請看 GitHub repo。
1static void usart_setup(void)
2{
3 /* Set USART-Tx and Rx pin to alternate function. */
4#if defined(STM32F1)
5 gpio_set_mode(GPIO_USART_TXRX_PORT,
6 GPIO_MODE_OUTPUT_50_MHZ,
7 GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,
8 GPIO_USART_TX_PIN);
9
10 gpio_set_mode(GPIO_USART_TXRX_PORT,
11 GPIO_MODE_INPUT,
12 GPIO_CNF_INPUT_FLOAT,
13 GPIO_USART_RX_PIN);
14#else
15 gpio_mode_setup(GPIO_USART_TXRX_PORT,
16 GPIO_MODE_AF,
17 GPIO_PUPD_NONE,
18 GPIO_USART_TX_PIN | GPIO_USART_RX_PIN);
19
20 gpio_set_af(GPIO_USART_TXRX_PORT,
21 GPIO_USART_AF,
22 GPIO_USART_TX_PIN | GPIO_USART_RX_PIN);
23#endif
24 /* 省略部分程式. */
25}
成果
從 RM0390 或 AN4187 中可以得知,STM32 使用的多項式是 0x4C1 1DB7
(部分系列可修改),初始值爲 0xFFFF FFFF
。
我依序輸入 32 位元的資料並各別得到其結果:
- 輸入
0x9D 12 3A D4
得到0xC9 68 5F 5E
。 - 輸入
0x00 00 00 00
得到0xC7 04 DD 7B
。 - 輸入
ABCD
(ASCII) 得到0xAB CF 9A 63
。
可以到一些線上的 CRC 計算機(如這個)驗證其結果是正確的(算法選擇「CRC-32/MPEG-2」)。
小結
CRC 的使用還是滿單純的,就只要致能 RCC 後呼叫計算函式,將要計算的資料傳入後就可以得到結果了。
參考資料
- libopencm3/libopencm3-examples
- platformio/platform-ststm32
- STM32 CRC application note (AN4187)
- STM32F446RE datasheet (DS10693)
- STM32F446xx reference manual (RM0390)
- STM32F103RB datasheet (DS5319)
- STM32 Nucleo-64 board user manual (UM1724)
本文的程式也有放在 GitHub 上。
本文同步發表於 iT 邦幫忙-2022 iThome 鐵人賽。
留言可能不會立即顯示。若過了幾天仍未出現,請 Email 聯繫:)
comments powered by Disqus