Có gì sai với việc truyền mã này trong mã C cho AVR?


8

Tôi đã xác định hai biến:

uint8_t a[2];
uint16_t b;

Tiếp theo tôi muốn sử dụng anhư biến của loại uint16_t, ví dụ

b = (uint16_t)a;

Nhưng điều này là sai! Các chương trình của tôi không hoạt động chính xác với mã như vậy. Tất cả là OK khi tôi thay thế bđể uint8_t b[2]và các hoạt động sử dụng elementwise.

Tại sao?

avr  c 

5
Tại sao bạn không ném một số giá trị vào ví dụ của bạn và cho chúng tôi biết kỳ vọng của bạn về "chính xác" là gì để chúng tôi thực sự có thể giúp đỡ mà không suy đoán về ý định ngữ nghĩa của bạn.
Abbeyatcu

1
Điều này sẽ phù hợp hơn nhiều cho Stack Overflow.
sharptooth

Câu trả lời:


16

alà một con trỏ tới một mảng byte. Nếu bạn truyền nó tới uint16_t và gán nó cho b, thì nó bsẽ chứa địa chỉ của cơ sở của mảng (nơi nó được lưu trữ) trong SRAM. Nếu bạn muốn coi hai byte của mảng alà một số nguyên, thì hãy sử dụng liên kết theo đề xuất của user14284, nhưng lưu ý rằng liên kết sẽ biểu diễn mảng byte theo thứ tự byte bộ nhớ của kiến ​​trúc (trong AVR sẽ nhỏ -endian, có nghĩa là byte 0 là byte có ý nghĩa nhỏ nhất). Cách để viết mã đó là:

union{
  uint8_t a[2];
  uint16_t b;
} x;

x.b[0] = 0x35;
x.b[1] = 0x4A;

// by virtue of the above two assignments
x.a == 0x4A35 // is true

Một cách khác để làm điều này mà không cần sử dụng liên kết là chuyển asang một con trỏ uint16_t và sau đó hủy đăng ký như vậy:

uint8_t a[2] = {0x35, 0x4A};
uint16_t b = *((uint16_t *) a);
b == 0x4A35; // because AVR is little endian

Nếu bạn đang sử dụng bộ đệm để lưu trữ dữ liệu cuối lớn (ví dụ thứ tự byte mạng), thì bạn sẽ cần trao đổi byte để sử dụng một trong hai kỹ thuật này. Một cách để làm điều đó mà không có bất kỳ nhánh hoặc biến tạm thời nào là:

uint8_t a[2] = {0x35, 0x4A};
a[0] ^= a[1];
a[1] ^= a[0];
a[0] ^= a[1];

a[0] == 0x4A; // true
a[1] == 0x35; // true

Ngẫu nhiên đây không phải là một vấn đề về AVR hay thậm chí là nhúng. Mức độ ứng dụng mạng mã được viết cho máy tính thường các cuộc gọi chức năng gọi htonl, htons(host vào mạng, 32 và 16-bit biến thể) và ntohl, ntohs(mạng đến máy chủ, 32 và 16-bit biến thể) mà hiện thực là mục tiêu kiến trúc phụ thuộc là liệu họ hoán đổi các byte hoặc không (theo giả định rằng các byte được truyền 'trên dây' luôn luôn là kết thúc lớn khi chúng là một phần của các từ nhiều byte).


Đây là một câu trả lời tuyệt vời. Bản thân khóa ' a' là một con trỏ.
Jon L

2
"Một cách khác để làm điều này mà không cần sử dụng liên kết là chuyển một con trỏ uint16_t và sau đó bỏ qua nó" - Trên thực tế, kiểu truyền này thường phá vỡ các quy tắc bí danh nghiêm ngặt. Bạn không nên làm điều đó trừ khi bạn biên dịch -fno-strict-aliasing.
Jim Paris

3

Nếu mục đích của bạn là ghép hai biến 8 bit thành biến 16 bit, hãy sử dụng a union. Nếu bạn muốn truyền một thành viên avào b, sau đó chỉ định phần tử nào của mảng bạn muốn sử dụng.


2

Trên mã của bạn, bạn chỉ truyền con trỏ đến mảng.

Bạn cần bỏ giá trị được chỉ bởi a.

b = (uint16_t)*a;

Tôi chưa bao giờ sử dụng AVR nhưng nếu bạn đang làm việc với kiến ​​trúc 16 bit, bạn phải chắc chắn rằng một từ được căn chỉnh. Không làm điều này có thể dẫn đến một ngoại lệ.


1
... đây hoàn toàn không phải là ý định của anh ta ... anh ta muốn b liên quan đến cả hai yếu tố của một (điều này hoàn toàn không để ý đến [1]), cũng không có ngoại lệ hoặc hạn chế nào về những gì bạn có thể sử dụng trong avr-gcc
Abbeyatcu

0

Mỗi thành viên của a là một số 8 bit. Nó không thể chứa bất cứ thứ gì lớn hơn. Truyền nó tới 16 bit không làm gì cả a . Nó chỉ đơn thuần trích xuất bất kỳ giá trị nào mà a có thể giữ và chuyển đổi nó thành 16 bit để phù hợp với định dạng của b khi giá trị được lưu trữ ở đó.

Bạn thậm chí không đề cập đến một thành viên của a . Bạn phải sử dụng [0] hoặc [1] (chứ không phải [2]!). Nếu bạn sử dụng một mình, bạn chỉ cần lấy địa chỉ của nó. (Bắt tốt, Bruno).

Khai báo a là một mảng gồm hai số 8 bit cũng không làm cho nó thành một số 16 bit. Bạn có thể thực hiện một số việc theo chương trình để lưu trữ và truy xuất các giá trị 16 bit bằng cách sử dụng các chuỗi 8 bit, nhưng không phải theo cách bạn nghĩ.


0

Nếu bạn muốn chuyển đổi các byte athành giá trị 16 bit và biểu diễn là endian nhỏ (8 bit thấp hơn của giá trị nằm trong byte đầu tiên), hãy làm

uint16_t b = a[0] | (a[1] << 8);

Đối với một đại diện endian lớn làm

uint16_t b = (a[0] << 8) | a[1];

Tránh sử dụng phôi con trỏ hoặc công đoàn để làm điều này, vì điều đó dẫn đến các vấn đề về tính di động.

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.