Еще немножко о мультиплексировании

AVR
Встала тут задачка, сделать беспроводной брелок для звонка.
И как раз в тему уважаемый neiver выложил статью о радиомодуле RFM70. Вот я и решил заказать пару этих модулей, поэкспериментировать, да и для пользы дела использовать. Долго-ли, коротко-ли я мучал обе RFM-ки и заваливал neiver'а глупыми вопросами — то может быть будет другая статья, а причем тут мультиплексирование — прошу под кат:

Корпуса под брелок мне попались в местном магазинчике радиодеталей тоже вовремя, т.к. раньше я таких не видел, поэтому габариты устройства были определены довольно жестко. В первую очередь встал вопрос, на каком контроллере делать? Чтоб маленький, и при этом ног хватило. Ну и лишние чтоб не болтались особенно. Сразу в глаза бросилась ATTiny13A, которых у меня было в загашнике штук пяток… Но вот беда — из 5ти доступных выводов — все нужны для управления RFM-кой. А еще ведь кнопка нужна… Да и светодиодик какой-нибудь, чтоб видно было что передача прошла или ошибка какая… И еще один нюанс: Тини 13 не имеет аппаратного SPI. Он его понимает только для внутрисхемного программирования, кстати, на которое тоже по хорошему, ноги нужны. Вот тут мы и приходим к мультиплексированию.

Мои опыты с RFM-кой не прошли напрасно. Я знал какие ноги ни в коем случае нельзя использовать в двойном назначении: это ногу управляющую сигналом CSN и ногу управляющую сигналом CE. Значит остаются 3 ноги, которые используются для программирования контроллера. Внутрисхемое программирование у нас идет по интерфейсу SPI, RFM70 тоже управляется по интерфейсу SPI. Значит, если мы заведем одноименные ноги на RFM и на площадки для разъема программирования — сразу убьем 2х зайцев. Чтобы RFM не мешала программированию, а так же не ловила ложные сигналы — ноги управления мы подтянем к соответствующим номиналам.

Остались неохваченными кнопка и светодиод. Что с ними делать?
В принципе понятно что делать… У нас согласно расположению ног SPI есть 2 ноги выхода и одна нога входа. Значит светодиод надо как-то прицепить к выходу, чтобы он не мешал, а кнопку — ко входу, опять же чтобы ничего не закоротить.

Со светодиодом мне подсказала решение когда-то прочитанная статья ДИ об управлении семисегментным индикатором по одному проводу. Я просто взял ногу выхода SCK и посадил на нее светодиод через RC-цепочку. Теперь общение контроллера с RFM светодиод практически не затрагивает, т.к. конденсатор не успевает зарядиться, а если надо светодиод зажечь — просто даем на эту ногу более длительный импульс и вуаля…

С кнопкой тоже все просто. Первым делом отвязываем ее от интерфейса махоньким резистором. 33 Ома вполне подходит. И так как кнопка у нас будет замыкаться на землю — притягиваем сигнал с нее к питанию резистором посолиднее. Я взял 10 KОм.
Ну и так как у нас устройство мобильное — предусматриваем в схеме батарейку и для защиты от переполюсовки — диод Шоттки.

Вот и все мультиплексирование на уровне схемы.

Схема, которая у меня получилась:



То, что получилось после разводки:



После сборки выяснилось, что корпус отсека батарейки все-таки в брелок не умещается, в результате я просто вынул из него контактные площадки и запаял без корпуса. Это заодно решило проблему пайки батарейного отсека и кнопки, т.к. у них площадки располагались под корпусами друг друга и чтоб все это запаять пришлось попотеть.

Еще проблемка была в том что выводы у RFM расположены с шагом 1.27 мм и контактные площадки для нее достаточно тонкие. Чтоб их не оторвало при сверлении я сначала вытравил нижнюю часть платы, с которой у меня планировалось ставить модуль, потом просверлил дырки под RFM, а потом уже вытравил верхнюю часть платы. В результате верхние площадки, к которым и паялась RFM сверловка никак не потревожила.

С кодом особых проблем не возникло.

Отсутствующий SPI-интерфейс не представляет ничего сложного, потому я его быстренько набыдлокодил.
Общий алгоритм работы брелка прост:
1. При включении питания инициализируем ноги согласно схемы, и тупим некоторое время давая инициализироваться модулю.
2. Читаем с модуля регистр статуса и регистр конфигурации.
3. Если значения указанных регистров не соответствуют значениям по умолчанию (указаны в даташите) — зажигаем светодиод и тупим в цикле сигнализируя что модуль не инициализировался правильно и надо переподключить батарейку заново. Если значения верны, то переходим к пункту 4.
4. Разрешаем внешние прерывания по изменению уровня и засыпаем. Потребление в спящем режиме порядко 200 мкА
5. Если происходит прерывание — просыпаемся и ждем отпускания кнопки заодно фильтруя дребезг.
6. Когда кнопка отпущена (чтоб не мешала интерфейсу), инициализируем модуль на передачу, отправляем 2 байта посылки, мигаем светодиодом и снова засыпаем. Потребление в момент передачи мультиметр фиксирует в районе 3 мА.

Вот собственно и все.

Как приеду с командировки — выложу фотки. Если кому будет интересно — могу вылжить код.

UPD.
Выкладываю код, т.к. просьбы были:





// The RFM70D transmitter controller v.1
// () 2011 Stas Novoselsky

#include<avr/interrupt.h>
#include"typedefs.h"

#include<compat/deprecated.h>
#include"controller.h"
#include"delay_.h"
#include"rfm70.h"


volatile u08 spi_shift_in, spi_shift_out;
volatile u08 databuf[32];

#include"rfm_cmd.h"

#ifdef DEBUG
	#include"led_7.h"
#endif

#define wdr() asm volatile ("wdr")
#define RF_NOP			0xff

SIGNAL (PCINT0_vect) //В прерывании только гасим флаги разрешающие прерывания
// и запрещаем контролеру спать (чтоб сам в сон не свалился)
{
	cli();
	GIMSK &=0xdf; // reset PCIE bit 
	MCUCR&=0x9f; //reset SE-bit
	return;
}

void main_init(void) 
{

  	PORTB=0x00;
	DDRB=0x15;
	ACSR=0x80; //disable Analog Comparator - Гасим лишних потребителей
	ADCSRA=0x80; // disable ADC;
	PRR=0x03; //disable ADC and Timers
	MCUCR |=0x10; //select Power Down mode - настраиваем режим сна
	GIMSK |=0x20; // set PCIE bit
	PCMSK |=0x02; // Select PB1 for interrupt
	spi_shift_in=0x00;
	spi_shift_out=0x00;
	CSN_HIGH;
#ifdef DEBUG
	led7_init();
#endif
}



void spi_transfer(void) //собственно реализация обмена по SPI
{
	if bit_is_set(spi_shift_out,7) //выставляем текущий бит на линии MOSI
	{
		sbi(MOSI_PORT,MOSI); 
	} 
	else 
	{
		cbi(MOSI_PORT,MOSI);
	};
	dummyloop(25);
	sbi(SCK_PORT,SCK); // создаем передний фронт SCK
	dummyloop(25);
	spi_shift_in<<=1; // подготавливаем приемник
	cbi(SCK_PORT,SCK); // создаем задний фронт SCK
	if bit_is_set(MISO_PIN,MISO) //читаем MISO
	{
		spi_shift_in|=0x01;
	}
	else
	{
		spi_shift_in&=0xFE;
	};
	spi_shift_out=spi_shift_out<<1;
}


u08 spi_byte_io(u08 data) //прием-передача байта по SPI
{
	u08 i;

	spi_shift_in=0x00;
	spi_shift_out=data;
	for (i=0; i<8; i++) spi_transfer();
	return (spi_shift_in);
}


void send_command (volatile u08 *buf, u08 byte_num) передача команды в RFM

{
	u08 k;

	dummyloop(40);
	CSN_LOW;
	dummyloop(40);
	for (k=0; k<byte_num; k++) (*(buf+k)=spi_byte_io(*(buf+k)));
	dummyloop(40);
	CSN_HIGH;
	dummyloop(40);
}



int main(void)
{
	u08 a;

	cli();
	dummyloop(100);
	main_init();	// init
	dummyloop(800); // даем время RFM-модулю запуститься и прийти в себя после подачи питания




#ifdef DEBUG
	led7_out(LED_CLEAR,1);
	led7_out(LED_CLEAR,1);
	dummyloop(200);
#endif

	a=0;
	while (1) 
	{
		get_RFMstatus; // первое чтение RFM
		if (databuf[0]==0x0e)
		{
			get_RFMconfig;
			if (databuf[1]==0x08) a=1;
		}
		else  // если проблема с инициализацией - зажигаем светодиод
		{
			led_on; 
			dummyloop(100);
			
		};	
		if (a==1) break; // если данные верны - выходим из вечного цикла
	};
#ifdef DEBUG
	a=(databuf[1]&0x0f);
	led7_out(led[a],0);
	a=((databuf[1]>>4)&0x0f);
	led7_out(led[a],1);
#endif

	while (1) // основной цикл
	{
		sei();
		led_off; // гасим светодиод
		if (bit_is_clear(BUTTON_PIN,BUTTON_IN)) // проверяем нажатие кнопки
		{
			cli();
			dummyloop(1000);
			if (bit_is_set(BUTTON_PIN,BUTTON_IN)) // проверяем что кнопка отпущена после паузы
			{
// 						transmitter init and transmitt packet

				RFM_flush;
				CE_LOW;
				dummyloop(500);
	
				RFM_activate;
				set_PTX;
				set_PWR_UP;
				transmitt;
#ifdef DEBUG
				a=(databuf[1]&0x0f);
				led7_out(led[a],1);
				a=((databuf[1]>>4)&0x0f);
				led7_out(led[a],1);
#endif
	
				clear_RFMflags;	
				reset_PWR_UP;
			};
				
		}
		else // если кнопка не нажималась - засыпаем
		{
			MCUCR|=0x20; //set SE-bit	
			GIMSK |=0x20; // set PCIE bit
			sei();
			asm volatile ("sleep");
			dummyloop(500);
		};
	};



    return(0);
}
// ----------------  end of file   -------------------------



Чтобы код был более читаемый я задефайнил нужные мне команды для RFM.
В основной код он инклудится как rfm_cmd.h. Вот этот код:


#define get_RFMstatus	databuf[0] = RFM_NOP_NOP; \
						send_command(databuf,1)
#define get_RFMconfig	databuf[0] = (RFM_READ_REG | RFM_CONFIG); \
						databuf[1] = 0xff; \
						send_command(databuf,2)
#define put_RFMconfig	databuf[0] = (RFM_WRITE_REG | RFM_CONFIG); \
						send_command(databuf,2) // put current databud[1]
#define clear_RFMflags	databuf[0] = (RFM_READ_REG | RFM_STATUS); \
						databuf[1] = 0xff; \
						send_command(databuf,2); \
						databuf[0] = (RFM_WRITE_REG | RFM_STATUS); \
						send_command(databuf,2) // put current databud[1]

#define RFM_flush		databuf[0] = (RFM_FLUSH_TX); \
						send_command(databuf,1)
#define RFM_activate	databuf[0] = (RFM_ACTIVATE_CMD); \
						databuf[1] = 0x73; \
						send_command(databuf,2)
#define set_PTX			get_RFMconfig; \
						databuf[0] = (RFM_WRITE_REG | RFM_CONFIG); \
						databuf[1] &= 0xfe; \
						send_command(databuf,2); \
						dummyloop(200)
#define set_PWR_UP		get_RFMconfig; \
						databuf[0] = (RFM_WRITE_REG | RFM_CONFIG); \
						databuf[1] |= 0x02; \
						send_command(databuf,2); \
						dummyloop(200)
#define reset_PWR_UP	get_RFMconfig; \
						databuf[0] = (RFM_WRITE_REG | RFM_CONFIG); \
						databuf[1] &= 0xfd; \
						send_command(databuf,2); \
						dummyloop(200)
#define transmitt		databuf[0] = RFM_WR_TX_PLOAD; \
						databuf[1] = 'L'; \
						databuf[2] = 's'; \
						send_command(databuf,3); \
						led_on; \
						CE_HIGH; \
						dummyloop(2000); \
						CE_LOW ; \
						led_off; \
						dummyloop(200)



UPD II:

Обещанные фотки:

Плата, пока еще есть несмытая канифоль и запаяны штыри для программирования. В дальнейшем это все уберу.

Сверху

Снизу

Корпус



  • +1
  • 20 ноября 2011, 23:58
  • Ultrin

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

RSS свернуть / развернуть
Мне интересно, а как ты такие тоненькие дорожки протравил?
0
С дорожками никаких проблем. Там всего пара дорожек 0.2 мм. зазоры вроде не меньше 0.3 так что травятся они элементарно. Надо еще учесть что при работе утюгом дорожка получается чуть толще оригинала, хоть у меня тонер и не плывет особо. Ну и раствор ХЖ, который я использую, не может похвастатья особой агрессивностью.
0
А зачем было кнопку подключать к МК? Что мешало просто питание через нее подавать? Как я понимаю, все равно пока кнопка не нажата — схема ничего не делает.
0
  • avatar
  • Vga
  • 21 ноября 2011, 01:08
Можно и так. Только в процессе ковыряния с RFM выяснилась одна особенность ее работы: при первом чтении регистра статуса теряется старший бит и все данные регистра идут сдвинутые. Последующие чтения любого регистра выдают вообще чудеса. Так как гонял я ее нв аппаратном SPI STM32 — мой быдлокод так накосячить не мог. Ну и нажатие кнопки будет давать всякие дребезги и давать. Можно сделать фильтры и прочее, но я посчитал что отфильтровать програмно мне проще и надежней работа всег устройства.
0
а код на чем писал?
0
на Си
0
причем место во флеше еще осталось и дымаю попробовать туда втиснуть инициализацию первого банка регистров RFM, чтоб связь по дальше была.
0
ну выкладывай код, интересно :-))
0
Это симптомы неправильно настроенного SPI или неправильно разводки платы.
Но скорей всего присутствует сразу две ошибки. На плате нет подтягивающий резисторов на SPI. Так, что будет шум. А сдвиг битов это от неправильной настройки.
0
Также все трансиверы дают хороший шум в цепь питания. От них избавляются электролитами или танталами. На плате их нет.
Тебя спасает от глюков только батарейка, но и она не лучшая замена конденсатору.
0
А зачем на SPI подтяжки? Или у модуля MISO с открытым коллектором? Даже если так, на нём есть подтяжка 33ом — 10к
0
mosi,miso,clk нужно через резюки подтягивать к питанию.
0
Я и спрашиваю — зачем? Объясните уж, если утверждаете…
0
Это можно видеть в других схемах. Да и на собственном опыте убедился в необходимости этих резюков. Также подобный совет есть в «Искусство схемотехники». Там ясен пень не об SPI идет речь.

В что там и как не вдумывался.
0
Ладно, тогда я объясню, если вы не против :)

Подтяжки необходимы на схемах с монтажным «И», т.е. когда все участники обмена имеют выходы с открытым коллектором и не способны подать на шину единицу. Например это 1-wire или I2C

Интерфейсы с жестким логическим уровнем на выходах (например UART или SPI) в подтяжках не нуждаются, т.к. там чёткое соединение — выход передатчика со входом приёмника. За исключением случаев, когда передатчик может быть отключен от приёмника физически, и не желательно, чтобы на последний сыпался мусор в виде помех.
0
Хороший шум дает угловой разъем от программатора. На большой скорости, без резюков, постоянно проскакивают битые байты.
Так, что я их ставлю всегда. Тем более они ничем не мешают.
0
Это все объясняло бы глюк на данной плате. Но как Вы объясните данный глюк на плате с STM32, у которого нормальное питание, аппаратный SPI и никаких левых разъемов программирования? На AVR я писал передатчик только после того, как обкатал все интересующие меня варианты на STM, т.к. там минимум 2 SPI и можно гонять оба модуля в любом направлении на одном контроллере…
Настройки порта менял все какие только возможны учитываю диаграммы приведенные в даташите на модуль. Тантал вкотячить в принципе не долго, только не уверен что он поможет…
0
Неправильно настроен SPI. Внимательней доку на трансивер почитай. Все симптомы на лицо.
0
Вот настройки SPI для STM32F103C8T6, на котором предварительно гонялись оба модуля:

	SPI_InitStructure.SPI_Direction = SPI_Direction_2Lines_FullDuplex;
	SPI_InitStructure.SPI_Mode = SPI_Mode_Master;
	SPI_InitStructure.SPI_DataSize = SPI_DataSize_8b;
	SPI_InitStructure.SPI_CPOL = SPI_CPOL_Low;
	SPI_InitStructure.SPI_CPHA = SPI_CPHA_1Edge;
	SPI_InitStructure.SPI_NSS =	SPI_NSS_Soft;
	SPI_InitStructure.SPI_BaudRatePrescaler	= SPI_BaudRatePrescaler_256;
	SPI_InitStructure.SPI_FirstBit = SPI_FirstBit_MSB;
	SPI_InitStructure.SPI_CRCPolynomial=7;

Посмотрите. Мож я в самом деле где затупил и чего-то неправильно настроил…
0
Что-то не вдуплил. Этой философии.
Есть же мультиплексоры, демультиплексоры, регистры. К примеру 74HC4052 и ей же подобные. Я использовал для получения многоканального юарта.

А тут только из мультиплексирования только SPI. Хотя это и не мультиплексирования. Интерфейс допускает множество датчиков.

Да и светодиод на более высокой частоте будет гореть не переставая.
0
  • avatar
  • a9d
  • 21 ноября 2011, 14:37
вот там как раз идет речь о мультиплексировании/демультиплексировании. А тут из мультиплексирования, только SPI и добавление на пины дополнительного функционала с сомнительной надежностью.
0
согласен, но вы размер платы видели? и объем флеша контроллера? Речь именно о том, как не используя дополнительные корпуса задействовать по максимуму малоногий контроллер. Все остальное к данной теме относится, но не в контексте данной задачи.
0
Так выбери другой контроллер. А трансивер замени на cc2500.
0
например?
0
т.к. добавился код — перенес в коллективный…
0
Мне кажется нет смысла так урезать себя в ресурсах. Нужна ведь минимальная площадь на плате, а не минимум ног контоллера? Реализация на STM8L в TSSOP20 была бы в разы проще.
0
за исключением того, что у меня нет пока ни STM8, ни программатора для них, ни среды разработки. И главное, что в пределах досягаемости местной радиолавки их тоже нет. Заказать их как-нибудь я конечно соберусь, но пока и так хватает чемодана АВР-ок, которые лежат — только руку протянуть…
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.