LowLevelPortIo rus

From Nutwiki
Jump to: navigation, search

Низкоуровневые операции с портами ввода/вывода.

Memory Mapped or Port Mapped

Низкоуровневые аппаратные порты (например цифровые или аналоговые I/O, RS232 и т.д.) как правило, управляются специальными регистрами. Например, большинство процессоров, используемых в системах как правило имеют встроенные двунаправленные цифровые порты ввода / вывода управляемые набором регистров.

• Port direction register Each bit in this register specifies, if the corresponding port pin is used as an input or output. • Port output register If the corresponding bit in the port direction register is set to output, then the bit value in this register will directly set the port pin. If the bit is zero, the related output pin will be driven low. If the bit is set to one, then the pin will be driven high. • Port input register

If the corresponding bit in the port direction register is set to input, then this register will reflect the current status at the related pin. If the signal level at the pin is low (e.g. tied to ground), the corresponding bit will be zero. If the level passes a certain voltage, then the bit in this register will change to one. Depending on the CPU, hardware I/O port registers may be accessed like any other memory location. This is called memory mapped I/O. Some CPUs, most notably Intel's x86 series, offer a dedicated I/O bus, which is seperated from the memory bus. In this case the hardware registers are port mapped and specific CPU instructions like inp (read port) or outp (write port) must be used to access them. Although all CPUs currently supported by Nut/OS provide memory mapped I/O, specific C language macros for port access are preferred in order to hide this difference. These macros can be easily adapted to both, memory mapped and port mapped hardware. Furthermore, it is easier to get applications running in an emulator, if access to I/O registers is distinguishable from access to memory locations. Размерность регистров. Beside differing access methods, CPUs provide different register sizes. Typically, 8-bit machines use 8-bit I/O registers while 32-bit machines use 32-bit I/O registers. Nevertheless, while 8-bit hardware may use 16 or 32 bit registers, almost all 32-bit CPUs are able to access registers with 16 or 8 bits in size. Thus, a number of macros for different register sizes is provided. The Nut/OS macros inr() and outr() provide access to the CPU specific register size (missing on 8-bit targets).


Макросы доступа к портам в Nut/OS.

Размер регистра Тип переменной Макрос вывода Макрос ввода 8 Бит unsigned char outb(регистр/порт, значение) inb(регистр/порт) 16 Бит unsigned short outw(регистр/порт, значение) inw(регистр/порт) Определяется типом CPU 8, 16 or 32 Бит unsigned int outr(регистр/порт, значение) inr(регистр/порт)

Использование цифровых портов ввода/вывода в AVR. Чтение данных цифрового порта состоит из 2 шагов: 1. Установка необходимого регистра порта в режим ввода. 2. Чтение данных порта ввода. Пример чтения значений всех 8 битов порта PORTB .

  1. include <compiler.h> /* Совместимость платформ. */

unsigned char val; outb(DDRB, 0x00); /* Установить все 8 битов порта PORTB для ввода.*/ val = inb(PINB); /* Чтение значений всех 8 битов порта PORTB в переменную.*/ Запись данных в цифровой порт состоит из 2 шагов: 1. Установка необходимого регистра порта в режим вывода. 2. Запись данных в порт. Пример записи значений всех 8 битов порта PORTB .

  1. include <compiler.h>

outb(DDRB,0xFF); /* Установка регистра порта PORTB для вывода.*/ outb(PORTB,0x00); /* Установка всех битов порта PORTB в низкое (0000 0000).*/ outb(PORTB,0xFF); /* Установка всех битов порта PORTB в высокое (1111 1111)*/ Using Digital I/O on AT91 ARM Here's how to read the level at the 8 least significant bits of the digital I/O port on the AT91R40008 CPU.

#include <compiler.h> /* Provides compatibility among platforms. */

unsigned int val;
outr(PIO_ODR, 0xFF); /* Use lower 8 bits for input. */
outr(PIO_PER, 0xFF); /* Enable PIO function. */
val = inr(PIO_PDSR); /* Read pin status. */

For digital output, a sightly different method is used on Atmel's AT91 series. Different registers are provided to set and clear the bits. In other word, there is a specific register for setting a digital output to high and another one to set the output to low.

#include <compiler.h>

outr(PIO_OER, 0xFF);  /* Use lower 8 bits for input. */
outr(PIO_PER, 0xFF);  /* Enable PIO function. */
outr(PIO_CODR, 0xFF); /* Set bits to low. */
outr(PIO_SODR, 0xFF); /* Set bits to high. */

[edit] Native AVR I/O Register Access When looking to other AVR sample code apart from Nut/OS, you may have noticed that there are no specific input and output macros. Most AVR compilers allow to use I/O registers like C variables. For example, setting the four most significant bits of PORTB to outputs and the remaining four least significant bits to inputs can be written as

DDRB = 0xF0;

This will even work for single bits, like setting output bit 4 of PORTB to 1, leaving all other bits unchanged.

PORTB |= 0x10;

You can use this method in your Nut/OS applications as well. However, as explained above, this way of register access is not fully portable and therefore Nut/OS prefers input and output macros, like

outb(DDRB, 0xF0);
outb(PORTB, inb(PORTB) | 0x10);

However, that second statement may not be exactly translated in the same machine instructions as the statement that uses PORTB like a C variable. That's because on certain registers the CPU is able to modify single bits in a single instruction. When using inb() and outb(), the compiler will at least generate three machine instructions. 1. Read value from PORTB register 2. Set bit 4 of this value 3. Write value back to PORTB register To solve this problem, Nut/OS provides two additional macros, which allow to set or clear single bits. Setting bit 4 of register PORTB can be written as

sbi(PORTB, 4);

Note, that sbi() as well as its counterpart cbi() for clearing bits expect bit numbers instead of mask values. [edit] Native AT91 ARM I/O Register Access Developers, which are familiar with the header files provided by Atmel for I/O register access will miss the register structure definitions. They are most convenient and produce highly readable code. However, they are not fully portable, very difficult or impossible to emulate and they can't be included into low level assembler code. Although not recommended, you can use these header files in your Nut/OS applications. [edit] Возможные ошибки. No Input Change on AVR Make sure you use inb(PINB), not inb(PORTB). The latter doesn't query the status of the input pin, but of its pull-up resistor. [edit] 16 Bit Register Access Fails on AVR If two 8-bit accesses work while a single 16-bit access fails, then the register you are using requires a different order. The 16 bit outw() and inw() macros write or read the low byte first, followed by the high byte. This may not work on all registers, specifically with AVR timers/counters. [edit] No Input Change on ARM Most ARM CPUs require to enable the PIO clock in order to latch input pins. This may be even true for output pins.