Tại sao không luôn luôn sử dụng DMA để ủng hộ các ngắt với UART trên STM32? [đóng cửa]


9

Tháng trước tôi đã dành rất nhiều thời gian để UART (cho MIDI) hoạt động với STM (STM32F103C8T6) bằng cách sử dụng các ngắt, nhưng không thành công lắm.

Tuy nhiên, tối nay sử dụng DMA nó hoạt động khá nhanh.

Vì theo như tôi đọc DMA thì nhanh hơn và làm giảm CPU, tại sao không luôn luôn sử dụng DMA để thay thế cho các ngắt? Đặc biệt là trên STM32 dường như có khá nhiều vấn đề.

Tôi đang sử dụng STM32CubeMx / HAL.


2
Tại sao không? Đó là một câu hỏi về ý kiến, một người tìm kiếm một dự đoán về lý do kỹ thuật có thể, hoặc theo cùng một cách quá rộng, và do đó không phải là một câu hỏi thuộc về đây. Để đặt tên cho một ví dụ ngẫu nhiên, DMA sẽ có nghĩa là có độ trễ cao hơn trong việc yêu cầu dữ liệu, đặc biệt là khi bạn không nhận được bất kỳ lợi ích thực sự nào trừ khi bạn cho phép nó thu thập nhiều ký tự. Thường thì điều đó có thể tốt, đôi khi có thể không.
Chris Stratton

6
Nếu việc gián đoạn làm việc mất nhiều tuần, đó là do bạn đã tiếp cận nhiệm vụ sai cách; làm cho DMA hoạt động có thể mất nhiều thời gian hơn - nó thực sự là một nhiệm vụ phức tạp hơn, do đó, sự dễ dàng rõ ràng của nhiệm vụ phức tạp hơn so với đơn giản hơn có lẽ là do các tài nguyên bạn sử dụng để hướng dẫn cho từng người, chứ không phải chính cơ chế.
Chris Stratton

5
Đừng bao giờ cho rằng dma giải phóng cpu, đôi khi có, cpu vẫn tiếp tục, đôi khi không có bộ xử lý nào bị đóng băng để giữ bus cho động cơ dma. Không quan trọng để thực hiện điều này với việc thực hiện cánh tay, vì vậy không thể chỉ nói rằng tất cả các cánh tay đều theo cách này và tất cả các x86 đều theo cách đó hoặc bất cứ điều gì, nó không đơn giản, bạn phải luôn kiểm tra thiết kế hệ thống và có thể thực hiện một chút hack. Con chip bạn có rất có thể giải phóng lõi cánh tay, đây chỉ là một nhận xét về dma. Theo như câu hỏi của bạn, không có nghĩa là bạn không thể theo kịp và dma + int có thể là giải pháp đầy đủ nếu bạn không thể thăm dò ý kiến.
old_timer

5
Ngắt là khá nhỏ trên cổng nối tiếp STM32F. Tại sao bạn không đăng câu hỏi với mã của mình để một số người trong chúng tôi có thể cố gắng phát hiện ra bạn đang sai ở đâu? Không bao giờ nên hack mã cho đến khi nó hoạt động mà không hiểu vấn đề tiềm ẩn là gì.
Jon

7
Theo ý kiến ​​khiêm tốn của tôi (không phải vậy), đây là một trong những mặt trái của việc sử dụng Cube khổng lồ, khủng khiếp. Viết phần mềm từ đầu, bạn sẽ tìm hiểu chính xác cách UART hoạt động (vì bạn phải làm), bạn sẽ hiểu thiết bị ngoại vi tốt hơn nhiều và về lâu dài nó sẽ giúp bạn tiết kiệm rất nhiều thời gian.
DiBosco

Câu trả lời:


24

Mặc dù DMA làm giảm CPU và do đó có thể giảm độ trễ của các ứng dụng điều khiển ngắt khác chạy trên cùng lõi, có các chi phí liên quan đến nó:

  • Chỉ có một số lượng hạn chế các kênh DMA và có những hạn chế về cách các kênh đó có thể tương tác với các thiết bị ngoại vi khác nhau. Một thiết bị ngoại vi khác trên cùng một kênh có thể phù hợp hơn cho việc sử dụng DMA.

    Ví dụ: nếu bạn có chuyển I2C số lượng lớn cứ sau 5ms, thì đây có vẻ là ứng cử viên tốt hơn cho DMA so với lệnh gỡ lỗi không thường xuyên đến trên UART2.

  • Thiết lập và duy trì DMA là một chi phí của chính nó. . dù sao, xem bên dưới.)

  • DMA có thể sử dụng năng lượng bổ sung , vì nó là một miền khác của lõi cần được đặt xung nhịp. Mặt khác, bạn có thể tạm dừng CPU trong khi quá trình chuyển DMA đang diễn ra, nếu lõi hỗ trợ điều đó.

  • DMA yêu cầu bộ đệm hoạt động với (trừ khi bạn đang thực hiện DMA ngoại vi đến ngoại vi), do đó, có một số chi phí bộ nhớ liên quan đến nó.

    (Chi phí bộ nhớ có thể cũng có mặt ở đó khi sử dụng ngắt mỗi nhân vật, nhưng nó cũng có thể tôi nhỏ hơn nhiều hoặc biến mất ở tất cả nếu các thông điệp được giải thích ngay lập tức bên trong ngắt.)

  • DMA tạo độ trễ vì CPU chỉ được thông báo khi quá trình truyền hoàn thành / hoàn thành một nửa (xem các câu trả lời khác).

  • Ngoại trừ khi truyền dữ liệu vào / từ bộ đệm vòng, bạn cần biết trước số lượng dữ liệu bạn sẽ nhận / gửi.

    • Điều này có thể có nghĩa là cần xử lý các ký tự đầu tiên của tin nhắn bằng cách sử dụng các ngắt cho mỗi ký tự: ví dụ: khi giao tiếp với XBee, trước tiên bạn sẽ đọc loại và kích thước gói và sau đó kích hoạt chuyển DMA vào bộ đệm được phân bổ.

    • Đối với các giao thức khác, điều này có thể hoàn toàn không thể, nếu chúng chỉ sử dụng các dấu phân cách cuối tin nhắn: ví dụ: các giao thức dựa trên văn bản sử dụng '\n'làm dấu phân cách. (Trừ khi thiết bị ngoại vi DMA hỗ trợ khớp trên một ký tự.)

Như bạn có thể thấy, có rất nhiều sự đánh đổi để xem xét ở đây. Một số có liên quan đến các giới hạn phần cứng (số lượng kênh, xung đột với các thiết bị ngoại vi khác, khớp trên các ký tự), một số dựa trên giao thức được sử dụng (dấu phân cách, độ dài đã biết, bộ đệm).

Để thêm một số bằng chứng giai thoại, tôi đã phải đối mặt với tất cả những sự đánh đổi này trong một dự án sở thích sử dụng nhiều thiết bị ngoại vi khác nhau với các giao thức rất khác nhau. Có một số sự đánh đổi để thực hiện, chủ yếu dựa trên câu hỏi "tôi chuyển bao nhiêu dữ liệu và tôi có thường xuyên làm điều đó không?". Điều này về cơ bản cung cấp cho bạn một ước tính sơ bộ về tác động của chuyển giao điều khiển ngắt đơn giản trên CPU. Do đó, tôi đã ưu tiên chuyển I2C đã nói ở trên cứ sau 5 giây so với chuyển UART cứ sau vài giây sử dụng cùng một kênh DMA. Một lần chuyển UART khác xảy ra thường xuyên hơn và mặt khác có nhiều dữ liệu hơn được ưu tiên hơn một lần chuyển I2C khác, điều này hiếm khi xảy ra hơn. Đó là tất cả sự đánh đổi.

Tất nhiên, sử dụng DMA cũng có những lợi thế, nhưng đó không phải là điều bạn yêu cầu.


Cảm ơn câu trả lời chi tiết của bạn. MIDI sẽ là phần quan trọng nhất vì vậy tôi đoán DMA phù hợp với nó (mặc dù tốc độ thấp: 31250 baud). Tôi có đủ các kênh DMA, sau này tôi sẽ sử dụng một STM32 khác khi sử dụng 4 USART. Tôi không cần tạm dừng CPU, vì nó sẽ có nguồn USB 5V và tôi cần xử lý giữa các tin nhắn (để xử lý các tin nhắn trong vòng lặp chính). Tôi có bộ đệm đọc 256 byte và bộ đệm truyền 256 byte. Tôi có thể tăng nó sau nếu cần. STM32f103c8t6 có RAM 20 KB, STM cuối cùng tôi sẽ sử dụng có 192 KB.
Michel Keijzers

Và bạn cho tôi một ý tưởng rất tốt làm thế nào để cải thiện. Cho đến nay tôi luôn đọc 1 byte và kiểm tra liên tục khi nhận được tin nhắn (MIDI) hoàn chỉnh. Nhưng tôi có thể đọc byte đầu tiên, và tùy thuộc vào đó phần lớn kích thước được biết và có thể yêu cầu phần còn lại. Điều này làm tôi tốn thêm một bộ đệm nhỏ nhưng không sao.
Michel Keijzers

Đọc các byte đơn với DMA rất không hiệu quả. Để có độ trễ thấp hơn và hiệu quả cao hơn, sử dụng các ngắt cho mỗi ký tự cho đến khi bạn biết kích thước và sau đó chuyển sang DMA sẽ thuận lợi.
Jonas Schäfer

Tôi đã gặp rất nhiều vấn đề khi sử dụng ngắt (không có DMA), tôi nghĩ rằng tôi sẽ sử dụng nhận DMA 1 byte và sau đó tôi biết mình sẽ mong đợi bao nhiêu byte và thực hiện yêu cầu DMA để nhận thêm.
Michel Keijzers

6
Đó có thể là một lỗi - bạn nên sửa mã ngắt đơn giản, không có DMA.
Chris Stratton

10

Sử dụng DMA thường có nghĩa là bạn không còn bị gián đoạn trên mỗi ký tự, mà chỉ sau khi nhận được "bộ đệm đầy" các ký tự (hoặc được truyền). Điều này làm tăng độ trễ xử lý các ký tự đó - ký tự đầu tiên không được xử lý cho đến khi nhận được ký tự cuối cùng trong bộ đệm.

Độ trễ này có thể là một điều tồi tệ, đặc biệt là trong một ứng dụng nhạy cảm với độ trễ như MIDI, trong đó một vài ms ở đây và có thể thêm vào các vấn đề nghiêm trọng về khả năng phát cho các buổi biểu diễn trực tiếp.


Những gì tôi làm là nhận 1 byte mỗi lần (vì vậy bộ đệm 'DMA' là 1 byte) và sau mỗi lần gọi lại DMA của một byte đó, để lưu trữ nó trong bộ đệm vòng mà tôi xử lý thủ công. Trong vòng lặp chính của tôi, tôi dự định kiểm tra các tin nhắn MIDI hoàn chỉnh và xử lý chúng.
Michel Keijzers

3
DMA thường được sử dụng để nhận nhiều byte và chỉ bị gián đoạn khi tất cả chúng đã được nhận. Việc gián đoạn chỉ sau một byte là bình thường khi không sử dụng DMA, vì vậy nó khiến tôi tự hỏi: điểm nào trong sự phức tạp thêm của việc sử dụng DMA cho điều đó?
Steve Melnikoff

5
@MichelKeijzers Sau đó, những gì bạn làm là khá chính xác giống như bạn sẽ làm trong các triển khai điều khiển gián đoạn thuần túy. Do đó, không có lợi ích gì trong việc sử dụng DMA trong trường hợp này và vấn đề ban đầu của bạn có thể không được giải quyết bằng DMA mà bằng cách viết lại mã (ISR, thiết lập) của bạn.
JimmyB

@JimmyB ... cảm ơn ... tuy nhiên do câu trả lời của Jonas bên dưới, tôi sẽ thực hiện một cải tiến để đọc nhiều byte như thông điệp dài. Tôi biết điều này sau khi nhận được byte đầu tiên (trong hầu hết các trường hợp). Hơn nó sẽ có lợi hơn khi sử dụng DMA trên các ngắt.
Michel Keijzers

8

DMA không phải là sự thay thế cho các ngắt - chúng thường được sử dụng cùng nhau! Ví dụ, nếu bạn đang sử dụng DMA để gửi dữ liệu qua UART, bạn vẫn cần một ngắt để thông báo cho bạn khi gửi xong.


Thực sự, có lẽ chỉ trên STM32, cơ chế ngắt (không thuần DMA) là một chút vụng về so với DMA trực tiếp.
Michel Keijzers

2
@duskwuff Không thực sự; bạn có thể thăm dò để xem khi nào DMA hoàn thành và bạn có thể muốn vì một trong những lý do chính để sử dụng DMA là không phải bận tâm đến cổng nối tiếp cho đến khi chương trình của bạn ở trạng thái có thể hoạt động trên nhận dữ liệu. Hoặc đối với DMA gửi đi, bạn chỉ có thể thăm dò ý kiến ​​để xem có thể thêm nhiều hơn vào bộ đệm gửi hay không.
Chris Stratton

1
@MichelKeijzers: IDK chip cụ thể, nhưng thường thì giải pháp thay thế cho DMA không bị gián đoạn theo nghĩa đen, nó được lập trình-IO (trong đó bạn sử dụng các hướng dẫn CPU để đọc / ghi dữ liệu từ / đến thanh ghi I / O). Trong một trình xử lý ngắt, bạn thường sẽ đọc một lần, và sau đó có thể là một trường hợp khác trong trường hợp một nhân vật bước vào trong khi bạn đang đọc đầu tiên, đặc biệt là nếu điều đó sẽ không gây ra gián đoạn khác. Hoặc đọc cho đến khi một bộ đệm nội bộ trống nếu có một bộ đệm như vậy. Rõ ràng bạn cần nhiều ngắt hơn cho PIO và thiết lập chúng khác nhau.
Peter Cordes

@ChrisStratton Điểm tốt ... cho đến nay tôi vẫn chưa kiểm tra xem có thể truyền được không, tôi chỉ truyền tải một cái gì đó, không kiểm tra xem nó có ổn không. Có lẽ nếu không, tôi sẽ thử lại sau.
Michel Keijzers

@PeterCordes Có vẻ như STM32 có đủ các ngắt cho DMA và tôi đọc mỗi lần chỉ 1 byte. Ngay cả STM32 đơn giản nhất (F103c8t6) cũng có đủ các cổng / ngắt DMA có sẵn.
Michel Keijzers

5

Sử dụng DMA giới thiệu một số câu hỏi và thách thức thú vị ngoài tất cả các cân nhắc khác về sử dụng ngoại vi UART. Tôi sẽ cung cấp cho bạn một vài ví dụ: Giả sử rằng uC của bạn đang ngồi trên xe buýt RS485 (hoặc bất cứ thứ gì) với các thiết bị khác. Có rất nhiều tin nhắn trên xe buýt, một số được dành cho uC của bạn, một số thì không. Ngoài ra, giả sử rằng tất cả các hàng xóm xe buýt nói một giao thức dữ liệu khác nhau, ngụ ý độ dài tin nhắn là khác nhau.

Một số câu hỏi chỉ xuất hiện khi sử dụng DMA là:

  • Khi nào thì tôi ngắt lời?
    • Các DMA chỉ thực sự muốn làm gián đoạn khi họ đã chuyển một lượng dữ liệu định sẵn.
    • Bạn sẽ làm gì nếu bạn không bao giờ nhận đủ dữ liệu để kích hoạt ngắt DMA?
  • Nếu bạn chỉ nhận được một phần tin nhắn khi DMA bị gián đoạn thì sao?
  • Bộ đệm RX của bạn trông như thế nào? Chúng là tuyến tính hay tròn?
    • DMA có thể là một người tham gia bộ đệm tròn bất thường theo nghĩa là nó chỉ tuân theo ranh giới địa chỉ, nhưng không có vấn đề gì thổi qua các con trỏ khác trong hệ thống đệm tròn.

Dù sao, chỉ là thức ăn cho suy nghĩ.


Cảm ơn những cân nhắc đó. Hiện tại tôi luôn nhận được 1 byte và lưu trữ nó trong bộ đệm vòng, vì thực sự các tin nhắn của tôi (MIDI) có thể có độ dài khác nhau và tôi không biết mình sẽ nhận được gì tiếp theo. Trong vòng lặp chính của tôi, tôi kiểm tra các thông báo hoàn chỉnh để xử lý chúng (và nếu hoàn thành, tôi xóa chúng khỏi bộ đệm vòng). Vì vậy, tôi sẽ luôn nhận đủ dữ liệu (trừ khi tôi bỏ lỡ byte, tôi phải kiểm tra điều đó). Bộ đệm RX của tôi chỉ có 1 byte, nhưng tôi sao chép nó vào bộ đệm vòng / tròn. Tôi đã không kiểm tra nếu nó đầy (cần thêm nó).
Michel Keijzers

Này, đừng lo lắng. Tôi chắc chắn rằng ứng dụng của bạn sẽ được lập trình tốt. Như những người khác đã đề cập, DMA là tuyệt vời, nhưng không phải là miễn phí. Nó giới thiệu thêm các cân nhắc trong hệ thống không tồn tại nếu bạn có thể thoát khỏi mà không cần sử dụng nó.
pgvoorhees

Tôi hy vọng, tôi vẫn là người mới bắt đầu.
Michel Keijzers

3

Về phía bên nhận (như tôi nhớ lại) DMA chấm dứt hoặc trên một ký tự khớp hoặc tại số đếm cuối. Một số giao thức và nhiều ứng dụng tương tác không dễ dàng phù hợp với mô hình này và bạn thực sự cần xử lý mọi thứ theo từng ký tự. Các kỹ thuật DMA cũng có thể dễ vỡ nếu liên kết truyền thông không đáng tin cậy, mất một ký tự trong luồng có thể dễ dàng gây rối cho máy trạng thái DMA của bạn.


Tôi thực sự nhận được byte theo byte và sao chép thủ công vào bộ đệm vòng để xử lý nó sau.
Michel Keijzers

1

Tôi đã sử dụng STM32CubeMx / HAL cho một vài dự án và nhận thấy rằng phần mềm xử lý UART mà nó tạo ra có những thiếu sót nhất định ở phía bên nhận.

Khi truyền, bạn thường muốn gửi một khối dữ liệu hoặc dòng văn bản. Trong trường hợp này, bạn biết trước thời gian truyền dữ liệu là bao lâu và vì vậy sử dụng DMA là một giải pháp rõ ràng. Bạn nhận được một ngắt sau khi quá trình truyền hoàn tất và có thể sử dụng chức năng gọi lại hoàn thành UART TX để chỉ ra mã chính của bạn rằng việc truyền hoàn tất và bạn có thể gửi một khối dữ liệu khác.

Khi nói đến nhận dữ liệu, các chức năng do ST cung cấp đều cho rằng bạn biết thiết bị gửi sẽ cung cấp cho bạn bao nhiêu ký tự trước khi bắt đầu gửi. Thông thường điều này không được biết đến. Chức năng ngắt đặt dữ liệu nhận được vào bộ đệm và chỉ cho biết rằng có sẵn dữ liệu khi số lượng ký tự được xác định trước đã được nhận. Nếu bạn cố gắng sử dụng chức năng DMA hoặc ngắt để nhận dữ liệu bằng cách thiết lập chuyển ký tự đơn liên tiếp thì thời gian thiết lập cho mỗi ký tự này sẽ có nghĩa là bạn sẽ mất các ký tự ở bất cứ thứ gì ngoài tốc độ dữ liệu chậm nhất (tốc độ baud mà bạn sẽ bắt đầu mất dữ liệu sẽ phụ thuộc vào tốc độ xung nhịp của bộ xử lý của bạn) và sẽ tải bộ xử lý quá mức, không để lại chu trình hướng dẫn cho bất kỳ quá trình xử lý nào khác

Để làm tròn điều này, tôi đã viết hàm xử lý ngắt riêng của mình lưu trữ dữ liệu trong một bộ đệm tròn cục bộ nhỏ và đặt số đếm được đọc bởi mã chính (một semaphore đếm RTOS) để chỉ ra rằng đã có dữ liệu nhận được. Mã chính sau đó có thể thu thập dữ liệu từ bộ đệm này một cách thoải mái, không có vấn đề gì nếu có một số chậm trễ trong việc thu thập dữ liệu với điều kiện bộ đệm cục bộ không bị tràn ra trước khi dữ liệu được thu thập.


Tôi làm chính xác như vậy (tôi nghĩ). Tôi đọc 1 byte mỗi lần và lưu trữ nó trong bộ đệm tuần hoàn và tôi dự định kiểm tra trong vòng lặp chính để có thông báo đầy đủ. Có thể được tăng cường một chút mặc dù.
Michel Keijzers

Bạn có nghĩ rằng tôi có thể gặp phải vấn đề là thiết lập DMA mỗi lần sẽ làm quá tải bộ xử lý / ký tự bị thiếu của tôi ở mức 31.250 baud không?
Michel Keijzers

1
Miễn là bạn thiết lập DMA để chuyển một số ký tự cùng một lúc thì điều này sẽ không thành vấn đề. Tôi có 4 UART chạy 115200 trở lên và I2C sử dụng DMA mà không gặp vấn đề gì. Các truyền UART là tất cả ~ 20 byte hoặc lâu hơn. Vấn đề là sử dụng DMA để nhận trên UART (bộ xử lý L4 ở tốc độ 80 MHz, 9600baud).
uɐɪ

Hiện tại tôi đặt nó thành 1 byte mỗi lần, nhưng tôi có thể cải thiện nó (bằng cách thực hiện byte đầu tiên, và hơn là kiểm tra xem cần bao nhiêu byte nữa).
Michel Keijzers
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.