1-wire на VHDL

Чтобы подключить какую-то микросхему к микроконтроллеру, их надо связать каким-то протоколом передачи данных. В МК для этого есть различная периферия, которая берет на себя реализацию этого протокола и не загружает ядро. В ПЛИС никакой периферии нет. Что же делать?

Остается самому писать модули для обмена или искать готовые.

Чаще всего для связи между микросхемами используется SPI или I2C, но различные датчики используют 1-wire. Если SPI проще простого, а реализацию I2C можно где-то найти, то с однопроводным интерфейсом немного труднее. Сам протокол уже неоднократно описан (для понимания написанного вам надо знать как он работает), так что я только приведу реализацию на VHDL.

Вся программа выглядит так:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;
	
entity one_wire is
	port ( reset : in std_logic;
			read_byte : in std_logic;
			write_byte : in std_logic;
			wire_out : out std_logic;
			wire_in : in std_logic;
			presense : out std_logic;
			busy : out std_logic;
			in_byte : in std_logic_vector (7 downto 0);
			out_byte : out std_logic_vector (7 downto 0);
			clk : in std_logic );
end one_wire;

architecture a of one_wire is
signal count : std_logic;
signal counter : integer range 0 to 127;

begin
process (clk)

type finit_state is (start, delay_reset, wire_read_presense, wire_0, wire_write, wire_read, delay );
variable state : finit_state := start; 

variable n_bit : integer range 0 to 7;
variable f : std_logic;
begin
if (clk'event and clk = '1') then
case (state) is
	when start => wire_out <= '1';         -- здесь программа посто висит и ждет команд
				busy <= '0';
				count <= '0';
				if (reset = '1') then        -- пришла команда сбросить шину
					busy <= '1';
					presense <= '0';
					state := delay_reset;   -- переходим туда, где эта шина сбрасывается
				elsif (write_byte = '1') then 
					f := '0';
					busy <= '1';
					state := wire_0;
				elsif (read_byte = '1') then
					f := '1';
					busy <= '1';
					state := wire_0;
				end if;
					
	when delay_reset => wire_out <= '0';     -- сбрасываем шину, т. е. выставляем 0 и ждем 480 мкс
				count <= '1';
				if (counter = 78) then
					state := wire_read_presense;
					count <= '0';
				end if;
			
	when wire_read_presense => wire_out <= '1';
				count <= '1';
				if (counter = 11) then     -- проверяем ответ от устройства
					presense <= not wire_in;
				end if;
				if (counter = 78) then 
					state := start;
					count <= '0';
				end if;
					
	when wire_0 => wire_out <= '0';                    -- инициируем передачу или прием бита
				if (f = '0') then
					state := wire_write;
				else 
					state := wire_read;
				end if;
					
	when wire_write => 
				if (in_byte(n_bit) = '1') then   -- по-очереди передаем байт
					wire_out <= '1';
				end if;
				state := delay;
										
	when wire_read => wire_out <= '1';
				count <= '1';
				if (counter = 1) then     
					out_byte(n_bit) <= wire_in;   -- считываем бит
					count <= '0';
					state := delay;
				end if;
				
	when delay => 
				count <= '1';
				if (counter = 8) then     -- задержка перед приемом или передачей следующего бита
					count <= '0';
					wire_out <= '1';
					if (n_bit = 7) then    -- если все биты приняты/переданы возвращаемся на начало
						n_bit := 0;
						state := start;
					else n_bit := n_bit + 1;
						state := wire_0;
					end if;
				end if;
								
end case;
end if;
end process;

-- счетчик, тикает с периодом 6 мкс, нужен для выдерживания временных интервалов
process (clk)
begin
if (count = '0') then
	counter <= 0;
elsif (clk'event and clk = '1') then
	counter <= counter + 1;
end if;
end process;

end architecture;


Все работает очень просто, в основе лежит конечный автомат, работу которого я уже описывал UART приемник на VHDL.

Передача и прием происходит по одному байту, но при желании это легко меняется изменением переменной n_bit и увеличением разрядности входя и выхода.

Получился такой блок

Добавляем буфер с отрытым стоком (opndrn в папке с буферами) и двунаправленный пин (bidir).

Как этим пользоваться
На вход clk подаются тактовые импульсы с периодом 6 мкс. Входы reset, read_byte, write_byte нужны для запуска соответствующей функции. На них выставляется 1, после появления 1 на busy их нужно сбросить в 0 (что-то на подобии регистров в МК, записал 1 — пошла работа, только сбрасывать ручками надо), если этого не сделать, то по завершению текущих действий они снова повторятся, причем наивысший приоритет у reset, наименьший у read_byte. Busy выставляется в 1 когда модуль что-то делает, если busy переходит с 1 в 0 можно считывать данные или запускать новый процесс.

Здесь реализована только аппаратная часть, всю логику должен делать другой модуль или внешний контроллер (тогда сюда надо будет еще прикрутить SPI). Если все делать в ПЛИС то для нормальной работы нужна еще память где будут храниться адреса устройств.

Файлы в топике: one_wire.zip

Комментарии (3)

RSS свернуть / развернуть
Никогда ничего на плис не делал. Это войдет в EPM7064STC100-10 на 64 макроячейки?
0
  • avatar
  • mlex
  • 25 марта 2011, 21:48
Главное ограничение у CPLD это количество триггеров, каждая ячейка содержит один триггер, если просмотреть код то должно влезть вроде, в коде не видно что требуется большое количество регистров.
0
Как раз влезет, оно занимает 63 логические ячейки, но управлять всем этим придется другой плис или микроконтроллером.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.