Mã STM32 đầu tiên của tôi - vui lòng chỉ trích tôi


7

Tôi vừa viết mã đầu tiên của mình trên STM32 - đèn LED nhấp nháy. Nó kết hợp một mảnh từ các nguồn khác nhau; vui lòng chỉ trích tôi để tôi có thể học cách viết mã phù hợp và không học thói quen ngu ngốc.

#include "stm32f30x.h"

void SysTick_Handler(void);
void TimingDelay_Decrement(void);
void Delay(__IO uint32_t nTime);

static __IO uint32_t TimingDelay;

int main(void)
{
  RCC->AHBENR  |=  ((1UL << 21) );                  // Enable GPIOE clock??
  GPIOE->MODER |= (1 << 2*8);                       // PIN 9 as output

//GPIOE->BSRR = 1 << 9;                             // LED ON
//GPIOE->BSRR = 1 << 9 << 16;                       // LED OFF
//GPIOE->BRR = 1 << 9;                              // LED OFF

    if (SysTick_Config(SystemCoreClock / 1000))
    { 
    /* Capture error */ 
    while (1);
  }

    while(1)
    {
        GPIOE->BSRR = 1 << 8;   
        Delay(50);
        GPIOE->BRR = 1 << 8;
        Delay(50);
    }
}

void SysTick_Handler(void)
{
  TimingDelay_Decrement();
}

void Delay(__IO uint32_t nTime)
{
  TimingDelay = nTime;

  while(TimingDelay != 0);
}

void TimingDelay_Decrement(void)
{
  if (TimingDelay != 0x00)
  { 
    TimingDelay--;
  }

}

Tại sao tôi phải bật đồng hồ GPIOE? Và sự khác biệt giữa 1UL << 211 << 21?


1
Bài viết wiki này rất hữu ích về mặt khái quát: en.wikipedia.org/wiki/Best_coding_practices
Nick Williams

4
Có một trang web StackExchange riêng để xem xét mã , nhưng họ thường muốn có nhiều câu hỏi tập trung hơn là "phê bình mã của tôi".
Dave Tweed

2
@Dave Tôi tin rằng đây là nơi tốt hơn để xem lại mã được viết riêng cho vi điều khiển ..
m.Alin

2
"Hãy chỉ trích tôi" Việc thụt mã của bạn không nhất quán.
Cole Johnson

2
Tại sao bạn không sử dụng Thư viện ngoại vi tiêu chuẩn? Nó cải thiện đáng kể khả năng đọc và giảm nguy cơ lỗi có thể xảy ra khi truy cập đăng ký trực tiếp.
thời gian thực

Câu trả lời:


10

Trên STM32, tất cả các thiết bị ngoại vi theo mặc định bị tắt và không nhận được bất kỳ tín hiệu đồng hồ nào. Điều này giúp tiết kiệm rất nhiều năng lượng (và buộc bạn phải suy nghĩ về sự tương tác giữa các chân khi bạn viết mã). Nếu bạn muốn sử dụng GPIOE, bạn cần nói với STM32 để bật đồng hồ của nó.

1UL << 21 thay đổi một unsigned long giá trị 1 21 bit sang trái. 1 << 21thay đổi một số số có giá trị bằng 1, nhưng đó là một số loại mặc định, có thể có hoặc không unsigned long, và bạn không nhất thiết phải biết mặc định hệ thống của bạn rộng bao nhiêu. Nó sẽ thay đổi theo nền tảng và trình biên dịch. Trong thực tế, UL có thể thay đổi theo nền tảng và trình biên dịch là tốt. Tốt nhất là sử dụng typedefs của thư viện, đó là những điều chắc chắn.

(uint32_t)1 << 21(uint64_t)1 << 21không rõ ràng.


5

Nhìn chung, trông khá đẹp. Có thể sử dụng thêm một số ý kiến.

SysTick_Handler có phải là trình xử lý ngắt không? Nếu vậy, hãy nói như vậy. Tôi ngạc nhiên không có thuộc tính nào khai báo điều này (hầu hết các C được nhúng cho các bộ xử lý khác có từ bị ngắt ở đâu đó theo định nghĩa của tên trình xử lý). Tôi giả sử rằng phải được thiết lập trong #DEFINE trong tệp .h hệ thống ở đâu đó. Quá tệ, họ giấu nó đi như thế.

SysTick_Handler thường được gọi như thế nào? Tôi biết điều đó được thiết lập trong cuộc gọi SysTick_Config, nhưng bạn không nói gì về nó.

Các đơn vị cho cuộc gọi Trì hoãn là gì? Một phần nghìn giây? Nói vậy

Đèn LED nào trên bảng bạn đang thao tác với GPIOE-> BSRR = 1 << 8?

Hàm TimingDelay_Decrement có thực sự cần thiết không? Tại sao không đặt mã của nó vào trong SysTick_Handler? Người ta thường cố gắng tránh các cuộc gọi không cần thiết trong một trình xử lý ngắt. Không tạo ra sự khác biệt ở đây, nhưng đôi khi có thể xử lý quan trọng.


Tò mò: nếu tôi lo lắng về cuộc gọi đến TimingDelay_Decrement, tôi có thể biến nó thành một hàm nội tuyến không? Điều đó sẽ giúp?
Greg d'Eon

2
Nó hơi khác một chút, nhưng đó là cách mà công cụ Cortex-M được thiết lập. SysTick_Handler là trình xử lý ngắt tiêu chuẩn cho bộ xử lý Cortex-M được thiết lập trong tệp khởi động được liên kết khi được biên dịch. Có một trình xử lý mặc định được sử dụng nếu người dùng không tự viết mà ghi đè trình xử lý defaut. (Tôi hy vọng tôi đã nói đúng tất cả. Tôi chủ yếu là một kỹ sư phần cứng). Cuộc gọi: SysTick_Config (SystemCoreClock / 1000) sẽ thiết lập SysTick_Handler để ngắt mỗi mili giây. Nếu nó được viết SysTick_Config (SystemCoreClock / 100), nó sẽ đánh dấu cứ sau 10ms.
Tut

... công cụ Cortex-M tiêu chuẩn, nhưng tôi đồng ý rằng các bình luận sẽ có ích.
Tut

1
@ m.Alin Câu hỏi hay và tôi thậm chí không chắc nó có được định nghĩa trong ví dụ trên hay không. Trong các hệ thống của tôi sử dụng STM32F4, có tệp khởi tạo system_stm32f4xx.c để thiết lập đồng hồ hệ thống và xác định biến toàn cầu SystemCoreClock được đặt thành giá trị bằng tốc độ xung nhịp lõi (cho ứng dụng của tôi 168000000 tức là 168Mhz). Cuộc gọi đến SysTick_Config thiết lập bộ chia đồng hồ để xác định tốc độ đánh dấu. Nếu bạn đã gọi SysTick_Config (SystemCoreClock), nó sẽ đánh dấu mỗi giây một lần. Tôi phải nói rằng một số nội dung của Cortex-M được thiết lập theo cách khó hiểu (theo ý kiến ​​của tôi).
Tut

1
@Kynit có, làm cho TimingDelay_Decrement sẽ giống như bao gồm mã trong trình xử lý như tôi đề xuất.
tcrosley

3

Để dễ đọc và để giảm khả năng mắc lỗi, hãy sử dụng macro thay vì số ma thuật.

stm32f30x.h chứa macro cho (hy vọng) tất cả các giá trị đăng ký. Vì thế:

RCC->AHBENR  |=  ((1UL << 21) );                  // Enable GPIOE clock??
GPIOE->MODER |= (1 << 2*8);                       // PIN 9 as output

có thể được viết như:

RCC->AHBENR  |= RCC_AHBENR_GPIOEEN;    // Enable GPIOE clock.
GPIOE->MODER |= GPIO_MODER_MODER8_0;   // PIN 8 - not 9! - as output.

(Thật vậy - nhận xét ban đầu đã sai ở đây; sử dụng macro làm cho điều này rõ ràng hơn!)

Tương tự, mã chuyển đổi pin có thể được viết là:

GPIOE->BSRR = GPIO_BSRR_BS_8;   
Delay(50);
GPIOE->BRR = GPIO_BRR_BR_8;
Delay(50);

Đi xa hơn, GPIO_MODER_MODER8_0không rõ ràng lắm, vì vậy tôi có thể thêm một macro của riêng mình:

#define GPIO_MODER_MODER8_OUT   GPIO_MODER_MODER8_0

Điều đó có thể gây ra một chút mệt mỏi nếu bạn đang thiết lập nhiều chân, vì vậy bạn có thể làm:

#define GPIO_MODER_OUT(pin)     (((uint32_t) 1) << (2 * (pin)))

Ngoài ra, dòng trên giả định rằng các bit trong MODERđó đã là 0x0 hoặc 0x1. Nếu chúng ta không thể cho rằng, thì chúng ta cần xóa chúng trước:

GPIOE->MODER &= ~GPIO_MODER_MODER8;
GPIOE->MODER |= GPIO_MODER_OUT(8);

Cũng thế:

Nếu các hàm được khai báo ở đầu tệp không được sử dụng ở bất kỳ nơi nào khác, hãy khai báo chúng static. Điều này ngăn chặn chúng được sử dụng trong bất kỳ tập tin khác do nhầm lẫn.

Nếu trình biên dịch cho phép, mainnên được khai báo là void, không trả về một int, vì nó không bao giờ trả về.

Đây là một nitpick thực sự: TimingDelaychỉ là một số thông thường; nó không được sử dụng khi đại diện thập lục phân là phù hợp. Vì vậy TimingDelay_Decrement, trong đó, so với không, sử dụng 0, không 0x00. Điều này cũng làm cho nó phù hợp với so sánh trong Delay.

Nếu tôi hiểu đúng, __IOđánh dấu một biến là volatile, và, tôi nghi ngờ, được sử dụng để đánh dấu các thanh ghi có thể đọc và ghi được. TimingDelaythực sự cần phải được volatile, vì nó được cập nhật trong một ngắt - nhưng tôi có xu hướng sử dụng volatilehơn là __IO, để chỉ ra rằng đó không phải là một đăng ký.

nTimekhông cần phải volatile(hoặc __IO), vì nó chỉ là một tham số hàm bình thường, được truyền bởi giá trị, được đọc một lần.


1

Điều này về mặt kỹ thuật nên thuộc về xem xét mã .

Đề nghị duy nhất của tôi là bạn nên che / thay đổi bit nên là macro hoặc các chức năng mô tả những gì đang diễn ra. Nhận xét là tốt nhưng chúng không hoạt động và cuối cùng bất cứ điều gì các ý kiến ​​nói không có nghĩa gì; đó là mã được tính (và chúng sẽ không đồng bộ). Làm cho mã trở thành các bình luận với phân rã chức năng:

RCC->AHBENR  |=  ((1UL << 21) );  

trở thành

enableGpioe() {
    RCC->AHBENR  |=  (1UL << 21);  
}

và điều này

GPIOE->BSRR = 1 << 9; 

trở thành

void setLEDStatus(LED led, Status status) {
     led = 1 << status;     // I don't know your struct members.
}

khi nào

Đèn led

Trạng thái

là enum. Tối thiểu, bạn có thể biến chúng thành MACROS. Điều này sẽ giúp bạn giảm bớt việc phải nhớ đúng mặt nạ bit để thực hiện nhiệm vụ.


2
Mặc dù nó thực sự có thể thuộc về CodeReview.SE, mọi người ở đó ít có khả năng hữu ích hơn khi nói đến mã nhúng và thực tiễn. Một số người có thể giúp đỡ, nhưng có lẽ ít hơn ở đây.
Morwenn

Mã là mã :) - Bạn có thể đúng về thái độ ở đó nhưng bạn cũng có nhiều khả năng nhận được phản hồi ở đó.
Christian Bongiorno
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.