Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

В phần đầu tiên Tôi đã cố gắng nói với các kỹ sư điện tử có sở thích lớn lên từ quần Arduino về cách thức và lý do họ nên đọc bảng dữ liệu và các tài liệu khác về bộ vi điều khiển. Văn bản có kích thước lớn nên tôi hứa sẽ đưa ra các ví dụ thực tế trong một bài viết riêng. À, anh ấy tự gọi mình là nấm sữa...

Hôm nay mình sẽ hướng dẫn các bạn cách sử dụng datasheets để giải quyết khá đơn giản nhưng cần thiết cho nhiều dự án, tác vụ trên bộ điều khiển STM32 (Blue Pill) và STM8. Tất cả các dự án demo đều dành riêng cho đèn LED yêu thích của tôi, chúng tôi sẽ thắp sáng chúng với số lượng lớn, do đó chúng tôi sẽ phải sử dụng tất cả các loại thiết bị ngoại vi thú vị.

Văn bản một lần nữa hóa ra lại rất lớn, vì vậy để thuận tiện, tôi sẽ tạo nội dung:

STM32 Blue Pill: 16 đèn LED với trình điều khiển DM634
STM8: Thiết lập sáu chânPWM
STM8: 8 đèn LED RGB trên ba chân, ngắt

Tuyên bố miễn trừ trách nhiệm: Tôi không phải là kỹ sư, tôi không giả vờ có kiến ​​thức sâu về điện tử, bài viết dành cho những người nghiệp dư như tôi. Trên thực tế, hai năm trước tôi đã coi mình là đối tượng mục tiêu. Nếu lúc đó ai đó nói với tôi rằng bảng dữ liệu trên một con chip lạ không đáng sợ khi đọc thì tôi đã không mất nhiều thời gian để tìm kiếm một số đoạn mã trên Internet và phát minh ra nạng bằng kéo và băng dính.

Trọng tâm của bài viết này là về bảng dữ liệu chứ không phải dự án nên mã có thể không gọn gàng cho lắm và thường chật chội. Bản thân các dự án rất đơn giản, mặc dù phù hợp cho những người lần đầu làm quen với con chip mới.

Tôi hy vọng rằng bài viết của tôi sẽ giúp ích được cho ai đó đang ở giai đoạn đắm chìm trong sở thích tương tự.

STM32

16 đèn LED với DM634 và SPI

Một dự án nhỏ sử dụng Blue Pill (STM32F103C8T6) và trình điều khiển LED DM634. Sử dụng bảng dữ liệu, chúng tôi sẽ tìm ra trình điều khiển, cổng STM IO và cấu hình SPI.

DM634

Chip Đài Loan với 16 đầu ra 16-bitPWM, có thể kết nối thành chuỗi. Model 12bit cấp thấp được biết đến từ một dự án trong nước Gói nhẹ. Có một lần, khi lựa chọn giữa DM63x và TLC5940 nổi tiếng, tôi đã chọn DM vì một số lý do: 1) TLC trên Aliexpress chắc chắn là giả, nhưng cái này thì không; 2) DM có một bộ điều khiển xung tự động với bộ tạo tần số riêng; 3) nó có thể được mua với giá rẻ ở Moscow, thay vì chờ đợi bưu kiện từ Ali. Và tất nhiên, thật thú vị khi tự học cách điều khiển con chip thay vì sử dụng thư viện có sẵn. Chip hiện nay chủ yếu được trình bày trong gói SSOP24, chúng dễ dàng hàn vào bộ chuyển đổi.

Vì nhà sản xuất là người Đài Loan nên bảng dữliệu con chip được viết bằng tiếng Anh Trung Quốc, có nghĩa là nó sẽ rất vui. Đầu tiên chúng ta nhìn vào sơ đồ chân (Kết nối pin) để hiểu chân nào cần kết nối với chân nào và mô tả về các chân (Ghim Mô tả). 16 chân:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Nguồn chìm DC (Cống mở)

Bồn rửa / Đầu ra thoát nước mở - làm khô hạn; nguồn dòng điện chạy vào; đầu ra được nối đất ở trạng thái hoạt động - đèn LED được kết nối với trình điều khiển bằng cực âm. Tất nhiên, về mặt điện, đây không phải là một “cống hở” (cống mở), nhưng trong bảng dữ liệu, ký hiệu này cho các chân ở chế độ thoát nước thường được tìm thấy.

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Điện trở ngoài giữa REXT và GND để đặt giá trị dòng điện đầu ra

Một điện trở tham chiếu được lắp đặt giữa chân REXT và mặt đất, điện trở này điều khiển điện trở trong của đầu ra, xem biểu đồ trên trang 9 của biểu dữ liệu. Trong DM634, điện trở này cũng có thể được điều khiển bằng phần mềm, cài đặt độ sáng tổng thể (độ sáng toàn cầu); Tôi sẽ không đi sâu vào chi tiết trong bài viết này mà chỉ đặt một điện trở 2.2 - 3 kOhm ở đây.

Để hiểu cách điều khiển chip, chúng ta hãy xem mô tả về giao diện thiết bị:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

Vâng, đây rồi, tiếng Anh Trung Quốc với tất cả sự vinh quang của nó. Việc dịch phần này có vấn đề, bạn có thể hiểu nó nếu muốn, nhưng có một cách khác - hãy xem cách kết nối với TLC5940 có chức năng tương tự được mô tả trong biểu dữ liệu:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
... Chỉ cần ba chân để nhập dữ liệu vào thiết bị. Cạnh lên của tín hiệu SCLK sẽ chuyển dữ liệu từ chân SIN sang thanh ghi bên trong. Sau khi tất cả dữ liệu đã được tải, tín hiệu XLAT ngắn, cao sẽ chốt dữ liệu được truyền tuần tự vào các thanh ghi bên trong. Các thanh ghi bên trong là các cổng được kích hoạt bởi mức tín hiệu XLAT. Tất cả dữ liệu được truyền bit quan trọng nhất trước tiên.

Chốt – chốt/chốt/khóa.
Cạnh tăng - cạnh đầu của xung
MSB đầu tiên – bit có ý nghĩa nhất (ngoài cùng bên trái) về phía trước.
dữ liệu đồng hồ – truyền dữ liệu tuần tự (từng bit một).

Lời cái chốt thường được tìm thấy trong tài liệu về chip và được dịch theo nhiều cách khác nhau, vì vậy để hiểu rõ, tôi sẽ cho phép mình

chương trình giáo dục nhỏTrình điều khiển LED về cơ bản là một thanh ghi thay đổi. "Sự thay đổi" (thay đổi) trong tên - chuyển động theo chiều bit của dữ liệu bên trong thiết bị: mỗi bit mới được đẩy vào bên trong sẽ đẩy toàn bộ chuỗi về phía trước nó. Vì không ai muốn quan sát sự nhấp nháy hỗn loạn của các đèn LED trong ca làm việc nên quá trình này diễn ra trong các thanh ghi đệm được ngăn cách với các thanh ghi làm việc bằng một bộ điều tiết (cái chốt) là một loại phòng chờ trong đó các bit được sắp xếp theo trình tự mong muốn. Khi mọi thứ đã sẵn sàng, màn trập sẽ mở ra và các mũi khoan hoạt động, thay thế lô trước đó. Từ cái chốt trong tài liệu về vi mạch hầu như luôn ngụ ý một bộ điều tiết như vậy, bất kể nó được sử dụng theo cách kết hợp nào.

Vì vậy, việc truyền dữ liệu đến DM634 được thực hiện như sau: đặt đầu vào DAI về giá trị bit quan trọng nhất của đèn LED xa, kéo DCK lên xuống; đặt đầu vào DAI thành giá trị của bit tiếp theo, kéo DCK; v.v. cho đến khi tất cả các bit được truyền đi (đã khóa trong), sau đó chúng tôi kéo LAT. Điều này có thể được thực hiện bằng tay (tiếng nổ lớn), nhưng tốt hơn là sử dụng giao diện SPI được thiết kế đặc biệt cho việc này, vì nó được trình bày trên STM32 của chúng tôi dưới dạng hai bản.

Thuốc màu xanh STM32F103

Giới thiệu: Bộ điều khiển STM32 phức tạp hơn nhiều so với Atmega328 vì chúng có vẻ đáng sợ. Hơn nữa, vì lý do tiết kiệm năng lượng, hầu hết tất cả các thiết bị ngoại vi đều bị tắt khi khởi động và tần số xung nhịp là 8 MHz tính từ nguồn bên trong. May mắn thay, các lập trình viên STM đã viết mã đưa chip lên đến tốc độ “được tính toán” là 72 MHz và tác giả của tất cả các IDE mà tôi biết đã đưa nó vào quy trình khởi tạo, vì vậy chúng ta không cần phải bấm giờ (nhưng bạn có thể nếu bạn thực sự muốn). Nhưng bạn sẽ phải bật các thiết bị ngoại vi.

Tài liệu: Blue Pill được trang bị chip STM32F103C8T6 phổ biến, có hai tài liệu hữu ích cho nó:

Trong biểu dữ liệu chúng ta có thể quan tâm:

  • Sơ đồ chân – sơ đồ chân chip – trong trường hợp chúng tôi quyết định tự làm bảng;
  • Bản đồ bộ nhớ – bản đồ bộ nhớ cho một con chip cụ thể. Hướng dẫn tham khảo có bản đồ cho toàn bộ dòng và nó đề cập đến các sổ đăng ký mà của chúng tôi không có.
  • Bảng Định nghĩa Chân – liệt kê các chức năng chính và thay thế của các chân; Đối với “viên thuốc màu xanh”, bạn có thể tìm thấy những hình ảnh tiện lợi hơn trên Internet với danh sách các chân và chức năng của chúng. Vì vậy, chúng tôi ngay lập tức google sơ đồ chân của Blue Pill và giữ lại bức ảnh này:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Lưu ý: có một lỗi trong hình ảnh từ Internet, đã được ghi chú trong phần bình luận, cảm ơn vì điều đó. Hình ảnh đã được thay thế, nhưng đây là một bài học - tốt hơn hết là bạn nên kiểm tra thông tin không phải từ bảng dữ liệu.

Chúng tôi xóa bảng dữ liệu, mở Tài liệu tham khảo và từ giờ chúng tôi chỉ sử dụng nó.
Quy trình: chúng tôi xử lý đầu vào/đầu ra tiêu chuẩn, định cấu hình SPI, bật các thiết bị ngoại vi cần thiết.

Đầu ra đầu vào

Trên Atmega328, I/O được triển khai cực kỳ đơn giản, đó là lý do tại sao sự phong phú của các tùy chọn STM32 có thể gây nhầm lẫn. Bây giờ chúng ta chỉ cần kết luận, nhưng ngay cả những kết luận này cũng có bốn lựa chọn:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
cống hở, kéo đẩy, kéo đẩy thay thế, cống mở thay thế

"Kéo-đẩy" (đẩy-kéo) là đầu ra thông thường từ Arduino, chân có thể lấy giá trị CAO hoặc THẤP. Nhưng với “cống hở” thì có nỗi khó khăn, mặc dù trên thực tế mọi thứ ở đây đều đơn giản:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Cấu hình đầu ra / khi cổng được gán cho đầu ra: / bật bộ đệm đầu ra: / – chế độ cống mở: “0” trong thanh ghi đầu ra kích hoạt N-MOS, “1” trong thanh ghi đầu ra sẽ rời cổng ở chế độ Hi-Z ( P-MOS chưa được kích hoạt ) / – chế độ kéo đẩy: “0” trong thanh ghi đầu ra kích hoạt N-MOS, “1” trong thanh ghi đầu ra kích hoạt P-MOS.

Tất cả sự khác biệt giữa cống mở (cống mở) từ “đẩy-kéo” (đẩy-kéo) là ở chân đầu tiên không thể chấp nhận trạng thái CAO: khi ghi một vào thanh ghi đầu ra, nó sẽ chuyển sang chế độ điện trở cao (trở kháng cao, Hi-Z). Khi ghi số XNUMX, chân hoạt động giống nhau ở cả hai chế độ, cả về mặt logic và về mặt điện.

Ở chế độ đầu ra bình thường, chân này chỉ phát nội dung của thanh ghi đầu ra. Trong "phương án thay thế", nó được điều khiển bởi các thiết bị ngoại vi tương ứng (xem 9.1.4):

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Nếu một bit cổng được cấu hình làm chân chức năng thay thế, thanh ghi chân sẽ bị vô hiệu hóa và chân này được kết nối với chân ngoại vi.

Chức năng thay thế của mỗi chân được mô tả trong Định nghĩa mã pin Bảng dữ liệu nằm trên hình ảnh được tải xuống. Đối với câu hỏi phải làm gì nếu một chốt có một số chức năng thay thế, câu trả lời được đưa ra bằng chú thích cuối trang trong biểu dữ liệu:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Nếu nhiều thiết bị ngoại vi sử dụng cùng một chân, để tránh xung đột giữa các chức năng thay thế, mỗi lần chỉ nên sử dụng một thiết bị ngoại vi, bật tắt bằng cách sử dụng bit kích hoạt đồng hồ ngoại vi (trong thanh ghi RCC thích hợp).

Cuối cùng, các chân ở chế độ đầu ra cũng có tốc độ xung nhịp. Đây là một tính năng tiết kiệm năng lượng khác; trong trường hợp của chúng tôi, chúng tôi chỉ đặt nó ở mức tối đa và quên nó đi.

Vì vậy: chúng tôi đang sử dụng SPI, có nghĩa là hai chân (có dữ liệu và có tín hiệu đồng hồ) phải là “chức năng kéo đẩy thay thế” và một chân khác (LAT) phải là “chức năng kéo đẩy thông thường”. Nhưng trước khi giao chúng, hãy giải quyết SPI.

SPI

Một chương trình giáo dục nhỏ khác

SPI hay Serial Peripheral Interface (giao diện ngoại vi nối tiếp) là một giao diện đơn giản và rất hiệu quả để kết nối MK với các MK khác và thế giới bên ngoài nói chung. Nguyên lý hoạt động của nó đã được mô tả ở trên, trong đó về trình điều khiển LED Trung Quốc (trong tài liệu tham khảo, xem phần 25). SPI có thể hoạt động ở chế độ master (“master”) và Slave (“slave”). SPI có bốn kênh cơ bản, trong đó không phải tất cả đều có thể được sử dụng:

  • MOSI, Đầu ra chính / Đầu vào phụ: chân này truyền dữ liệu ở chế độ chính và nhận dữ liệu ở chế độ phụ;
  • MISO, Đầu vào chính / Đầu ra nô lệ: ngược lại, nó nhận ở chủ và truyền ở nô lệ;
  • SCK, Serial Clock: đặt tần số truyền dữ liệu ở master hoặc nhận tín hiệu clock ở Slave. Về cơ bản là đánh nhịp;
  • SS, Chọn nô lệ: với sự trợ giúp của kênh này, nô lệ biết rằng mình đang muốn thứ gì đó. Trên STM32 nó được gọi là NSS, trong đó N = âm, tức là bộ điều khiển trở thành nô lệ nếu có mặt đất trong kênh này. Nó kết hợp tốt với chế độ Open Drain Output, nhưng đó lại là một câu chuyện khác.

Giống như mọi thứ khác, SPI trên STM32 có nhiều chức năng nên hơi khó hiểu. Ví dụ, nó có thể hoạt động không chỉ với SPI mà còn với giao diện I2S và trong tài liệu, các mô tả của chúng được trộn lẫn với nhau, cần phải cắt bỏ phần thừa một cách kịp thời. Nhiệm vụ của chúng tôi cực kỳ đơn giản: chúng tôi chỉ cần gửi dữ liệu chỉ bằng MOSI và SCK. Chúng ta đi đến phần 25.3.4 (giao tiếp bán song công, giao tiếp bán song công), nơi chúng ta tìm thấy 1 đồng hồ và 1 dây dữ liệu một chiều (1 tín hiệu đồng hồ và 1 luồng dữ liệu một chiều):

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Ở chế độ này, ứng dụng sử dụng SPI ở chế độ chỉ truyền hoặc chỉ nhận. / Chế độ chỉ truyền tương tự như chế độ song công: dữ liệu được truyền trên chân truyền (MOSI ở chế độ chính hoặc MISO ở chế độ phụ) và chân nhận (tương ứng là MISO hoặc MOSI) có thể được sử dụng làm chân I/O thông thường . Trong trường hợp này, ứng dụng chỉ cần bỏ qua bộ đệm Rx (nếu nó được đọc thì sẽ không có dữ liệu nào được truyền vào đó).

Tuyệt vời, chân MISO còn trống, hãy kết nối tín hiệu LAT với nó. Hãy xem Slave Select, tính năng này trên STM32 có thể được điều khiển theo chương trình, cực kỳ tiện lợi. Chúng ta đọc đoạn văn cùng tên ở mục 25.3.1 SPI General Description:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Điều khiển phần mềm NSS (SSM = 1) / Thông tin lựa chọn nô lệ được chứa trong bit SSI của thanh ghi SPI_CR1. Chân NSS bên ngoài vẫn còn trống cho các nhu cầu ứng dụng khác.

Đã đến lúc ghi vào sổ đăng ký. Tôi quyết định sử dụng SPI2, tìm địa chỉ cơ sở của nó trong biểu dữ liệu - trong phần 3.3 Bản đồ bộ nhớ:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

Nào, hãy bắt đầu:

#define _SPI2_(mem_offset) (*(volatile uint32_t *)(0x40003800 + (mem_offset)))

Mở phần 25.3.3 với tiêu đề tự giải thích “Định cấu hình SPI ở Chế độ chính”:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

1. Đặt tần số xung nhịp nối tiếp bằng bit BR[2:0] trong thanh ghi SPI_CR1.

Các sổ đăng ký được thu thập trong phần hướng dẫn tham khảo cùng tên. Chuyển đổi địa chỉ (Bù địa chỉ) đối với CR1 – 0x00, theo mặc định tất cả các bit đều bị xóa (Đặt lại giá trị 0x0000):

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

Các bit BR thiết lập bộ chia xung nhịp của bộ điều khiển, từ đó xác định tần số mà SPI sẽ hoạt động. Tần số STM32 của chúng tôi sẽ là 72 MHz, trình điều khiển LED, theo bảng dữ liệu của nó, hoạt động với tần số lên đến 25 MHz, vì vậy chúng tôi cần chia cho bốn (BR[2:0] = 001).

#define _SPI_CR1 0x00

#define BR_0        0x0008
#define BR_1        0x0010
#define BR_2        0x0020

_SPI2_ (_SPI_CR1) |= BR_0;// pclk/4

2. Đặt các bit CPOL và CPHA để xác định mối quan hệ giữa truyền dữ liệu và thời gian đồng hồ nối tiếp (xem sơ đồ trang 240)

Vì chúng ta đang đọc biểu dữ liệu ở đây và không nhìn vào sơ đồ, chúng ta hãy xem xét kỹ hơn mô tả văn bản của các bit CPOL và CPHA ở trang 704 (Mô tả chung SPI):

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Pha đồng hồ và cực tính
Bằng cách sử dụng các bit CPOL và CPHA của thanh ghi SPI_CR1, bạn có thể chọn bốn mối quan hệ định thời theo chương trình. Bit CPOL (đồng hồ phân cực) kiểm soát trạng thái của tín hiệu đồng hồ khi không có dữ liệu nào được truyền đi. Bit này điều khiển chế độ master và Slave. Nếu CPOL được đặt lại, chân SCK ở mức thấp ở chế độ nghỉ. Nếu bit CPOL được đặt thì chân SCK ở mức cao trong chế độ nghỉ.
Khi bit CPHA (pha đồng hồ) được đặt, nhấp nháy bẫy bit cao là cạnh thứ hai của tín hiệu SCK (giảm nếu CPOL rõ ràng, tăng nếu CPOL được đặt). Dữ liệu được ghi lại bởi sự thay đổi thứ hai trong tín hiệu đồng hồ. Nếu bit CPHA bị xóa, nhấp nháy bẫy bit cao là cạnh tăng của tín hiệu SCK (cạnh giảm nếu CPOL được đặt, cạnh tăng nếu CPOL bị xóa). Dữ liệu được ghi lại ở lần thay đổi đầu tiên trong tín hiệu đồng hồ.

Sau khi tiếp thu kiến ​​thức này, chúng ta đi đến kết luận rằng cả hai bit phải bằng XNUMX, bởi vì Chúng ta muốn tín hiệu SCK duy trì ở mức thấp khi không sử dụng và dữ liệu được truyền ở cạnh lên của xung (xem Hình XNUMX). tăng cạnh trong biểu dữ liệu DM634).

Nhân tiện, ở đây lần đầu tiên chúng ta bắt gặp một đặc điểm của từ vựng trong bảng dữ liệu ST: trong đó có viết cụm từ “đặt lại bit về XNUMX” để thiết lập lại một chút, А не để xóa một chút, chẳng hạn như Atmega.

3. Đặt bit DFF để xác định xem khối dữ liệu có định dạng 8 bit hay 16 bit

Tôi đặc biệt sử dụng DM16 634 bit để không phải bận tâm đến việc truyền dữ liệuPWM 12 bit, như DM633. Thật hợp lý khi đặt DFF thành một:

#define DFF         0x0800

_SPI2_ (_SPI_CR1) |= DFF; // 16-bit mode

4. Cấu hình bit LSBFIRST trong thanh ghi SPI_CR1 để xác định định dạng khối

LSBFIRST, như tên gọi của nó, trước tiên cấu hình việc truyền với bit ít quan trọng nhất. Nhưng DM634 muốn nhận dữ liệu bắt đầu từ bit quan trọng nhất. Vì vậy, chúng tôi để nó thiết lập lại.

5. Ở chế độ phần cứng, nếu cần có đầu vào từ chân NSS, hãy cấp tín hiệu cao cho chân NSS trong toàn bộ chuỗi truyền byte. Trong chế độ phần mềm NSS, đặt bit SSM và SSI trong thanh ghi SPI_CR1. Nếu chân NSS được sử dụng làm đầu ra thì chỉ cần đặt bit SSOE.

Cài đặt SSM và SSI để quên chế độ phần cứng NSS:

#define SSI         0x0100
#define SSM         0x0200

_SPI2_ (_SPI_CR1) |= SSM | SSI; //enable software control of SS, SS high

6. Các bit MSTR và SPE phải được đặt (chúng chỉ được đặt nếu tín hiệu NSS cao)

Trên thực tế, với những bit này, chúng tôi chỉ định SPI của mình là SPI chính và bật nó lên:

#define MSTR        0x0004
#define SPE         0x0040

_SPI2_ (_SPI_CR1) |= MSTR; //SPI master
//когда все готово, включаем SPI
_SPI2_ (_SPI_CR1) |= SPE;

SPI đã được cấu hình, hãy viết ngay các hàm gửi byte cho trình điều khiển. Tiếp tục đọc 25.3.3 “Định cấu hình SPI ở chế độ chính”:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Lệnh truyền dữ liệu
Quá trình truyền bắt đầu khi một byte được ghi vào bộ đệm Tx.
Byte dữ liệu được nạp vào thanh ghi dịch chuyển tại song song (từ bus nội bộ) trong quá trình truyền bit đầu tiên, sau đó nó được truyền tới tuần tự Chế độ chân MOSI, chuyển tiếp bit đầu tiên hoặc cuối cùng tùy thuộc vào cài đặt của bit LSBFIRST trong thanh ghi CPI_CR1. Cờ TXE được đặt sau khi truyền dữ liệu từ bộ đệm Tx tới thanh ghi dịch chuyểnvà cũng tạo ra ngắt nếu bit TXEIE trong thanh ghi CPI_CR1 được đặt.

Tôi đã nhấn mạnh một số từ trong bản dịch để thu hút sự chú ý đến một tính năng của việc triển khai SPI trong bộ điều khiển STM. Trên Atmega cờ TXE (Tx trống, Tx trống và sẵn sàng nhận dữ liệu) chỉ được đặt sau khi toàn bộ byte đã được gửi ra. Và ở đây cờ này được đặt sau khi byte được chèn vào thanh ghi dịch chuyển bên trong. Vì nó được đẩy đến đó với tất cả các bit cùng lúc (song song) và sau đó dữ liệu được truyền tuần tự, nên TXE được đặt trước khi byte được gửi hoàn toàn. Điều này quan trọng vì trong trường hợp trình điều khiển LED, chúng ta cần kéo chốt LAT sau khi gửi của tất cả dữ liệu, tức là Chỉ cờ TXE sẽ không đủ đối với chúng tôi.

Điều này có nghĩa là chúng ta cần một lá cờ khác. Hãy nhìn vào 25.3.7 - “Cờ trạng thái”:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
<…>
Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
cờ BẬN
Cờ BSY được đặt và xóa bằng phần cứng (việc ghi vào nó không có hiệu lực). Cờ BSY cho biết trạng thái của lớp giao tiếp SPI.
Nó đặt lại:
khi quá trình truyền hoàn tất (ngoại trừ ở chế độ chính nếu quá trình truyền liên tục)
khi SPI bị tắt
khi xảy ra lỗi chế độ chính (MODF=1)
Nếu quá trình truyền không liên tục, cờ BSY sẽ bị xóa giữa mỗi lần truyền dữ liệu

Được rồi, điều này sẽ có ích. Chúng ta hãy tìm xem bộ đệm Tx nằm ở đâu. Để thực hiện việc này, hãy đọc “Đăng ký dữ liệu SPI”:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Bit 15:0 DR[15:0] Thanh ghi dữ liệu
Dữ liệu nhận được hoặc dữ liệu được truyền đi.
Thanh ghi dữ liệu được chia thành hai bộ đệm - một để ghi (bộ đệm truyền) và một để đọc (bộ đệm nhận). Việc ghi vào thanh ghi dữ liệu sẽ ghi vào bộ đệm Tx và việc đọc từ thanh ghi dữ liệu sẽ trả về giá trị có trong bộ đệm Rx.

Vâng, và thanh ghi trạng thái, nơi tìm thấy cờ TXE và BSY:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

Chúng tôi viết:

#define _SPI_DR  0x0C
#define _SPI_SR  0x08

#define BSY         0x0080
#define TXE         0x0002

void dm_shift16(uint16_t value)
{
    _SPI2_(_SPI_DR) = value; //send 2 bytes
    while (!(_SPI2_(_SPI_SR) & TXE)); //wait until they're sent
}

Chà, vì chúng ta cần truyền 16 lần hai byte, theo số lượng đầu ra của trình điều khiển LED, đại loại như thế này:

void sendLEDdata()
{
    LAT_low();
    uint8_t k = 16;
    do
    {   k--;
        dm_shift16(leds[k]);
    } while (k);

    while (_SPI2_(_SPI_SR) & BSY); // finish transmission

    LAT_pulse();
}

Nhưng chúng ta chưa biết cách kéo chốt LAT nên chúng ta sẽ quay lại I/O.

Gán ghim

Trong STM32F1, các thanh ghi chịu trách nhiệm về trạng thái của các chân khá bất thường. Rõ ràng là có nhiều chip hơn Atmega, nhưng chúng cũng khác với các chip STM khác. Phần 9.1 Mô tả chung về GPIO:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Mỗi cổng I/O có mục đích chung (GPIO) có hai thanh ghi cấu hình 32 bit (GPIOx_CRL và GPIOx_CRH), hai thanh ghi dữ liệu 32 bit (GPIOx_IDR và ​​GPIOx_ODR), một thanh ghi đặt/đặt lại 32 bit (GPIOx_BSRR), một thanh ghi đặt lại 16 bit (GPIOx_BRR) và một thanh ghi 32-bit. thanh ghi chặn bit (GPIOx_LCKR).

Hai thanh ghi đầu tiên không bình thường và cũng khá bất tiện vì 16 chân cổng nằm rải rác trên chúng theo định dạng “bốn bit cho mỗi anh em”. Những thứ kia. các chân từ XNUMX đến XNUMX nằm trong CRL và các chân còn lại nằm trong CRH. Đồng thời, các thanh ghi còn lại chứa thành công các bit của tất cả các chân của cổng - thường còn lại một nửa “dành riêng”.

Để đơn giản, hãy bắt đầu từ cuối danh sách.

Chúng tôi không cần một đăng ký chặn.

Các thanh ghi đặt và đặt lại khá buồn cười ở chỗ chúng trùng lặp một phần với nhau: bạn chỉ có thể ghi mọi thứ trong BSRR, trong đó 16 bit cao hơn sẽ đặt lại mã pin về 1 và các bit thấp hơn sẽ được đặt thành 16 hoặc bạn cũng có thể sử dụng BRR, XNUMX bit thấp hơn chỉ đặt lại chân. Tôi thích lựa chọn thứ hai. Các thanh ghi này rất quan trọng vì chúng cung cấp quyền truy cập nguyên tử vào các chân:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Đặt nguyên tử hoặc đặt lại
Không cần phải tắt các ngắt khi lập trình GPIOx_ODR ở cấp độ bit: một hoặc nhiều bit có thể được thay đổi bằng một thao tác ghi nguyên tử duy nhất APB2. Điều này đạt được bằng cách ghi "1" vào thanh ghi thiết lập/đặt lại (GPIOx_BSRR hoặc, chỉ dành cho thiết lập lại, GPIOx_BRR) của bit cần thay đổi. Các bit khác sẽ không thay đổi.

Các thanh ghi dữ liệu có tên khá dễ hiểu - IDR = Đầu vào Thanh ghi hướng, thanh ghi đầu vào; ODR = Đầu ra Thanh ghi hướng, thanh ghi đầu ra. Chúng tôi sẽ không cần chúng trong dự án hiện tại.

Và cuối cùng là các thanh ghi điều khiển. Vì chúng tôi quan tâm đến các chân SPI thứ hai, cụ thể là PB13, PB14 và PB15, nên chúng tôi xem xét ngay CRH:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

Và chúng ta thấy rằng chúng ta sẽ cần viết một cái gì đó theo bit từ 20 đến 31.

Chúng tôi đã tìm ra ở trên những gì chúng tôi muốn từ các chân, vì vậy ở đây tôi sẽ làm mà không cần ảnh chụp màn hình, tôi chỉ nói rằng MODE chỉ định hướng (đầu vào nếu cả hai bit được đặt thành 0) và tốc độ chân (chúng tôi cần 50 MHz, tức là. cả hai chân đều là “1”) và CNF đặt chế độ: “push-pull” thông thường – 00, “alternative” – 10. Theo mặc định, như chúng ta thấy ở trên, tất cả các chân đều có bit thứ ba từ dưới lên (CNF0), nó đặt chúng ở chế độ đầu vào nổi.

Vì tôi dự định làm điều gì đó khác với con chip này nên để đơn giản, tôi đã xác định tất cả các giá trị MODE và CNF có thể có cho cả thanh ghi điều khiển trên và dưới.

Bằng cách nào đó như thế này

#define CNF0_0 0x00000004
#define CNF0_1 0x00000008
#define CNF1_0 0x00000040
#define CNF1_1 0x00000080
#define CNF2_0 0x00000400
#define CNF2_1 0x00000800
#define CNF3_0 0x00004000
#define CNF3_1 0x00008000
#define CNF4_0 0x00040000
#define CNF4_1 0x00080000
#define CNF5_0 0x00400000
#define CNF5_1 0x00800000
#define CNF6_0 0x04000000
#define CNF6_1 0x08000000
#define CNF7_0 0x40000000
#define CNF7_1 0x80000000
#define CNF8_0 0x00000004
#define CNF8_1 0x00000008
#define CNF9_0 0x00000040
#define CNF9_1 0x00000080
#define CNF10_0 0x00000400
#define CNF10_1 0x00000800
#define CNF11_0 0x00004000
#define CNF11_1 0x00008000
#define CNF12_0 0x00040000
#define CNF12_1 0x00080000
#define CNF13_0 0x00400000
#define CNF13_1 0x00800000
#define CNF14_0 0x04000000
#define CNF14_1 0x08000000
#define CNF15_0 0x40000000
#define CNF15_1 0x80000000

#define MODE0_0 0x00000001
#define MODE0_1 0x00000002
#define MODE1_0 0x00000010
#define MODE1_1 0x00000020
#define MODE2_0 0x00000100
#define MODE2_1 0x00000200
#define MODE3_0 0x00001000
#define MODE3_1 0x00002000
#define MODE4_0 0x00010000
#define MODE4_1 0x00020000
#define MODE5_0 0x00100000
#define MODE5_1 0x00200000
#define MODE6_0 0x01000000
#define MODE6_1 0x02000000
#define MODE7_0 0x10000000
#define MODE7_1 0x20000000
#define MODE8_0 0x00000001
#define MODE8_1 0x00000002
#define MODE9_0 0x00000010
#define MODE9_1 0x00000020
#define MODE10_0 0x00000100
#define MODE10_1 0x00000200
#define MODE11_0 0x00001000
#define MODE11_1 0x00002000
#define MODE12_0 0x00010000
#define MODE12_1 0x00020000
#define MODE13_0 0x00100000
#define MODE13_1 0x00200000
#define MODE14_0 0x01000000
#define MODE14_1 0x02000000
#define MODE15_0 0x10000000
#define MODE15_1 0x20000000

Các chân của chúng tôi được đặt trên cổng B (địa chỉ cơ sở – 0x40010C00), mã:

#define _PORTB_(mem_offset) (*(volatile uint32_t *)(0x40010C00 + (mem_offset)))

#define _BRR  0x14
#define _BSRR 0x10
#define _CRL  0x00
#define _CRH  0x04

//используем стандартный SPI2: MOSI на B15, CLK на B13
//LAT пусть будет на неиспользуемом MISO – B14

//очищаем дефолтный бит, он нам точно не нужен
_PORTB_ (_CRH) &= ~(CNF15_0 | CNF14_0 | CNF13_0 | CNF12_0);

//альтернативные функции для MOSI и SCK
_PORTB_ (_CRH) |= CNF15_1 | CNF13_1;

//50 МГц, MODE = 11
_PORTB_ (_CRH) |= MODE15_1 | MODE15_0 | MODE14_1 | MODE14_0 | MODE13_1 | MODE13_0;

Và theo đó, bạn có thể viết các định nghĩa cho LAT, sẽ được điều chỉnh bởi các thanh ghi BRR và BSRR:

/*** LAT pulse – high, then low */
#define LAT_pulse() _PORTB_(_BSRR) = (1<<14); _PORTB_(_BRR) = (1<<14)

#define LAT_low() _PORTB_(_BRR) = (1<<14)

(LAT_thấp theo quán tính thôi, vốn dĩ là thế rồi, cứ để vậy đi)

Bây giờ mọi thứ đều tuyệt vời, nhưng nó không hoạt động. Vì đây là STM32 nên chúng tiết kiệm điện, nghĩa là bạn cần kích hoạt tính năng xung nhịp của các thiết bị ngoại vi cần thiết.

Bật đồng hồ

Đồng hồ hay còn gọi là Đồng hồ có nhiệm vụ bấm giờ. Và chúng ta đã có thể nhận thấy chữ viết tắt RCC. Chúng tôi tìm kiếm nó trong tài liệu: đây là Reset and Clock Control.

Như đã nói ở trên, may mắn thay, phần khó nhất của chủ đề đồng hồ đã được những người từ STM thực hiện cho chúng tôi, vì vậy chúng tôi cảm ơn họ rất nhiều (một lần nữa tôi sẽ đưa đường dẫn tới Trang web của Di Halt, để làm rõ nó khó hiểu đến mức nào). Chúng tôi chỉ cần các thanh ghi chịu trách nhiệm kích hoạt đồng hồ ngoại vi (Thanh ghi kích hoạt đồng hồ ngoại vi). Đầu tiên, chúng ta hãy tìm địa chỉ cơ sở của RCC, nó nằm ở phần đầu của “Bản đồ bộ nhớ”:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

#define _RCC_(mem_offset) (*(volatile uint32_t *)(0x40021000 + (mem_offset)))

Sau đó nhấp vào liên kết nơi bạn cố gắng tìm thứ gì đó trong bảng hoặc tốt hơn nhiều là xem qua mô tả về các thanh ghi kích hoạt từ các phần về kích hoạt đăng ký. Nơi chúng tôi sẽ tìm thấy RCC_APB1ENR và RCC_APB2ENR:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

Và theo đó, chúng chứa các bit bao gồm xung nhịp của SPI2, IOPB (Cổng I/O B) và các chức năng thay thế (AFIO).

#define _APB2ENR 0x18
#define _APB1ENR 0x1C

#define IOPBEN 0x0008
#define SPI2EN 0x4000
#define AFIOEN 0x0001

//включаем тактирование порта B и альт. функций
_RCC_(_APB2ENR) |= IOPBEN | AFIOEN;

//включаем  тактирование SPI2
_RCC_(_APB1ENR) |= SPI2EN;

Mã cuối cùng có thể được tìm thấy đây.

Nếu bạn có cơ hội và mong muốn thử nghiệm thì hãy kết nối DM634 như sau: DAI với PB15, DCK với PB13, LAT với PB14. Chúng tôi cấp nguồn cho trình điều khiển từ 5 volt, đừng quên kết nối đất.

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

STM8PWM

PWM trên STM8

Ví dụ, khi tôi chuẩn bị viết bài này, tôi đã quyết định cố gắng thành thạo một số chức năng của một con chip lạ chỉ bằng cách sử dụng một biểu dữ liệu để không phải trở thành một thợ đóng giày không có ủng. STM8 rất lý tưởng cho vai trò này: thứ nhất, tôi có một vài bảng Trung Quốc với STM8S103, và thứ hai, nó không phổ biến lắm, và do đó, sự cám dỗ để đọc và tìm giải pháp trên Internet là do thiếu chính những giải pháp này.

Con chip này cũng có bảng dữliệu и tài liệu tham khảo RM0016, trong phần đầu tiên có địa chỉ sơ đồ chân và đăng ký, trong phần thứ hai - mọi thứ khác. STM8 được lập trình bằng C trong một IDE khủng khiếp ST trực quan phát triển.

Bấm giờ và I/O

Theo mặc định, STM8 hoạt động ở tần số 2 MHz, điều này phải được sửa ngay lập tức.

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Đồng hồ HSI (Nội bộ tốc độ cao)
Tín hiệu đồng hồ HSI được lấy từ bộ tạo dao động RC 16 MHz bên trong với bộ chia có thể lập trình (1 đến 8). Nó được đặt trong thanh ghi bộ chia xung nhịp (CLK_CKDIVR).
Lưu ý: khi bắt đầu, bộ tạo dao động HSI RC có bộ chia 8 được chọn làm nguồn tín hiệu đồng hồ hàng đầu.

Chúng tôi tìm địa chỉ thanh ghi trong biểu dữ liệu, mô tả trong refman và thấy rằng thanh ghi cần được xóa:

#define CLK_CKDIVR *(volatile uint8_t *)0x0050C6

CLK_CKDIVR &= ~(0x18);

Vì chúng ta sẽ chạyPWM và kết nối các đèn LED, chúng ta hãy xem sơ đồ chân:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

Con chip nhỏ, nhiều chức năng được treo trên cùng một chân. Nội dung trong ngoặc vuông là “chức năng thay thế”, nó được chuyển đổi bằng “byte tùy chọn” (byte tùy chọn) – thứ gì đó giống như cầu chì Atmega. Bạn có thể thay đổi giá trị của chúng theo chương trình, nhưng điều đó là không cần thiết, bởi vì Chức năng mới chỉ được kích hoạt sau khi khởi động lại. Việc sử dụng ST Visual Programmer (được tải xuống bằng Visual Development) sẽ dễ dàng hơn, có thể thay đổi các byte này. Sơ đồ chân cho thấy các chân CH1 và CH2 của bộ hẹn giờ đầu tiên được ẩn trong dấu ngoặc vuông; cần phải đặt các bit AFR1 và AFR0 trong STVP, đồng thời bit thứ hai cũng sẽ chuyển đầu ra CH1 của bộ định thời thứ hai từ PD4 sang PC5.

Do đó, 6 chân sẽ điều khiển các đèn LED: PC6, PC7 và PC3 cho bộ hẹn giờ đầu tiên, PC5, PD3 và PA3 cho bộ hẹn giờ thứ hai.

Việc tự thiết lập các chân I/O trên STM8 đơn giản và logic hơn trên STM32:

  • quen thuộc từ thanh ghi hướng dữ liệu Atmega DDR (Thanh ghi hướng dữ liệu): 1 = đầu ra;
  • thanh ghi điều khiển đầu tiên CR1, khi xuất ra, đặt chế độ kéo đẩy (1) hoặc mở cống (0); vì tôi kết nối đèn LED với chip bằng cực âm nên tôi để lại các số XNUMX ở đây;
  • thanh ghi điều khiển thứ hai CR2, khi xuất ra, đặt tốc độ xung nhịp: 1 = 10 MHz

#define PA_DDR     *(volatile uint8_t *)0x005002
#define PA_CR2     *(volatile uint8_t *)0x005004
#define PD_DDR     *(volatile uint8_t *)0x005011
#define PD_CR2     *(volatile uint8_t *)0x005013
#define PC_DDR     *(volatile uint8_t *)0x00500C
#define PC_CR2     *(volatile uint8_t *)0x00500E

PA_DDR = (1<<3); //output
PA_CR2 |= (1<<3); //fast
PD_DDR = (1<<3); //output
PD_CR2 |= (1<<3); //fast
PC_DDR = ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //output
PC_CR2 |= ((1<<3) | (1<<5) | (1<<6) | (1<<7)); //fast

Cài đặt xung điện

Đầu tiên, hãy xác định các thuật ngữ:

  • Tần số PWM – tần số mà bộ đếm thời gian tích tắc;
  • Tự động tải lại, AR - giá trị có thể tự động tải mà bộ đếm thời gian sẽ đếm (chu kỳ xung);
  • Cập nhật sự kiện, UEV – một sự kiện xảy ra khi bộ đếm thời gian đã đếm đến AR;
  • Chu kỳ nhiệm vụ củaPWM - Chu kỳ nhiệm vụ củaPWM, thường được gọi là “hệ số nhiệm vụ”;
  • Chụp/So sánh giá trị – giá trị để thu thập/so sánh mà bộ đếm thời gian đã tính đến sẽ làm gì đó (trong trường hợp củaPWM, nó đảo ngược tín hiệu đầu ra);
  • Giá trị tải trước - giá trị được tải trước. So sánh giá trị không thể thay đổi trong khi bộ đếm thời gian đang tích tắc, nếu không chu kỳ xung sẽ bị hỏng. Do đó, các giá trị được truyền mới được đặt trong bộ đệm và được lấy ra khi bộ đếm thời gian kết thúc quá trình đếm ngược và được đặt lại;
  • Căn chỉnh theo cạnh и Chế độ căn giữa – căn chỉnh dọc theo đường viền và ở trung tâm, giống như của Atmel PWM nhanh и Pha đúng pha.
  • OCiREF, Tín hiệu tham chiếu so sánh đầu ra – trên thực tế, tín hiệu đầu ra tham chiếu là tín hiệu xuất hiện trên chân tương ứng ở chế độPWM.

Như đã thấy rõ từ sơ đồ chân, hai bộ định thời có khả năng điều khiển xung điện - bộ định thời thứ nhất và bộ định thời thứ hai. Cả hai đều là 16-bit, cái đầu tiên có rất nhiều tính năng bổ sung (đặc biệt là nó có thể đếm cả lên và xuống). Chúng tôi cần cả hai hoạt động như nhau, vì vậy tôi quyết định bắt đầu với cái thứ hai rõ ràng là kém hơn, để không vô tình sử dụng thứ gì đó không có ở đó. Một số vấn đề là mô tả chức năngPWM của tất cả các bộ định thời trong tài liệu tham khảo nằm trong chương về bộ định thời đầu tiên (Chế độ 17.5.7PWM), vì vậy bạn phải nhảy qua nhảy lại trong suốt tài liệu.

PWM trên STM8 có lợi thế quan trọng so vớiPWM trên Atmega:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Biên giới liên kết xung quanh
Cấu hình tài khoản từ dưới lên trên
Tính từ dưới lên được kích hoạt nếu bit DIR trong thanh ghi TIM_CR1 bị xóa
Ví dụ
Ví dụ sử dụng chế độ xung đầu tiên. Tín hiệu tham chiếu PLC OCiREF được giữ ở mức cao miễn là TIM1_CNT < TIM1_CCRi. Nếu không thì nó có mức độ thấp. Nếu giá trị so sánh trong thanh ghi TIM1_CCRi lớn hơn giá trị tự động tải (thanh ghi TIM1_ARR), tín hiệu OCiREF được giữ ở mức 1. Nếu giá trị so sánh là 0 thì OCiREF được giữ ở mức XNUMX....

Hẹn giờ STM8 trong khi cập nhật sự kiện kiểm tra trước so sánh giá trịvà chỉ sau đó tạo ra tín hiệu tham chiếu. Bộ đếm thời gian của Atmega đầu tiên vặn vít và sau đó so sánh, dẫn đến compare value == 0 đầu ra là một cái kim, phải được xử lý bằng cách nào đó (ví dụ: bằng cách đảo ngược logic theo chương trình).

Vì vậy, những gì chúng tôi muốn làm:PWM 8 bit (AR == 255), đếm từ dưới lên trên, căn dọc theo đường viền. Vì các bóng đèn được kết nối với chip bằng cực âm nên xung điện sẽ xuất ra 0 (bật đèn LED) cho đến khi so sánh giá trị và 1 sau đó.

Chúng tôi đã đọc về một số Chế độ PWM, vì vậy chúng tôi tìm thấy thanh ghi cần thiết của bộ đếm thời gian thứ hai bằng cách tìm kiếm trong hướng dẫn tham khảo cho cụm từ này (18.6.8 - TIMx_CCMR1):

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
110: Chế độ xung đầu tiên – khi đếm từ dưới lên trên, kênh đầu tiên hoạt động khi TIMx_CNT < TIMx_CCR1. Nếu không, kênh đầu tiên không hoạt động. [thêm trong tài liệu còn có lỗi sao chép-dán từ bộ đếm thời gian 1] 111: Chế độ xung thứ hai – khi đếm từ dưới lên trên, kênh đầu tiên không hoạt động trong khi TIMx_CNT < TIMx_CCR1. Nếu không thì kênh đầu tiên đang hoạt động.

Vì đèn LED được kết nối với MK bằng cực âm nên chế độ thứ hai phù hợp với chúng tôi (chế độ đầu tiên cũng vậy, nhưng chúng tôi chưa biết điều đó).

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Bit 3 OC1PE: Cho phép tải trước chân 1
0: Đăng ký tải trước trên TIMx_CCR1 bị tắt. Bạn có thể viết thư cho TIMx_CCR1 bất cứ lúc nào. Giá trị mới hoạt động ngay lập tức.
1: Đăng ký tải trước trên TIMx_CCR1 được bật. Hoạt động đọc/ghi truy cập vào thanh ghi tải trước. Giá trị tải trước TIMx_CCR1 được tải vào thanh ghi ẩn trong mỗi sự kiện cập nhật.
*Lưu ý: Để chế độPWM hoạt động bình thường, các thanh ghi tải trước phải được bật. Điều này là không cần thiết trong chế độ tín hiệu đơn (bit OPM được đặt trong thanh ghi TIMx_CR1).

Được rồi, hãy bật mọi thứ chúng ta cần cho ba kênh của bộ hẹn giờ thứ hai:

#define TIM2_CCMR1 *(volatile uint8_t *)0x005307
#define TIM2_CCMR2 *(volatile uint8_t *)0x005308
#define TIM2_CCMR3 *(volatile uint8_t *)0x005309

#define PWM_MODE2   0x70 //PWM mode 2, 0b01110000
#define OCxPE       0x08 //preload enable

TIM2_CCMR1 = (PWM_MODE2 | OCxPE);
TIM2_CCMR2 = (PWM_MODE2 | OCxPE);
TIM2_CCMR3 = (PWM_MODE2 | OCxPE);

AR bao gồm hai thanh ghi tám bit, mọi thứ đều đơn giản:

#define TIM2_ARRH  *(volatile uint8_t *)0x00530F
#define TIM2_ARRL  *(volatile uint8_t *)0x005310

TIM2_ARRH = 0;
TIM2_ARRL = 255;

Bộ đếm thời gian thứ hai chỉ có thể đếm từ dưới lên trên, căn chỉnh dọc theo đường viền, không cần thay đổi gì. Ví dụ: hãy đặt bộ chia tần số thành 256. Đối với bộ định thời thứ hai, bộ chia được đặt trong thanh ghi TIM2_PSCR và là lũy thừa của hai:

#define TIM2_PSCR  *(volatile uint8_t *)0x00530E

TIM2_PSCR = 8;

Tất cả những gì còn lại là bật kết luận và đồng hồ bấm giờ thứ hai. Vấn đề đầu tiên được giải quyết bằng cách đăng ký Chụp/So sánh Kích hoạt tính năng: có hai, ba kênh nằm rải rác không đối xứng. Ở đây chúng ta cũng có thể biết rằng có thể thay đổi cực tính của tín hiệu, tức là về nguyên tắc, có thể sử dụng Chế độ 1. Chúng tôi viết:

#define TIM2_CCER1 *(volatile uint8_t *)0x00530A
#define TIM2_CCER2 *(volatile uint8_t *)0x00530B

#define CC1E  (1<<0) // CCER1
#define CC2E  (1<<4) // CCER1
#define CC3E  (1<<0) // CCER2

TIM2_CCER1 = (CC1E | CC2E);
TIM2_CCER2 = CC3E;

Và cuối cùng, chúng ta khởi động bộ đếm thời gian trong thanh ghi TIMx_CR1:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

#define TIM2_CR1   *(volatile uint8_t *)0x005300

TIM2_CR1 |= 1;

Hãy viết một phép tương tự đơn giản của AnalogWrite(), nó sẽ chuyển các giá trị thực tế sang bộ đếm thời gian để so sánh. Các thanh ghi được đặt tên theo dự đoán Ghi lại/so sánh các thanh ghi, có hai trong số chúng cho mỗi kênh: 8 bit bậc thấp trong TIM2_CCRxL và bit bậc cao trong TIM2_CCRxH. Vì chúng ta đã tạo một xung 8 bit nên chỉ cần ghi các bit có ý nghĩa nhỏ nhất là đủ:

#define TIM2_CCR1L *(volatile uint8_t *)0x005312
#define TIM2_CCR2L *(volatile uint8_t *)0x005314
#define TIM2_CCR3L *(volatile uint8_t *)0x005316

void setRGBled(uint8_t r, uint8_t g, uint8_t b)
{
    TIM2_CCR1L = r;
    TIM2_CCR2L = g;
    TIM2_CCR3L = b;
}

Người đọc chú ý sẽ nhận thấy rằng chúng ta có một bộ điều khiển xung điện tử hơi bị lỗi, không thể tạo ra mức lấp đầy 100% (ở giá trị tối đa là 255, tín hiệu sẽ bị đảo ngược trong một chu kỳ hẹn giờ). Đối với đèn LED, điều này không thành vấn đề và người đọc chú ý có thể đoán được cách khắc phục.

Chế độ điều khiển xung quanh bộ hẹn giờ thứ hai đang hoạt động, hãy chuyển sang bộ hẹn giờ đầu tiên.

Bộ hẹn giờ đầu tiên có các bit giống hệt nhau trong cùng một thanh ghi (chỉ là những bit vẫn được "dành riêng" trong bộ hẹn giờ thứ hai sẽ được sử dụng tích cực trong bộ hẹn giờ đầu tiên cho tất cả những thứ nâng cao). Do đó, chỉ cần tìm địa chỉ của các thanh ghi giống nhau trong biểu dữ liệu và sao chép mã là đủ. Vâng, hãy thay đổi giá trị của bộ chia tần số, bởi vì... bộ hẹn giờ đầu tiên muốn nhận không phải lũy thừa bằng hai mà là giá trị 16 bit chính xác trong hai thanh ghi Bộ đếm gộp cao и Thấp. Chúng tôi làm mọi thứ và... bộ đếm thời gian đầu tiên không hoạt động. Có chuyện gì vậy?

Vấn đề chỉ có thể được giải quyết bằng cách xem qua toàn bộ phần về các thanh ghi điều khiển của bộ định thời 1, trong đó chúng ta tìm kiếm thanh ghi mà bộ định thời thứ hai không có. Sẽ có 17.7.30 Đăng ký nghỉ (TIM1_BKR), nơi có bit này:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Kích hoạt đầu ra chính

#define TIM1_BKR   *(volatile uint8_t *)0x00526D

TIM1_BKR = (1<<7);

Bây giờ tất cả đều chắc chắn, mã ở đó.

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

Bộ ghép kênh STM8

Ghép kênh trên STM8

Dự án nhỏ thứ ba là kết nối tám đèn LED RGB với bộ hẹn giờ thứ hai ở chế độPWM và làm cho chúng hiển thị các màu khác nhau. Nó dựa trên khái niệm ghép kênh LED, nghĩa là nếu bạn bật và tắt đèn LED rất nhanh, chúng ta sẽ thấy rằng chúng liên tục bật (bền bỉ của tầm nhìn, quán tính của nhận thức thị giác). Tôi đã từng làm một cái gì đó như thế này trên Arduino.

Thuật toán công việc trông như thế này:

  • kết nối cực dương của đèn LED RGB đầu tiên;
  • thắp sáng nó, gửi các tín hiệu cần thiết đến cực âm;
  • đợi cho đến khi kết thúc chu kỳPWM;
  • kết nối cực dương của đèn LED RGB thứ hai;
  • thắp sáng nó...

Vâng, v.v. Tất nhiên, để hoạt động tốt, cần phải kết nối cực dương và đèn LED “bốc cháy” cùng một lúc. Vâng, hoặc gần như vậy. Trong mọi trường hợp, chúng ta cần viết một mã sẽ xuất các giá trị trong ba kênh của bộ hẹn giờ thứ hai, thay đổi chúng khi đạt đến UEV, đồng thời thay đổi đèn LED RGB hiện đang hoạt động.

Vì việc chuyển đổi đèn LED là tự động nên chúng ta cần tạo một "bộ nhớ video" để bộ xử lý ngắt sẽ nhận dữ liệu từ đó. Đây là một mảng đơn giản:

uint8_t colors[8][3];

Để thay đổi màu của một đèn LED cụ thể, chỉ cần ghi các giá trị cần thiết vào mảng này là đủ. Và biến sẽ chịu trách nhiệm về số lượng đèn LED hoạt động

uint8_t cnt;

Demux

Để ghép kênh thích hợp, thật kỳ lạ, chúng ta cần một bộ tách kênh CD74HC238. Demultiplexer - một con chip thực hiện toán tử trong phần cứng <<. Thông qua ba chân đầu vào (bit 0, 1 và 2), chúng tôi cung cấp cho nó một số X ba bit và để đáp lại, nó sẽ kích hoạt số đầu ra (1<<X). Các đầu vào còn lại của chip được sử dụng để mở rộng quy mô toàn bộ thiết kế. Chúng ta cần con chip này không chỉ để giảm số lượng chân chiếm dụng của vi điều khiển mà còn vì sự an toàn - để không vô tình bật nhiều đèn LED hơn mức có thể và không làm cháy MK. Con chip này có giá một xu và phải luôn được giữ trong tủ thuốc gia đình của bạn.

CD74HC238 của chúng tôi sẽ chịu trách nhiệm cung cấp điện áp cho cực dương của đèn LED mong muốn. Trong một bộ ghép kênh hoàn chỉnh, nó sẽ cung cấp điện áp cho cột thông qua P-MOSFET, nhưng trong bản demo này thì điều đó có thể thực hiện được một cách trực tiếp, bởi vì nó rút ra 20 mA, theo xếp hạng tối đa tuyệt đối trong bảng dữ liệu. Từ bảng dữ liệu CD74HC238 chúng ta cần sơ đồ chân và bảng ghi chú này:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
H = cấp điện áp cao, L = cấp điện áp thấp, X – không quan tâm

Chúng tôi kết nối E2 và E1 với mặt đất, E3, A0, A1 và A3 với các chân PD5, PC3, PC4 và PC5 của STM8. Vì bảng trên chứa cả mức thấp và mức cao nên chúng tôi định cấu hình các chân này là các chân kéo đẩy.

PWM

Chế độ điều chỉnh xung xung điện trên bộ hẹn giờ thứ hai được cấu hình theo cách tương tự như trong câu chuyện trước, với hai điểm khác biệt:

Đầu tiên, chúng ta cần kích hoạt ngắt trên Cập nhật sự kiện (UEV) sẽ gọi một chức năng bật tắt đèn LED đang hoạt động. Điều này được thực hiện bằng cách thay đổi bit Kích hoạt ngắt cập nhật trong một sổ đăng ký với một cái tên đáng chú ý

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
Đăng ký cho phép ngắt

#define TIM2_IER   *(volatile uint8_t *)0x005303

//enable interrupt
TIM2_IER = 1;

Sự khác biệt thứ hai liên quan đến hiện tượng ghép kênh, chẳng hạn như bóng ma - ánh sáng ký sinh của điốt. Trong trường hợp của chúng tôi, nó có thể xuất hiện do bộ hẹn giờ, đã gây ra gián đoạn trên UEV, tiếp tục tích tắc và bộ xử lý ngắt không có thời gian để chuyển đèn LED trước khi bộ hẹn giờ bắt đầu ghi nội dung nào đó vào các chân. Để chống lại điều này, bạn sẽ phải đảo ngược logic (0 = độ sáng tối đa, 255 = không có gì sáng) và tránh các giá trị chu kỳ nhiệm vụ cực cao. Những thứ kia. đảm bảo rằng sau UEV, đèn LED sẽ tắt hoàn toàn trong một chu kỳ xung điện.

Thay đổi cực:

//set polarity 
    TIM2_CCER1 |= (CC1P | CC2P);
    TIM2_CCER2 |= CC3P;

Tránh đặt r, g và b thành 255 và nhớ đảo ngược chúng khi sử dụng.

ngắt

Bản chất của ngắt là trong một số trường hợp nhất định, chip sẽ ngừng thực thi chương trình chính và gọi một số chức năng bên ngoài. Sự gián đoạn xảy ra do tác động bên ngoài hoặc bên trong, bao gồm cả bộ đếm thời gian.

Khi chúng tôi lần đầu tạo một dự án trong ST Visual Development, ngoài main.c chúng tôi nhận được một cửa sổ có tập tin bí ẩn stm8_interrupt_vector.c, tự động được đưa vào dự án. Trong tệp này, một chức năng được gán cho mỗi ngắt NonHandledInterrupt. Chúng ta cần liên kết chức năng của mình với ngắt mong muốn.

Bảng dữ liệu có một bảng các vectơ ngắt, nơi chúng tôi tìm thấy những vectơ chúng tôi cần:

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8
13 Cập nhật/tràn TIM2
14 Chụp/so sánh TIM2

Chúng ta cần thay đổi đèn LED ở UEV, vì vậy chúng ta cần ngắt số 13.

Theo đó, trước hết, trong tập tin stm8_interrupt_vector.c thay đổi tên mặc định của hàm chịu trách nhiệm về ngắt số 13 (IRQ13) thành tên của riêng bạn:

{0x82, TIM2_Overflow}, /* irq13 */

Thứ hai, chúng ta sẽ phải tạo một tập tin main.h với nội dung sau:

#ifndef __MAIN_H
#define __MAIN_H

@far @interrupt void TIM2_Overflow (void);
#endif

Và cuối cùng, hãy viết hàm này vào main.c:

@far @interrupt void TIM2_Overflow (void)
{
    PD_ODR &= ~(1<<5); // вырубаем демультиплексор
    PC_ODR = (cnt<<3); // записываем в демультиплексор новое значение
    PD_ODR |= (1<<5); // включаем демультиплексор

    TIM2_SR1 = 0; // сбрасываем флаг Update Interrupt Pending

    cnt++; 
    cnt &= 7; // двигаем счетчик LED

    TIM2_CCR1L = ~colors[cnt][0]; // передаем в буфер инвертированные значения
    TIM2_CCR2L = ~colors[cnt][1]; // для следующего цикла ШИМ
    TIM2_CCR3L = ~colors[cnt][2]; // 

    return;
}

Tất cả những gì còn lại là kích hoạt ngắt. Việc này được thực hiện bằng lệnh trình biên dịch mã rim - bạn sẽ phải tìm nó trong Hướng dẫn lập trình:

//enable interrupts
_asm("rim");

Một lệnh lắp ráp khác là sim – tắt các ngắt. Chúng phải được tắt trong khi các giá trị mới đang được ghi vào “bộ nhớ video”, để việc ngắt gây ra không đúng lúc không làm hỏng mảng.

Tất cả mã - trên GitHub.

Đọc datasheet 2: SPI trên STM32; PWM, bộ hẹn giờ và ngắt trên STM8

Nếu ít nhất ai đó thấy bài viết này hữu ích thì tôi đã không viết nó một cách vô ích. Tôi sẽ rất vui khi nhận được những bình luận và nhận xét, tôi sẽ cố gắng trả lời mọi thứ.

Nguồn: www.habr.com

Thêm một lời nhận xét