Tài nguyên về cách viết mã C hiệu quả cho bộ điều khiển vi mô? [đóng cửa]


15

Giúp đỡ nghiêm túc cần thiết ở đây. Tôi thích lập trình. Gần đây tôi đã đọc rất nhiều sách (như K & R) và các bài báo / diễn đàn trực tuyến về ngôn ngữ C. Thậm chí đã thử tìm kiếm mã Linux (mặc dù, tôi đã bị mất bắt đầu từ đâu nhưng nhìn trộm vào các thư viện nhỏ có giúp được không?).

Tôi bắt đầu là một lập trình viên Java và trong Java, nó khá hay và khô khan; Nếu các chương trình trở nên quá lớn, hãy cắt nó trong các lớp sau đó tiếp tục vào các hàm. Nguyên tắc như, giữ mã có thể đọc và thêm ý kiến. Sử dụng kỹ thuật ẩn thông tin và OOP. Một số trong đó vẫn áp dụng cho C.

Bây giờ tôi đã viết mã bằng C và cho đến nay tôi có các chương trình để hoạt động theo cách này hay cách khác. Rất nhiều người nói về hiệu suất / hiệu quả, thuật toán / thiết kế, tối ưu hóa và khả năng bảo trì. Một số người nhấn mạnh thêm một lần nữa, nhưng đối với các kỹ sư phần mềm không chuyên nghiệp, bạn thường nghe thấy một cái gì đó như: Linux kernel dev sẽ không lấy bất kỳ mã nào.

Câu hỏi của tôi là: Tôi dự định viết mã cho vi điều khiển 8 bit mà không lãng phí bất kỳ tài nguyên nào . Biết rằng tôi đến từ nền java nên mọi thứ không còn như trước nữa ... tài nguyên / sách / liên kết / mẹo sẽ được đánh giá cao. Hiệu suất và kích thước bây giờ có vấn đề. Tài nguyên / Thủ thuật về hiệu quả (trong thực tiễn tốt nhất) Mã C cho bộ điều khiển vi mô 8 bit?

Ngoài ra, inline assemblyđóng một vai trò quan trọng cũng như bám sát với tiêu chuẩn vi điều khiển. Nhưng có quy tắc chung cho hiệu quả áp dụng cho tất cả?

Ví dụ: register unsigned int variable_name;được ưa thích hơn charbất cứ lúc nào. Hoặc sử dụng uint8_t nếu bạn không cần số lớn.

EDIT: Cảm ơn bạn rất nhiều cho tất cả các câu trả lời và đề xuất. Tôi đánh giá cao nỗ lực của mọi người để chia sẻ kiến ​​thức.


2
Điều này thường không xảy ra trên bộ xử lý x86, nhưng trên vi điều khiển, nếu bạn muốn đảm bảo rằng bạn nhận được mỗi lần giảm hiệu suất cuối cùng, có lẽ bạn sẽ muốn sử dụng lắp ráp thay thế.
Rei Miyasaka

3
@Rei: Hội thủ công hiếm khi, nếu có, sử dụng ít bộ nhớ hơn và nhanh hơn trình biên dịch C hiện đại. Thật lãng phí thời gian để mã hóa khi lắp ráp khi có thể (như vậy) nên được thực hiện trong C.
mattnz

1
@mattnz Bởi hiện đại, bạn mới nói về điều gì? Thành thật mà nói, tôi đã không viết mã cho một vi điều khiển trong gần một thập kỷ.
Rei Miyasaka

2
Một mẹo đơn giản: trong microncontrolles, nói chung vẫn đúng là "nếu đơn giản hơn, nó sẽ nhanh hơn". Trên các chip phức tạp (ARM trở lên), phần cứng thực hiện rất nhiều tối ưu hóa mà bạn không bao giờ biết cho đến khi thử nghiệm.
Javier

1
@ReiMiyasaka với chi phí bảo trì tăng lên rất nhiều. Một trình biên dịch C tốt có thể tạo ra mã gần giống như một lập trình viên có kinh nghiệm cao.

Câu trả lời:


33

Tôi có hơn 20 năm hệ thống nhúng, chủ yếu là 8 và 16 micros. Câu trả lời ngắn cho câu hỏi của bạn cũng giống như bất kỳ sự phát triển phần mềm nào khác - không tối ưu hóa cho đến khi bạn biết bạn cần, và sau đó không tối ưu hóa cho đến khi bạn biết những gì bạn cần tối ưu hóa. Viết mã cho bạn để nó đáng tin cậy, dễ đọc và duy trì trước tiên. Tối ưu hóa sớm là nhiều, nếu không phải là nhiều hơn, một vấn đề trong các hệ thống nhúng

Khi bạn lập trình "mà không lãng phí bất kỳ tài nguyên nào.", Bạn có coi thời gian của mình là tài nguyên không? Nếu không, ai trả tiền cho bạn cho thời gian của bạn, và nếu không có ai, bạn có điều gì tốt hơn để làm với nó không. Một khi lựa chọn, bất kỳ nhà thiết kế hệ thống nhúng nào cũng phải thực hiện là chi phí phần cứng so với chi phí thời gian kỹ thuật. Nếu bạn sẽ vận chuyển 100 đơn vị, sử dụng vi mô lớn hơn, ở mức 100.000 đơn vị, tiết kiệm 1 đô la cho mỗi đơn vị tương đương với 1 năm phát triển phần mềm (bỏ qua thời gian để tiếp thị, chi phí cơ hội, v.v.), ở mức 1 triệu đơn vị, bạn bắt đầu nhận ROI vì bị ám ảnh về việc sử dụng tài nguyên, nhưng hãy cẩn thận vì nhiều dự án nhúng không bao giờ đạt được 1 triệu vì họ đã thiết kế để bán 1 triệu (Đầu tư ban đầu cao với chi phí sản xuất thấp) và đã phá sản trước khi họ đến đó.

Điều đó nói rằng, những điều bạn cần xem xét và nhận thức được với các hệ thống nhúng (nhỏ), bởi vì những điều này sẽ ngăn nó hoạt động, theo những cách không ngờ tới, không chỉ làm cho nó đi chậm.

a) Ngăn xếp - bạn thường chỉ có kích thước ngăn xếp nhỏ và kích thước khung ngăn xếp thường bị giới hạn. Bạn phải nhận thức được việc sử dụng ngăn xếp của bạn mọi lúc. Được cảnh báo, vấn đề ngăn xếp gây ra một số khiếm khuyết ngấm ngầm nhất.

b) Heap - một lần nữa, kích thước heap nhỏ vì vậy hãy cẩn thận về việc cấp phát bộ nhớ không chính đáng. Sự phân mảnh trở thành một vấn đề. Với hai điều này, bạn cần biết những gì bạn làm khi bạn hết tiền - điều đó không xảy ra trên một hệ thống lớn do hệ điều hành cung cấp phân trang. tức là khi malloc trả về NULL, bạn có kiểm tra nó không và bạn sẽ làm gì. Mỗi mallow cần kiểm tra và xử lý, mã phình?. Như một hướng dẫn - không sử dụng nó nếu có một sự thay thế. Hầu hết các hệ thống nhỏ không sử dụng bộ nhớ dynmaic vì những lý do này.

c) Ngắt phần cứng - Bạn cần biết cách xử lý chúng một cách an toàn và kịp thời. Bạn cũng cần biết cách tạo mã đăng ký lại an toàn. Ví dụ, libs tiêu chuẩn C thường không được đăng ký lại, do đó không nên được sử dụng bên trong các trình xử lý ngắt.

d) Hội - hầu như luôn luôn tối ưu hóa sớm. Nhiều nhất là một lượng nhỏ (nội tuyến) là cần thiết để đạt được điều gì đó mà C không thể làm được. Như một bài tập, viết một phương pháp nhỏ trong lắp ráp thủ công (từ đầu). Làm tương tự trong C. Đo hiệu suất. Tôi cá là C sẽ nhanh hơn, tôi biết nó sẽ dễ đọc hơn, có thể duy trì và có thể mở rộng. Bây giờ cho phần 2 của bài tập - viết một chương trình hữu ích trong lắp ráp và C.
Như một bài tập khác, hãy xem phần mềm của nhân Linux là trình biên dịch, nthen đọc, đoạn dưới đây về kernel linux.

Thật đáng để biết cách làm điều đó, nó thậm chí có thể đáng để thành thạo các ngôn ngữ cho một hoặc hai micros.

e) "đăng ký không dấu int biến_name", "đăng ký", và luôn luôn là một gợi ý cho trình biên dịch, không phải là một hướng dẫn, trở lại vào đầu những năm 70 (40 năm trước), điều đó có ý nghĩa. Vào năm 2012, thật lãng phí cho các tổ hợp phím vì trình biên dịch rất thông minh và các hướng dẫn vi mô rất phức tạp.

Quay lại nhận xét linux của bạn - vấn đề bạn gặp phải ở đây là chúng tôi không nói chuyện chỉ 1 triệu đơn vị, chúng tôi đang nói chuyện 100 triệu, với thời gian tồn tại mãi mãi. Thời gian và chi phí kỹ thuật để có được nó tối ưu nhất có thể là con người có giá trị trong khi. Mặc dù là một ví dụ điển hình về thực hành kỹ thuật tốt nhất, nó sẽ là tự sát thương mại đối với hầu hết các nhà phát triển hệ thống nhúng có tính mô phạm như kernal linux yêu cầu.


4
mattnz: đây là một trong những câu trả lời đáng yêu nhất trên các trang web .stackexchange.
Ahmed Masud

1
Tôi không thể cải thiện câu trả lời này. Tôi chỉ có thể thêm rằng việc chèn mã lắp ráp hiếm khi có ý nghĩa đối với hiệu năng, nhưng nó có thể có ý nghĩa đối với những thứ như chọc chip I / O hoặc các thủ thuật phần cứng khác có thể không dễ thực hiện trong C.
Mike Dunlavey

@mattnz Cảm ơn câu trả lời cũng được. +1
AceofSpades

1
Trình biên dịch @MikeDunlavey đôi khi cần thiết cho thời gian chính xác. Vừa hoàn thành lớp phủ video sử dụng bit banging để tạo video NTSC trên chân I / O, thời gian là ở mức - điện áp cao cho chu kỳ 3 giờ, sau đó thấp trong 6 rồi ....
Martin Beckett

@Martin: Điều đó có ý nghĩa hoàn hảo. Đó là một thời gian dài kể từ khi tôi được mã hóa ở cấp độ đó.
Mike Dunlavey

3

Câu hỏi của bạn ("không lãng phí tài nguyên") quá chung chung, vì vậy thật khó để đưa ra nhiều lời khuyên. Theo nghĩa đen, nếu bạn không muốn lãng phí tài nguyên, có lẽ bạn nên lùi lại một bước và đánh giá xem bạn có cần làm gì không, tức là bạn có thể giải quyết vấn đề theo những cách khác hay không.

Ngoài ra, lời khuyên hữu ích phụ thuộc rất nhiều vào các ràng buộc của bạn - bạn đang xây dựng loại hệ thống nào và loại CPU nào bạn đang sử dụng? Nó có phải là một hệ thống thời gian thực cứng? Bạn có bao nhiêu bộ nhớ cho mã và dữ liệu? Nó có hỗ trợ tất cả các hoạt động C (đáng chú ý nhất là nhân và chia) và cho loại nào không? Tổng quát hơn: đọc toàn bộ bảng dữ liệu và hiểu nó.

Lời khuyên quan trọng nhất: giữ cho nó đơn giản.

Ví dụ: quên các cấu trúc dữ liệu phức tạp (băm, cây, thậm chí có thể liệt kê các danh sách được liên kết) và sử dụng các mảng có kích thước cố định. Việc sử dụng các cấu trúc dữ liệu phức tạp hơn chỉ được bảo hành sau khi bạn đã chứng minh bằng phép đo rằng các mảng quá chậm.

Ngoài ra, đừng quá mức (một số nhà phát triển Java / C # có xu hướng phải làm): viết mã thủ tục đơn giản, không có quá nhiều lớp. Trừu tượng có một chi phí!

Thoải mái với ý tưởng sử dụng các biến toàn cục và goto [rất hữu ích cho việc dọn dẹp trong trường hợp không có ngoại lệ];)

Nếu bạn phải đối phó với các ngắt, hãy đọc về reentrancy. Viết mã reentrant là rất không tầm thường.


3

Tôi đồng ý với câu trả lời của mattnz - phần lớn. Tôi đã bắt đầu lập trình trên 8085 hơn 30 năm trước, sau đó là Z80, sau đó nhanh chóng chuyển sang 8031. Sau đó, tôi đã đi đến các bộ vi điều khiển 68300 series, sau đó là 80x86, XScale, MXL và (muộn nhất) PICS 8 bit, mà tôi đoán có nghĩa là tôi đã đến vòng tròn đầy đủ. Đối với hồ sơ, tôi có thể nói rằng FAE tại một số nhà sản xuất bộ vi xử lý lớn vẫn sử dụng trình biên dịch, mặc dù theo kiểu hướng đối tượng để tái sử dụng mã có mục đích.

Những gì tôi không thấy trong câu trả lời được phê duyệt là một cuộc thảo luận về loại bộ xử lý đích và / hoặc kiến ​​trúc được đề xuất. Có phải là 0,5 đô la đắng với bộ nhớ hạn chế? Đây có phải là lõi ARM9 với pipelining và 8Meg flash không? Quản lý bộ nhớ đồng xử lý? Nó sẽ có một hệ điều hành? Một vòng lặp while (1)? Một thiết bị tiêu dùng với số lượng sản xuất 100000 chiếc? Một công ty khởi nghiệp với những ý tưởng và ước mơ lớn?

Mặc dù tôi đồng ý rằng các trình biên dịch hiện đại làm rất tốt việc tối ưu hóa, tôi đã không bao giờ làm việc với một dự án trong 30 năm mà tôi đã không dừng trình gỡ lỗi và xem mã lắp ráp được tạo để xem những gì đang diễn ra dưới mui xe ( thừa nhận là một cơn ác mộng khi đường ống và tối ưu hóa đi vào hoạt động), vì vậy kiến ​​thức về lắp ráp là rất quan trọng.

Và tôi chưa bao giờ có CEO, VP of Engineering, khách hàng đã không thúc ép tôi thử và nhét một gallon vào thùng chứa quart, hoặc tiết kiệm 0,05 đô la bằng cách sử dụng giải pháp phần mềm để khắc phục sự cố phần cứng (hey chỉ là phần mềm đúng không? Có gì khó không?). Tối ưu hóa bộ nhớ (mã hoặc dữ liệu) sẽ luôn được tính.

Quan điểm của tôi là nếu bạn xem dự án từ quan điểm lập trình thuần túy, bạn sẽ có được giải pháp phạm vi hẹp hơn. Mattnz đã đúng - làm cho nó hoạt động, sau đó làm cho nó hoạt động nhanh hơn, nhỏ hơn, tốt hơn, nhưng bạn vẫn cần dành RẤT NHIỀU thời gian cho các yêu cầu và khả năng cung cấp trước khi bạn nghĩ về tiền mã hóa.


Xin chào Gio, vui lòng tránh HTML không cần thiết trong bài đăng của bạn và sử dụng cú pháp Markdown thay thế. Đối với <br />bạn chỉ có thể nhấn enter và đối với các đoạn chỉ để lại một dòng trống giữa chúng. Ngoài ra khi bạn tham khảo một câu trả lời khác, xin vui lòng thêm một liên kết đến nó. Tại thời điểm này, có thể chỉ có một vài câu trả lời nhưng có thể có nhiều hơn, được phân phối trên nhiều trang và sẽ không rõ câu trả lời của bạn là gì. Kiểm tra lịch sử sửa đổi để xem các chỉnh sửa của tôi.
yannis

@Gio Cảm ơn bạn đã đề cập đến các yếu tố quan trọng khác. +1 :)
AceofSpades

+1 - Mở rộng tốt đẹp câu trả lời của tôi.
mattnz

1

Câu trả lời của Manttz cho thấy rất rõ những điểm chính nhất về cách thực hiện lập trình "gần với phần cứng". Đây là sau tất cả những gì C có nghĩa là.

Tuy nhiên, tôi muốn nói thêm rằng trong khi từ khóa nghiêm ngặt của "Class" không tồn tại trong C - thì khá đơn giản để suy nghĩ về mặt lập trình hướng đối tượng trong C ngay cả khi bạn ở gần phần cứng.

Bạn có thể xem xét câu trả lời này: OO thực tiễn tốt nhất cho các chương trình C giải thích điểm này.

Dưới đây là một số tài nguyên sẽ giúp bạn viết mã hướng đối tượng tốt trong C.

a. Lập trình hướng đối tượng trong C
b. đây là một nơi tốt để mọi người trao đổi ý tưởng
c. và đây là cuốn sách đầy đủ

Một tài nguyên tốt khác tôi muốn đề xuất với bạn là:

Sê-ri Viết mã tuyệt vời . Đây là hai quyển sách. Cuốn sách đầu tiên bao gồm các khía cạnh rất cần thiết của máy móc ở các công trình cấp thấp hơn. Cuốn sách thứ hai chạm đến là "Suy nghĩ cấp độ thấp - viết cấp độ cao"


1

Bạn có một vài vấn đề. đầu tiên bạn có muốn dự án / mã này là di động không? Tính di động chi phí cho hiệu suất và kích thước của bạn, nền tảng của sự lựa chọn và nhiệm vụ bạn đang thực hiện có thể chịu được kích thước vượt quá và hiệu suất thấp hơn không?

Có, hoàn toàn trên một máy 8 bit trả về ký tự không dấu thay vì ints hoặc quần short không dấu, là một cách để cải thiện hiệu suất và kích thước. Tương tự như vậy trên máy 16 bit, sử dụng quần short không dấu và máy không dấu 32 bit. Mặc dù vậy, bạn có thể dễ dàng nhận thấy rằng nếu bạn chỉ sử dụng số nguyên không dấu ở khắp mọi nơi để có thể di chuyển trên toàn hệ thống (ví dụ ARM đẩy nó xuống thị trường thiết bị nhỏ nhất, nhỏ nhất) thì mã đó là một bản rom khổng lồ. một vi 8 bit. Tất nhiên bạn có thể chỉ sử dụng unsign mà không có int hoặc short hoặc char và để trình biên dịch chọn kích thước tối ưu.

Không chỉ lắp ráp nội tuyến, mà ngôn ngữ lắp ráp nói chung. Lắp ráp nội tuyến rất không dễ mang theo và khó viết hơn là chỉ gọi một hàm asm. có, bạn ghi lại thiết lập cuộc gọi và trả lại nhưng với chi phí phát triển dễ dàng hơn, bảo trì và kiểm soát tốt hơn. Quy tắc vẫn được áp dụng, chỉ viết nó bằng asm nếu bạn thực sự cần, bạn đã hoàn thành công việc để kết luận rằng đầu ra của trình biên dịch là vấn đề của bạn trong lĩnh vực này và mức tăng hiệu suất bạn có thể nhận được bằng cách làm bằng tay. Sau đó quay lại tính di động và bảo trì, ngay khi bạn bắt đầu trộn C và asm, và mỗi khi bạn trộn C và asm, bạn có thể làm hại tính di động của mình và có thể làm cho dự án ít bảo trì hơn tùy thuộc vào người khác đang làm việc với nó hoặc nếu điều này là một sản phẩm bạn đang phát triển và người khác phải bảo trì. Một khi bạn đã thực hiện phân tích đó, bạn sẽ tự động biết liệu bạn có phải đi nội tuyến hay đi với lắp ráp thẳng. Tôi có hơn 25 năm trong lĩnh vực này, viết hỗn hợp C và asm mỗi ngày, sống trên / ở lớp phần cứng / phần mềm và, tốt, không bao giờ sử dụng asm nội tuyến. Nó hiếm khi đáng giá cho nỗ lực, quá cụ thể trình biên dịch, tôi viết mã trình biên dịch không cụ thể bất cứ nơi nào có thể (hầu như ở mọi nơi).

Chìa khóa cho toàn bộ câu hỏi của bạn là tháo rời mã C của bạn. Tìm hiểu trình biên dịch làm gì với mã của bạn và theo thời gian, nếu bạn mong muốn, bạn có thể học cách thao tác trình biên dịch để tạo mã bạn muốn mà không cần phải dùng đến asm nhiều. Với nhiều thời gian hơn, bạn có thể học cách thao tác trình biên dịch để tạo mã hiệu quả trên nhiều mục tiêu làm cho mã dễ di chuyển hơn mà không cần phải dùng đến asm. Bạn sẽ không gặp vấn đề gì khi thấy char không dấu hoạt động tốt hơn khi trả về trạng thái từ hàm so với unsign trong micro 8 bit, tương tự, char không dấu sẽ tốn kém hơn trên các hệ thống 16 và 32 bit (một số kiến ​​trúc giúp bạn ra, một số không).

Một số bộ vi điều khiển 8 bit, tất cả?, Rất không thân thiện với trình biên dịch và không có trình biên dịch nào tạo ra mã tốt. Không có đủ nhu cầu để tạo ra một thị trường trình biên dịch cho các thiết bị đó để tạo ra một trình biên dịch tuyệt vời cho các mục tiêu đó, vì vậy các trình biên dịch có ở đó để thu hút nhiều doanh nghiệp, các lập trình viên không phải là asm, và không phải vì trình biên dịch tốt hơn so với viết tay . cánh tay và mips bước vào thế giới này đang thay đổi mô hình đó khi bạn có các mục tiêu có trình biên dịch đã thực hiện rất nhiều công việc trên chúng, trình biên dịch tạo mã khá tốt, v.v ... Đối với micros có bộ xử lý như bạn tất nhiên vẫn có trường hợp bạn phải thả xuống asm, nhưng nó không thường xuyên, sẽ dễ dàng hơn rất nhiều khi chỉ nói cho trình biên dịch những gì bạn muốn nó làm hơn là không sử dụng nó. Lưu ý rằng thao tác với trình biên dịch không phải là một mã xấu, không thể đọc được, trong thực tế, nó là ngược lại, sạch đẹp, mã đơn giản, có lẽ sắp xếp lại một vài mục. Kiểm soát kích thước của các chức năng và số lượng tham số của bạn, loại điều đó tạo ra sự khác biệt rất lớn trong đầu ra của trình biên dịch. Tránh các tính năng ghee-whiz của trình biên dịch hoặc ngôn ngữ, KISS, giữ cho nó đơn giản ngu ngốc, thường tạo ra mã tốt hơn và nhanh hơn nhiều.


Bạn không nói rõ loại sản phẩm bạn sản xuất, tôi sẽ cho rằng đó là khối lượng rất cao, tỷ suất lợi nhuận thấp hoặc thị trường ngách cụ thể với tỷ suất lợi nhuận khủng. Điều này rất quan trọng để quyết định ở cấp độ kinh doanh nếu bạn nên sử dụng một trình biên dịch thủ công nhỏ và vi thủ công 8 bit, hoặc một vi mô lớn hơn và C. Để phản hồi lại nhận xét (đã xóa?) Của bạn - tuy nhiên chúng tôi làm việc với micros 8 bit bắt đầu với một vi mô lớn hơn mức cần thiết và chỉ sửa đổi lại khi và nếu chi phí BOM trở thành vấn đề) Thời gian đưa ra thị trường, chi phí hỗ trợ và chi phí phát triển khấu hao cho phép chúng tôi bỏ thêm 10 hoặc 20 xu vào BOM.
mattnz
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.