Писал программную реализацию SPI протокола для атмеги 168, для мастера , пины как в аппаратном, в протеусе работает, а в железе наотрез отказывается.
Слейв тоже 168 мега, но задействован аппаратный SPI ( режим 0).
Есть у кого какие мысли почему в железе не работает???
программный, мастер
Код: Выделить всё
/*
* GccApplication37.c
*
* Created: 03.01.2021 0:42:56
* Author: сергей
*/
//
//
#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 ( тут настройка порта на выход буит и все таккое)))
volatile uint8_t sis_taimer =0; // объявим переменную системного таймера
//-----------------------------------------------------------------------------------------------------------------------------------------------------------
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)
_delay_ms(2); // задержка 10 милисекунд
_delay_ms(2); // задержка 10 милисекунд
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; // сбросим таймер и ждем следующего тика системного таймера
}
asm("NOP"); // пустая операция
}
}
аппаратный, слейв, проверен в работе.
Код: Выделить всё
/*
* GccApplication18.c
*
* Created: 29.10.2020 16:49:11
* Author: сергей
*/
// -------------------------------------------------------------- ИЗУЧАЕМ 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"); // пустая операция
}
}


