Вы можете, как другие предложили, объявить соответствующий указатель, т. Е.
volatile uint32_t *reg = (volatile uint32_t *)0xc000;
Обратите внимание, что я добавил квалификатор volatile
. Это всегда хорошая идея при чтении или записи аппаратных регистров, поскольку она гарантирует, что каждый доступ, который вы выполняете в своем коде C, действительно отображается в сгенерированном коде.
Однако я обычно предпочитаю писать макросы вот так
#define READ_LCDCW1() ...
#define WRITE_LCDCW1(value) ...
и затем заполните их соответствующими gcc asms. Мне они нравятся лучше, чем прямое использование указателей, потому что:
- Я думаю, что они читают лучше в коде, идентифицируя, что я делаю, читая регистр, вместо того, чтобы сосредоточиться на том, как я это делаю.
- Для некоторых регистров требуется многоэтапный процесс чтения с аппаратного обеспечения. Это легко скрывается в макросах этого стиля, и большая часть моего кода все еще относится к интересующим меня регистрам, а не к сложному способу, которым аппаратные средства заставляют меня их трогать.
- Наконец, используя
asm
s, я точно знаю, как я получаю доступ к регистру. Иногда существуют специальные инструкции или адресные пространства, необходимые для доступа к регистру, которые обычно не могут быть сгенерированы компилятором C.
- Даже если вы не согласны с мотивацией использования операторов asm, я бы по-прежнему предлагал обернуть ваши обращения к реестру в макросы (или встроенные функции), подобные этим.
В вашем случае самые простые определения должны быть:
#define LCDCW1_ADDR 0xc000
#define READ_LCDCW1() (*(volatile uint32_t *)LCDCW1_ADDR)
#define WRITE_LCDCW1(val) ((*(volatile uint32_t *)LCDCW1_ADDR) = (val))