CẨM NANG STM32 CĂN BẢN CẦN BIẾT – SPI Flash – Cấu hình STM32 SPI

Cấu hình STM32 SPI

Để giao tiếp với chip flash ngoài, chúng ta cần phải cấu hình và sẵn sàng cho thiết bị ngoại vi STM32 SPI. Mã nguồn demo của chương này được tìm thấy trong thư mục sau:

$ cd ~/stm32f103c8t6/rtos/winbond

Bảng 8-1 tóm tắt các chân GPIO sẽ được sử dụng để kết nối thiết bị ngoại vi SPI1 với chip flash Winbond. Chúng cũng được thể hiện trong sơ đồ trong Hình 8-3.

Bảng 8-1 Chân STM32 SPI1 được sử dụng

Chân GPIOChức năng SPIMô tả
PA4/CSChip Select (active low)
PA5SCKClock Hệ thống
PA6MISOMaster In, Slave Out
PA7MOSIMaster Out, Slave In

Bản kê 8-1 cung cấp mã nguồn khởi tạo chung được tìm thấy trong chương trình chính. Dòng 679 cho phép kích hoạt xung cho GPIOA, vì thiết bị ngoại vi SPI của chúng ta sử dụng các chân đó (Bảng 8-1). Việc thiết lập SPI còn lại được thực hiện trong dòng 685, hàm spi_setup() mà chúng ta sẽ kiểm tra.

Bản kê 8-1. Khởi động Chương chình Chính

0674: int
0675: main(void) {
0676:
0677:   rcc_clock_setup_in_hse_8mhz_out_72mhz(); // Blue pill
0678:
0679:   rcc_periph_clock_enable(RCC_GPIOA);
0680:   rcc_periph_clock_enable(RCC_GPIOC);
0681:
0682:   // LED on PC13
0683:   gpio_set_mode(GPIOC,GPIO_MODE_OUTPUT_2_MHZ,
            GPIO_CNF_OUTPUT_PUSHPULL,GPIO13);
0684:
0685:   spi_setup();
0686:   gpio_set(GPIOC,GPIO13);    // PC13 = on
0687:
0688:   usb_start(1);
0689:   std_set_device(mcu_usb);   // Use USB for std I/O
0690:   gpio_clear(GPIOC,GPIO13);  // PC13 = off
0691:
0692:   xTaskCreate(monitor_task,"monitor",
            500,NULL,configMAX_PRIORITIES-1,NULL);
0693:   vTaskStartScheduler();
0694:   for (;;);
0695:   return 0;
0696: }

Để cho phép chúng ta tập trung vào SPI trong chương này, chúng ta sử dụng một thư viện để cung cấp giao tiếp USB cho máy tính. Thư viện đó nằm ở đây:

•	~/stm32f103c8t6/rtos/libwwg/libwwg.a

Mã nguồn cho thư viện được tìm thấy trong hai thư mục sau:

•	~/stm32f103c8t6/rtos/libwwg/include
•	~/stm32f103c8t6/rtos/libwwg/src

Các dòng 688 và 689 thực hiện khởi tạo USB, cho phép chương trình giao tiếp với một chương trình terminal. Dòng 689 chuyển hướng tất cả các lệnh gọi tới std_ printf() sang usb_printf(), v.v. Nếu sau đó bạn nên sử dụng UART để liên lạc, trình chuyển hướng này có thể được đặt cho thay thế đó.

Bản kê 8-2 cho thấy hàm spi_setup(), bao gồm các đặc tả SPI. Các bước sau được sử dụng để khởi tạo SPI1 ngoại vi:

  1. Xung cho SPI1 được bật (dòng 648)
  2. Các chân GPIOA được cấu hình cho (các dòng 649–654):
  1. đầu ra chức năng thay thế (đẩy-kéo)
  2. 50 MHz (cho thời gian lên/xuống nhanh)
  3. cho PA4, PA5 và PA7
  1. GPIO PA6 được cấu hình cho đầu vào không có điện trở kéo lên (dòng 655–660)
  2. Thiết bị ngoại vi SPI1 được reset (dòng 661)
  3. SPI1 được cấu hình để sử dụng:
    1. một ước số fpclk 256 (dòng 664)
    2. Phân cực SCK 0 (low) khi không được sử dụng (dòng 655)
    3. Pha của xung (clock phase) xảy ra trong lần chuyển tiếp đầu tiên (dòng 666)
    4. Độ dài của word là 8 bit (dòng 667)
    5. Các bit được chuyển ra MSB (Most Significant Bit: Bit bậc cao nhất) đầu tiên (dòng 668)
  4. SPI1 đang sử dụng quản lý phần cứng /CS (tức là, không sử dụng quản lý Slave phần mềm, dòng 670).
  5. Thiết bị ngoại vi SPI có thể sử dụng /CS (dòng 671).

Bản kê 8-2. Thiết lập ngoại vi SPI

0645: static void
0646: spi_setup(void) {
0647:
0648:   rcc_periph_clock_enable(RCC_SPI1);
0649:   gpio_set_mode(
0650:       GPIOA,
0651:           GPIO_MODE_OUTPUT_50_MHZ,
0652:           GPIO_CNF_OUTPUT_ALTFN_PUSHPULL,
0653:           GPIO4|GPIO5|GPIO7  // NSS=PA4,SCK=PA5,MOSI=PA7
0654:   );
0655:   gpio_set_mode(
0656:       GPIOA,
0657:       GPIO_MODE_INPUT,
0658:       GPIO_CNF_INPUT_FLOAT,
0659:       GPIO6                // MISO=PA6
0660:   );
0661:   spi_reset(SPI1);
0662:   spi_init_master(
0663:           SPI1,
0664:           SPI_CR1_BAUDRATE_FPCLK_DIV_256,
0665:           SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE,
0666:           SPI_CR1_CPHA_CLK_TRANSITION_1,
0667:           SPI_CR1_DFF_8BIT,
0668:           SPI_CR1_MSBFIRST
0669:   );
0670:   spi_disable_software_slave_management(SPI1);
0671:   spi_enable_ss_output(SPI1);
0672: }

=> Bài viết được trích từ sách : Cẩm nang STM32 (tập 1)

Tốc độ xung SPI

Một trong những điều khó chịu nhất về nền tảng STM32 là sự phức tạp của hệ thống xung. Câu hỏi chúng ta muốn trả lời là macro SPI_CR1_BAUDRATE_FPCLK_DIV_256 cung cấp tần số bao nhiêu? Một phần của câu trả lời nằm trong việc xác định fPCLK Đối với STM32F103, SPI1 sử dụng xung bus APB2, có tần số tối đa là 72 MHz. SPI2 sử dụng xung bus APB1, có tần số tối đa là 36 MHz.

Chương trình chính sử dụng hàm libopencm3 sau để thiết lập một số xung chính:

0677:   rcc_clock_setup_in_hse_8mhz_out_72mhz(); // Blue pill

Khi các cài đặt xung này có hiệu lực, chúng ta có thể tóm tắt fPCLK của SPI như trong Bảng 8-2.

Bảng 8-2. Tần số cho SPI1 và SPI2, Giả sử rcc_clock_setup_in_hse_8mhz_out_72mhz()

XungBusNgoại vifPCLK
PCLK1APB1SPI236 MHz
PCLK2APB2SPI172 MHz

Với thông tin này, chúng ta có thể tóm tắt các lựa chọn cho các ước số xung SPIx, như trong Bảng 8-3. Tôi đã xác nhận với một DSO (dao động ký lưu trữ kỹ thuật số) rằng chu kỳ tín hiệu SCK là khoảng 3,56 μs (micro-giây) khi chạy chương trình demo. Điều này cho ra tần số khoảng 281 kHz, như mong đợi.

Bảng 8-3. Tần-số số-chia SPI, Dựa trên Bảng 8-3.

Số chiaMacroTần số SPI1Tần số SPI2
2SPI_CR1_BAUDRATE_FPCLK_DIV_236 MHz18 MHz
4SPI_CR1_BAUDRATE_FPCLK_DIV_418 MHz9 MHz
8SPI_CR1_BAUDRATE_FPCLK_DIV_89 MHz4.5 MHz
16SPI_CR1_BAUDRATE_FPCLK_DIV_164.5 MHz2.25 MHz
32SPI_CR1_BAUDRATE_FPCLK_DIV_322.25 MHz1.125 MHz
64SPI_CR1_BAUDRATE_FPCLK_DIV_641.125 MHz562.5 kHz
128SPI_CR1_BAUDRATE_FPCLK_DIV_128562.5 kHz281.25 kHz
256SPI_CR1_BAUDRATE_FPCLK_DIV_256281.25 kHz140.625 kHz

Tôi đã chọn tần số thấp cho bảng demo này để đảm bảo kết quả tốt trên breadboard. Đôi khi với breadboard và dây nối dài, nhiễu có thể gây rối cho giao tiếp SPI. Với mã nguồn theo ý của bạn, bạn có thể thử tốc độ bit cao hơn sau thành công ban đầu của mình. Chip Winbond sẽ đọc liên tục lên đến 50 MHz, nhưng SPI1 bị giới hạn ở 36 MHz trên nền tảng STM32, thiết lập giới hạn trên của bạn.

=> xem thêm : Lập trình Arm STM32

Chế độ Xung SPI

Phương thức spi_setup() trong Bản kê 8-2 sử dụng tham số cấu hình sau:

0665:           SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE,

Điều đó có ý nghĩa gì đối với bộ lập trình?

SPI có thể hoạt động ở một trong bốn chế độ, dễ dẫn đến nhầm lẫn. Các chip flash Winbond được sử dụng trong chương này có thể hoạt động ở các chế độ 0 hoặc 3. Hình 8-5 minh họa các mối quan hệ giữa các macro cấu hình libopencm3 khác nhau. Lưu ý rằng cực tính và pha của xung nhịp cùng nhau xác định chế độ hoạt động SPI.

Hình 8-5. Các cấu hình cực tính và pha của xung nhịp

Khi SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE được sử dụng với SPI_CR1_CPHA_CLK_ TRANSITION_1, đầu nhận lấy mẫu dữ liệu input ở cạnh lên khi chuyển xung (mũi tên ngắn của Hình 8-5). Nếu cùng cực tính xung được sử dụng và SPI_CR1_CPHA_CLK_ TRANSITION_2 được định cấu hình, dữ liệu được lấy mẫu ở cạnh xuống của xung (mũi tên dài).

Tình huống được đảo ngược khi sử dụng phân cực SPI_CR1_CPOL_CLK_TO_1_WHEN_IDLE (dòng thứ hai từ trên cùng trong Hình 8-5). Cạnh xuống (mũi tên ngắn) của xung được sử dụng khi SPI_CR1_CPHA_CLK_TRANSITION_1 được định cấu hình; nếu không, cạnh lên của xung (mũi tên dài) được sử dụng.

Bảng 8-4 tóm tắt các chế độ SPI bằng cách sử dụng tên macro libopencm3. Biết rằng các chip Winbond W25QXX sẽ hoạt động ở chế độ 0 hoặc 3, chúng ta có thể đi đến kết luận rằng chúng hoạt động chỉ trên cạnh lên của tín hiệu SCK.

Bảng 8-4. Tóm tắt các chế độ SPI theo Số

SPI ModeCực tính của ClockPha của Clock
0SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLESPI_CR1_CPHA_CLK_TRANSITION_1
1SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLESPI_CR1_CPHA_CLK_TRANSITION_2
2SPI_CR1_CPOL_CLK_TO_1_WHEN_IDLESPI_CR1_CPHA_CLK_TRANSITION_1
3SPI_CR1_CPOL_CLK_TO_1_WHEN_IDLESPI_CR1_CPHA_CLK_TRANSITION_2

Một điểm khác cần phải được thực hiện là cực tính của xung SPI, ít nhất là tham chiếu đến tên macro libopencm3. Macro SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE mô tả cực tính chưa được sử dụng của xung trong thời gian chọn chip. Hình 8-6 là một ảnh chụp lại đường quét trên máy hiện sóng của tín hiệu SCK đang hoạt động với /CS xuống mức low. Trước khi kích hoạt /CS, xung ở mức high. Nhưng trong giao tác SPI (/CS đang hoạt động), xung đã ở mức low khi không hoạt động. Điều này rất quan trọng cần lưu ý khi kiểm tra tín hiệu SPI.

Hình 8-6. Đường quét trên DSO của SCK và /CS, với SCK nghỉ ở mức low

Endian và Độ dài của Word

Bây giờ, chúng ta có thể trình bày các khía cạnh cuối cùng của cấu hình SPI1 (từ Bản kê 8-2):

0662:   spi_init_master(
0663:           SPI1,
0664:           SPI_CR1_BAUDRATE_FPCLK_DIV_256,
0665:           SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE,
0666:           SPI_CR1_CPHA_CLK_TRANSITION_1,
0667:           SPI_CR1_DFF_8BIT,
0668:           SPI_CR1_MSBFIRST
0669:   );

Macro SPI_CR1_DFF_8BIT (dòng 667) xác định rằng độ dài của một word là một byte (8 bit). Macro SPI_CR1_MSBFIRST (dòng 668) chỉ ra là chúng ta sẽ truyền bit bậc cao nhất (MSB) trước tiên (big endian). Các lựa chọn libopencm3 khác là SPI_CR1_DFF_16BIT và SPI_CR1_LSBFIRST.

Chương trình thiết lập SPI kết thúc với hai hàm gọi nữa:

0670:   spi_disable_software_slave_management(SPI1);
0671:   spi_enable_ss_output(SPI1);

Dòng 670 chỉ đơn giản là chỉ định một cách ngược lại rằng chúng ta sẽ sử dụng xác nhận phần cứng của chân NSS (/CS). Hàm gọi này có thể không hoàn toàn cần thiết sau khi reset, nhưng nó sẽ phục vụ cho việc ghi lại ý định của chúng ta. Dòng 671 chỉ ra rằng thiết bị ngoại vi SPI là để xác nhận điều khiển chân NSS (/CS). Với những bước thực hiện, SPI1 đã sẵn sàng để sử dụng.

=> Sách Arduino, ESP8266, STM32 : Sách tự động hóa