Приветствую товарищи радиокоты). Писал программную реализацию SPI протокола для атмеги 168, для мастера , пины как в аппаратном, в протеусе работает, а в железе наотрез отказывается. Слейв тоже 168 мега, но задействован аппаратный SPI ( режим 0). Есть у кого какие мысли почему в железе не работает???
#include <avr/io.h> #include <avr/interrupt.h> #define F_CPU 8000000UL #include <util/delay.h> // Подключаем библиотеку функций задержки ( для использования функций задержки например _delay_ms(10); // задержка 10 милисекунд
//---------- ПОРТ (SPI), НАСТРОЙКА ВХОДОВ И ВЫХОДОВ ----------
#define DDR_SPI DDRB // обзовем порт (DDRB) удобным именем (DDR_SPI) #define DD_MOSI DDB3 // обзовем пин (DDB3) удобным именем (DD_MOSI) #define DD_SCK DDB5 // обзовем пин (DDB5) удобным именем (DD_SCK) #define DD_SS DDB2 // обзовем пин (DDB2) удобным именем (DD_SS) #define DD_MISO DDB4 // обзовем пин (DDB4) удобным именем (MISO_SS)
//---------- (SPI), НАСТРОЙКА РАБОЧИХ ПИНОВ ----------
#define PORT_SPI PORTB // обзовем порт (PORTB) удобным именем (PORT_SPI) #define PB_SS PORTB2 // обзовем пин (PB2) удобным именем (PB_SS) #define PB_SCK PORTB5 // обзовем пин (PB5) удобным именем (PB_SCK) #define PB_MOSI PORTB3 // обзовем пин (PB3) удобным именем (PB_MOSI) #define PB_MISO PORTB4 // обзовем пин (PB4) удобным именем (PB_MISO)
#define PIN_SPI PINB // PINB обзавем PIN_SPI ( тут буит проверка бита и все таккое))) #define PIN_MISO PINB4 // обзовем пин (PINB4) удобным именем (PIN_MISO)
//---------- МАКРОС ВКЛЮЧЕНИЯ И ОТКЛЮЧЕНИЯ ВЫВОДА (SS) МОДУЛЯ (SPI) ----------
#define macro_START_SLAVE() PORT_SPI &= ~(1 << PB_SS) // старт ведомому МК ( активизируем ведомый МК на прием и передачу данных сбросив в 0 пин и линию (SS)) #define macro_STOP_SLAVE() PORT_SPI |= (1 << PB_SS) // стоп ведомому МК ( дезактивизируем ведомый МК отключим прием и передачу данных установив в 1 пин и линию (SS)) //----------
#define PORT_LED PORTD // PORTD обзавем PORT_LED ( тут индикация будет и все такое)))) #define DDR_LED DDRD // DDRD обзавем DDR_LED ( тут настройка порта на выход буит и все таккое)))
//---------- void TIMER0_init(void) // инициализация и настройка модуля таймера Т0 { TIMSK0 = 0b00000001; // бит РВ0(TOIE0) устанавливаем в 1, разрешения прерывания по переполнению таймера /счетчика Т0 TCCR0A = 0b00000000; // биты (WGM02,WGM01,WGM00) сбрасываем в 0, настройка режима Normal TCCR0B = 0b00000100; // настраиваем предделитель на 256 (биты CS02=1,CS01=0,CS00=0),бит В3(WGM02) сбрасываем в 0,режим Normal GTCCR = 0b00000001; // бит РВ0(PSRSYNC) устанавливаем в 1, Сброс предделителя TO (сбрасываем пред. в конце настроек) } //---------- void SPI_MasterInit(void) // инициализация и настройка модуля SPI { DDR_SPI |= (1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS); // MOSI, SCK и SS установим на выход так как у нас МК в режиме МАСТЕР // MISO на вход , по умолчанию инициализируется нулем } //---------- ISR(TIMER0_OVF_vect) // прерывание по переполнению таймера { sis_taimer ++; // инкрементируем наш системный таймер }
//---------- uint8_t SPI_send_and_receive_bytes (uint8_t data) // функция программного SPI. входные параметры байт для передачи (data), полученый байт возвращаем в вызывающюю функцию { uint8_t a = 7; // переменая (a) номер бита байта (data) вначеле цикла он равен 7 ( с 7 старшего бита начнем передачу) uint8_t b = 7; // переменая (b) номер бита байта (out_bytes) в нее будем читать полученый бит, он равен 7 ( с 7 старшего бита принимаем биты от слейва) uint8_t out_bytes = 0; // создадим переменную (out_bytes), через нее будем возвращать принятый байт в вызывающюю функцию // режим 0, CPOL=0 сигнал синхронизации начинается с низкого уровня, CPHA=0 чтение битов по переднему фронту
macro_START_SLAVE(); // старт ведомому МК ( активизируем ведомый МК на прием и передачу данных сбросив в 0 пин и линию (SS))
for (uint8_t i = 0; i < 8; i++) // цикл "для", повторяющийся 8 раз {
_delay_ms(2); // задержка 10 милисекунд
if (data & (1 << a)) // проверяем бит (a) переменной (data), если этот бит = 1, то выполняется КОД 1. (выражение в скобках) { PORT_SPI |= (1<<PB_MOSI); // устанавливаем пин (PB_MOSI) в 1. то есть у нас в переменной (data) в этом бите 1 } else // иначе если (a) бит = 0 , то выполняем КОД 2. { PORT_SPI &= ~(1 << PB_MOSI); // сбрасываем пин (PB_MOSI) в 0. то есть у нас в переменной (data) в этом бите 0 } a-- ; // декрементируем номер бита (a) байта (data) _delay_ms(2); // задержка 10 милисекунд
PORT_SPI |= (1<<PB_SCK); // пин (SCK) установим в 1, нарастающий фронт)
asm("NOP"); // пустая операция
if (PIN_SPI & (1 << PIN_MISO)) // проверяем бит (PIN_SPI) порта (PIN_SPI), если этот бит = 1, то выполняется КОД 1. (выражение в скобках) { out_bytes |= (1<<b); // устанавливаем бит (b) байта (out_bytes) в 1. } else // иначе если бит (PB_MISO) = 0 , то выполняем КОД 2. { out_bytes &= ~(1 << b); // сбрасываем пин (b) байта (out_bytes) в 0. } b-- ; // декрементируем номер бита (b) байта (out_bytes)
PORT_SPI &= ~(1<<PB_SCK); // пин (SCK) сбросим в 0,( начался период такта синхронизации. ) } asm("NOP"); // пустая операция macro_STOP_SLAVE(); // стоп ведомому МК return (out_bytes); // возвращаем значение полученого байта от слейва в вызывающюю функцию
} //---------- int main(void) { DDR_LED = 0b11111111; // на выход ( на нем наши индикаторные светодиодики))) TIMER0_init (); // инициализация и настройка модуля таймера Т0 SPI_MasterInit (); // инициализация и настройка модуля SPI volatile uint8_t XXX; // в эту переменную будет возвращатся значение из функции (SPI_send_and_receive_bytes) PORT_SPI &= ~(1<<PB_SCK); // пин (SCK) сбросим в 0,( режим ожидания ) sei(); // Разрешить прерывания
while (1) // Основной цикл программы {
if (sis_taimer ==10) // системный таймер { XXX = SPI_send_and_receive_bytes (0b10101010); // присваиваем переменной (XXX) возвращаемое значение функции (SPI_send_and_receive_bytes) asm("NOP"); // пустая операция
PORT_LED = XXX; // принятый байт выведем в порт индикации
sis_taimer =0; // сбросим таймер и ждем следующего тика системного таймера }
// ---------- ИЗУЧАЕМ SPI ---------- ПРЕРЫВАНИЕ ПО SPI // ПРОГА для прошивки мега168р в режиме SLAVE---------------------------------------------------------ПРОЕКТ 5-------------------------- // отправляем мастеру бегущий огонек по команде мастера. // // передаем мастеру массив побайтно и по кругу // #include <avr/io.h> #include <avr/interrupt.h> #define F_CPU 1000000UL
//---------- НАСТРОЙКА ПОРТА (SPI) ----------
#define DDR_SPI DDRB // обзовем порт (DDRB) удобным именем (DDR_SPI) #define DD_MISO DDB4 // обзовем пин (DDB4) удобным именем (MISO_SS) //----------
#define PORT_LED PORTD // PORTD обзавем PORT_LED ( тут индикация будет и все такое)))) #define DDR_LED DDRD // DDRD обзавем DDR_LED ( тут настройка порта на выход буит и все таккое)))
//---------- uint8_t go [8] = // объявим массив (go) на 8 элементов иинициализируем каждый элемент числом { 0b00000001, // загоратся будет пин (PD0) 0b00000010, // загоратся будет пин (PD1) 0b00000100, // загоратся будет пин (PD2) 0b00001000, // загоратся будет пин (PD3) 0b00010000, // загоратся будет пин (PD4) 0b00100000, // загоратся будет пин (PD5) 0b01000000, // загоратся будет пин (PD6) 0b10000000, // загоратся будет пин (PD7) }; //---------- void SPI_SlaveInit(void) // инициализация и настройка модуля SPI МК в режиме (Slave) { DDR_SPI |= (1<<DD_MISO); // MISO установим на выход так как у нас МК в режиме SLAVE // MOSI, SCK и SS на вход , по умолчанию инициализируется нулем
SPCR |= (1<<SPE)|(1<<SPR0)|(1<<SPR1)|(1<<SPIE); // установим бит (SPE) в 1 включим модуль SPI, бит (MSTR) сброшен в 0 включим режим (Slave), // (SPR0) и (SPR1) установим в 1 режим скорости fclk/128, (DORD) сброшен в 0 и первым // передается старший (седьмой) бит, (CPOL) сброшен в 0 и на линии SCK во время ожидания установлен низкий логический } // уровень,(CPHA) сброшен в 0 и данные считываются по нарастающему фронту сигнала,(SPIE) в 1 Разрешение прерывания от SPI //---------- uint8_t running_light(void) // функция передает в вызывающюю функцию бегущий огонек (при каждом вызове в переданом байте еденица смещается на разряд влево) { uint8_t data = 0; // создаем и задаем значение переменной (data), static uint8_t z=0; // статическая переменная (z) , она у нас будет порядковым номером массива (go) data = go [z]; // отправляем в переменную (data) элемент массива (go) с порядковым номером (z) z++; // инкрементируем переменную (z) чтобы в следующей интерации в переменную (data) поступил следующий по счету элемент массива if (z>7) z=0; // защита от переполнения массива (go) return (data); // передаем в вызывающюю функцию наш байт } //---------- ISR(SPI_STC_vect) // прерывание по завершению передачи байта { PORT_LED = SPDR; // читаем (SPDR) и выводим полученый байт в порт (PORT_LED) SPDR = running_light(); // запишем в дата регистр (SPDR) байт полученый из функции (running_light) } //----------
int main(void) { DDR_LED = 0b11111111; // на выход ( на нем наши индикаторные светодиодики))) SPI_SlaveInit (); // инициализация и настройка модуля SPI МК в режиме (Slave) SPDR = 0b11110000; // запишем в регистр данных (SPDR) число 0, его считает мастер при первом обмене байтами по SPI sei(); // Разрешить прерывания
while (1) // Основной цикл программы { asm("NOP"); // пустая операция } }
в железе на мастере загораются пины 4-7 ( то есть мастер получил один байт от слейва и дальше не может почему то получать)
_________________ глаза боятся, а руки что то не делают))
Когда коту делать нечего, он программный SPI реализует!.. ЗАЧЕМ? АВРка — то еще дерьмо, какой, нафиг, программный SPI? Вы еще CAN программный запилите. И USB-HS программный…
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ очень важен контроль процесса заряда и разряда для избегания воздействия внешнего зарядного напряжения после достижения 100% заряда. Инженеры КОМПЭЛ подготовили список таких решений от разных производителей.
sergo80zxc, программный UART, I2C, SPI и т.п. — это просто маразм чистой воды! Если нет аппаратного, значит, микроконтроллер выбран неправильно!!!
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
Компания EVE выпустила новый аккумулятор серии PLM, сочетающий в себе высокую безопасность, длительный срок службы, широкий температурный диапазон и высокую токоотдачу даже при отрицательной температуре.
Эти аккумуляторы поддерживают заряд при температуре от -40/-20°С (сниженным значением тока), безопасны (не воспламеняются и не взрываются) при механическом повреждении (протыкание и сдавливание), устойчивы к вибрации. Они могут применяться как для автотранспорта (трекеры, маячки, сигнализация), так и для промышленных устройств мониторинга, IoT-устройств.
sergo80zxc, только в экстраординарных, когда "очень надо" написать прошивку для готовой железяки, а проектировал ее идиот… Но на мой взгляд, в данном случае быстрей будет спроектировать с нуля новую железяку правильно. Да и по деньгам выгодней будет: дороже почасовка выйдет.
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
так то понимаю физику процесса, вроде ничего космического тут нет в программном исполнении... после спада сигнала скк ждем полпериода его нулевого значения, затем пишем отправляемый бит в моси, ждем еще полпериода нулевого значения и выставляем скк,тут же читаем присланный бит с мисо и ждем уже весь период единичного значения, затем сбрасываем скк в ноль - это один цикл приемопередачи, их 8 , вот и вся функция приемопередачи
но в железе не работает,может где какая глупая ошибка?
_________________ глаза боятся, а руки что то не делают))
Много чего приходилось читать, но такой бред впервые.
Бред — это брать МК без аппаратного SPI/I2C/etc, а потом мучить его софтовым. Маразм крепчал и крыша отъезжала!..
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
но в железе не работает,может где какая глупая ошибка?
Если не работает, значит есть ошибка… если по простому - на пальцах, то ждать там некого… Спецификация SPI имеет 4 режима передачи данных… если рассматривать первый (обработка данных производится по переднему фронту импульсов сигнала SCK), то когда на линии SCK лог 0, оба МК формируют на своих выходах очередной бит. А при SCK лог 1 читают входа, сохраняя очередной принятый бит в своих регистрах. Вот собственно и всё. Если подробней описать один из вариантов этого процесса передачи данных мастером, то выглядит это так: Назовём один из рабочих регистров Loop (петля) – это регистр определяющий количество передаваемых бит. Следующий регистр назовём Data_SPI (данные SPI) – в этот регистр будет загружаться байт для передачи и в него же будут записываться биты при приёме. Теперь по пунктам: 1. На выходе SS формируем лог 0 – что указывает ведомому о готовности к передаче данных по шине. 2. В регистр Loop записываем число 8. 3. Устанавливаем лог 0 на выводе MOSI. 4. Сдвигаем влево данные регистра Data_SPI. Это означает, что передаём старшим битом вперёд. 5. Проверяем состояние бита C в регистре SREG. Если бит С равен лог 0, то переходим к пункту 7. Если бит С равен лог 1, то переходим к пункту 6. 6. Устанавливаем лог 1 на выводе MOSI. 7. Устанавливаем лог 1 на выводе SCK. 8. Затем проверяем вход MISO. Если на нём лог 0, то переходим к пункту 10. Если лог 1, то переходим на пункт 9. 9. Прибавляем к регистру Data_SPI единицу. Не записываем в него 1, а именно прибавляем. 10. Устанавливаем лог 0 на выводе SCK. 11. Отнимаем единицу из регистра Loop с проверкой нуля. Если не ноль, то переходим в пункт 3. Если ноль, то к пункту 12. 12. Устанавливаем лог 1 на выводе SS. Передача байта по SPI завершена. И можно забрать при необходимости из регистра Data_SPI данные переданные ведомым устройством.
Карма: 90
Рейтинг сообщений: 1289
Зарегистрирован: Чт мар 18, 2010 23:09:57 Сообщений: 4510 Откуда: Планета Земля
Рейтинг сообщения:3 Медали: 1
Eddy_Em, завязывайте тут слюнями брызгать. Идите проспитесь.
Цитата:
хотелось бы понять в моем коде что не так
Вам же всё расписали по полочкам, сравнивайте
Добавлено after 17 minutes 39 seconds: Сейчас вбил в поисковик "SPI интерфейс" и, по мимо множества статей, наткнулся даже на видео https://yandex.ru/efir?stream_id=vB1hVVu6t_QE. А Вы пробовали это делать ?
Аlex, хватит уже маразмом полыхать! Если уж так хочется BDSM, так сделайте его по-человечески: таймер генерирует импульсы CLK на одной ноге, а двумя другими работает с MOSI и MISO! Все реализуется через DMA "почти аппаратно". Но все равно это — идиотизм чистой воды! Есть аппаратный SPI — так и используйте его. Нет — берите МК, где он есть!!!
Я предупреждал. Споры с Вами завязывать не буду. Отдохните недельку. Ещё раз повторите свои выпады - получите вечный бан. Alex.
_________________ Linux rules! Windows must die. Здравомыслящий человек добровольно будет пользоваться мастдаем лишь в двух случаях: под дулом автомата или под влиянием анального зонда. Я на гитхабе, в ЖЖ
sergo80zxc я ваш код проверил на аппаратуре STM32 c VS1063.
Передача сигналов верная и соответствует тому что у меня выдает аппаратный SPI, но код упорно продолжал не работать. Тогда я просто убрал все задержки из кода и код заработал. Макросы старт и стоп не использовал, свое дерганье.
Передача работает правильно. Прием байта не проверял так как у меня прием не использовался.
_________________ Инженер R@D
Telegram чат: https://t.me/radiowolf или в поиске приложения @radiowolf. Личка:@cncoxford
Oxford, да, согласен, в слейве ради приличия скорость настраивается)) попробовал делаи убрать опять в железе не работает, только первый байт от слейва приходит и все, буду копать..)
_________________ глаза боятся, а руки что то не делают))
Сейчас этот форум просматривают: Varlakotam и гости: 43
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения