Là con trỏ được đối xử khác nhau trong AVR so với x86?


7

Vi điều khiển: ATtiny13

IDE: Studio của Atmel

Tôi đang cố gắng viết một ứng dụng Hello World bằng cách viết cao lên cổng PB4.

Điều này hoạt động tốt:

int main(void)
{
    DDRB = 0x10;
    PORTB = 0x10;

    while(1)
    {
    }
}

Nhìn vào định nghĩa của DDRB và PORTB, họ chỉ ra 0x17 và 0x18, như mong đợi.

Điều này, tuy nhiên, không hoạt động:

int main(void)
{
    char *dir = (void *)0x17;
    char *port = (void *)0x18;

    *dir = (unsigned int)0x10;
    *port = (unsigned int)0x10;

    while(1)
    {
    }
}

Là mã của tôi sai, hay tôi cần phải làm gì khác để sử dụng con trỏ?


1
Tại sao bạn sẽ sử dụng con trỏ cho một cái gì đó đơn giản?
Matt Young

2
Phải bắt đầu ở đâu đó - lần đầu tiên lập trình một mcu AVR.
tgun926

5
So sánh sự tháo gỡ của cả hai sẽ là một bài tập tốt.
Matt Young

1
Các unsigned intdiễn viên không có ý nghĩa; Hướng dữ liệu và thanh ghi cổng chỉ rộng 8 bit. Đây không phải là vấn đề của bạn, nhưng hãy thử phân vai uint8_t.
sherrellbc

@sherrellbc Tôi đã thử các loại đúc khác nhau và không đúc, nhưng nó không tạo ra sự khác biệt. 0x10rộng 8 bit phải không?
tgun926

Câu trả lời:


23

Con trỏ là con trỏ. Đó là những gì họ đang có. Họ không đối xử khác nhau (bạn có thể đối xử với họ khác nhau như thế nào?)

Sự khác biệt chính giữa X86 và AVR là:

  • AVR là 8 bit, X86 là 32 bit (hoặc 64 bit cho x86_64), vì vậy con trỏ có kích thước khác nhau.
  • AVR là kiến ​​trúc Harvard được sửa đổi, do đó có nhiều hơn một không gian địa chỉ, vì vậy bạn phải đảm bảo rằng bạn đang tham chiếu đúng không gian địa chỉ.

Ngoài ra, mã của bạn không có ý nghĩa:

char *dir = (void *)0x17;

Gán một khoảng trống * cho một char *?

error: invalid conversion from void*’ to char*’

Hiện tại tôi chưa được thiết lập để biên dịch ATTiny13, vì vậy những con số này đều dành cho ATMega328p:

Truy cập kết quả DDRB và PORTB trong hội đồng này:

12c:    80 e1           ldi r24, 0x10   ; 16  
12e:    84 b9           out 0x04, r24   ; 4
130:    85 b9           out 0x05, r24   ; 5

Truy cập một con trỏ đến một vị trí bộ nhớ dẫn đến lắp ráp này:

12c:    80 e1           ldi r24, 0x10   ; 16
12e:    80 93 17 00     sts 0x0017, r24
132:    80 93 18 00     sts 0x0018, r24

Như bạn có thể thấy, DDRB và PORTB không phải là biến thông thường. DDRB được định nghĩa là:

#define DDRB _SFR_IO8(0x04)

và PORTB là:

#define PORTB _SFR_IO8(0x05)

_SFR_IO8 () là một macro:

#define _SFR_IO8(io_addr) _MMIO_BYTE((io_addr) + __SFR_OFFSET)

và _MMIO_BYTE là:

#define _MMIO_BYTE(mem_addr) (*(volatile uint8_t *)(mem_addr))

__SFR_OFFSET có thể là 0 hoặc 0x20 tùy thuộc vào chip (thông thường là 0x20).

Vì vậy, điều đó có nghĩa là địa chỉ của DDRB và PORTB phải lớn hơn 0x20.

Nhìn vào chip 328P, DDRB là 0x04 và PORTB là 0x05. Vì vậy, truy cập dưới dạng 0x24 và 0x25, với các loại dữ liệu phù hợp, do đó:

volatile uint8_t *dir = (volatile uint8_t *)0x24;
volatile uint8_t *port = (volatile uint8_t *)0x25;

*dir = 0x10;
*port = 0x10;

kết quả trong hội nghị này:

12c:    80 e1           ldi r24, 0x10   ; 16
12e:    84 b9           out 0x04, r24   ; 4
130:    85 b9           out 0x05, r24   ; 5

Nhìn có quen không? Trình biên dịch đã nhận ra phần bù 0x20, nhận ra chúng là SFR và được biên dịch theo đúng outhướng dẫn mà không có phần bù 0x20.

Vì vậy, truy cập địa chỉ cổng của bạn + 0x20 có thể hoạt động cho ATTiny13.

Chỉ cần nhìn vào ATTiny25, DDRB = 0x10kết quả trong:

out 0x17, r24

và truy cập một con trỏ tại địa chỉ 0x37 dẫn đến:

out 0x17, r24

Vì vậy, có vẻ như nó có thể là nó (thêm 0x20 vào địa chỉ con trỏ của bạn).


Bạn có ý nghĩa gì có nhiều hơn một không gian địa chỉ? Tôi đã xem xét các tài liệu cho các MCU, và nó cho thấy một bản đồ bộ nhớ từ 0x0để 0x9Fđược sử dụng để truy cập vào sổ đăng ký, I / O và SRAM. Các không gian địa chỉ khác là gì?
tgun926

2
Kiến trúc Harvard xác định tối thiểu 2 không gian địa chỉ - chương trình (flash) và dữ liệu (sram). Có các chức năng đặc biệt để truy cập không gian địa chỉ chương trình.
Majenko

4
Chúng có thể được tối ưu hóa vì trình biên dịch không biết rằng chúng đang được đọc. Nếu một biến đang được đặt nhưng chưa bao giờ được đọc thì nó có thể loại bỏ ghi vì nó không được sử dụng ở bất cứ đâu. Bằng cách làm cho nó biến động, nó báo cho trình biên dịch nó đang được đọc ở nơi khác (trong phần cứng trong trường hợp này), do đó, để lại ghi đúng chỗ.
Majenko

1
SFR = Đăng ký chức năng đặc biệt. Nó nhận ra họ theo địa chỉ. Không biết làm thế nào, nhưng nó làm. Bạn nên kiểm tra tệp ioxxxx.h cho chip bạn đang sử dụng. Nó có tất cả các định nghĩa cho các thanh ghi trong đó.
Majenko

1
Ngay cả trên 8x86 được sử dụng trong PC (và 8080 trước nó) cũng có các hướng dẫn riêng để truy cập vào không gian và bộ nhớ I / O. Một số thiết bị I / O như thẻ hiển thị sử dụng một phần không gian địa chỉ bộ nhớ ngoài một số không gian địa chỉ I / O, nhưng trên 8x86 có cách xác định một con trỏ để ví dụ như thanh ghi điều khiển LPT1. Nó có một địa chỉ (0x378) nhưng viết 0x34 cho nó theo một phương ngữ C điển hình sẽ yêu cầu outportb(0x378, 0x34);.
supercat
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.