dFi.w = F*167.77216; //частота передаваемая через SPI, какое-то фиксированное число 167.77216
PORTB &= ~_BV(PB0);
SPI_MasterTransmit(Cnt & 0x03);//0x03-маска
SPI_MasterTransmit(dFi.b[0]);//флаги передачи
SPI_MasterTransmit(dFi.b[1]);//флаги передачи
SPI_MasterTransmit(dFi.b[2]);//флаги передачи
Боюсь, что dFi.w не dFi.w, a &Fi.w (указатель, а [0], [1] ... - индекс для ячейки для double переменных.
Иначе я без побитового сдвига понятия не имею, как можно получить частоту в 3 байта, тем более со десет. знаком (F*167.77216).
А и какой double, в Вашем компиляторе для этого МК вероятно double -> float (32 bit).
Портировать код с другого МК на этот?
Последний раз редактировалось veso74 Пт июн 09, 2023 15:03:36, всего редактировалось 4 раза.
Начал менять настройки мастера, изменил прескейлер в регистре SPI SPCR с 16 до 128, при частоте CK - 7813 Гц и заработал 3 кейс.MOHCTEP был прав, осталось разобраться с как настроить частоту, чтобы работали все 4 кейса USI_OVERFLOW. Но пока что ка как бы я не настраивал частоту мастера, 4 кейс оживить не удается.
Добавлено after 2 minutes 33 seconds: veso74, это прошивка с рабочего прибора, прошивка с слейва не сохранилась и сгорела в результате КЗ.
Скинул архив, иницилизация SPI в USART.c. Слейв в данной ситуации выступает как регулируемый генератор меандра.
После некоторых изменений, таких как перенос из for функции инициилизации USI и изменения прескейлера частоты тактирующего сигнала мастера, МК в протеусе начал реагировать на измения номера ножки и частоты со стороны мастера. Теперь 1 байт(номер ножки, отправляемый со стороны мастера) определяется корректно и на заданной ножке генерируется частота, но частота не соответствует заданной и форма генерируемой частоты далека от меандра.
Я думаю, что это связано с тем что кейс 3 по прежнему не работает, также пробовал подавать 2-х байтные и 1-байтные числа по SPI со стороны мастера и все равно частота на выходе МК ATtiny2313 не корректна.
На данный момент присутствует 3 основные проблемы:
1) Неверная частота на выходе МК.
2) Неверная форма сигнала, должен быть меандр.
3) Хоть и частота генерируется на верной ножке, почему-то МК подает +5В на другие ножки, которые задействованы как выводы частоты nG, а этого быть не должно.
Вот актуальный код слейва:
Спойлер
VFG_DDR тоже настройте один раз, при инициализации. Тогда set_out_pin (nG); будет не нужна. Вместо ее вызова, в бесконечном цикле достаточно будет строки nG=1<<ch_num;
MOHCTEP, тогда при nG = 0 на мастере(0x00000000), частота генерируется на ножках PB0 и PB1.
Добавлено after 3 minutes 50 seconds:
При nG = 1(0x00000001), частота генерируется на ножке PB1, при nG = 2(0x00000002), частота генерируется на ножке PB2.
typedef union
{
unsigned long int w ; // w as WORD
unsigned int h[2]; // h as HALF-WORD
unsigned char b[4]; // b as BYTE
} Union32;
Union32 dFi;
объединение - каждый элемент (w,h,b) начинается с одной и тойже ячейки памяти,
т.е. dFi.b[0] - это младший байт из dFi.w
dFi.w = F*167.77216; - здесь неявное преобразование float->long int
(как компилятор это делает я не знаю, допустимо ли выбрасывать старший байт)
в передаче:
SPI_MasterTransmit(Cnt & 0x03); //1-й байт
SPI_MasterTransmit(dFi.b[0]); //2-байт - младший из dFi.w
SPI_MasterTransmit(dFi.b[1]); //3-байт - второй из dFi.w
SPI_MasterTransmit(dFi.b[2]); //4-байт - третий из dFi.w,
четвертый из dFi.w - проигнорирован, не передается,
т.е. целостность unsigned long int w нарушается
при приеме:
ch_num = USIDR;
dFi.b[1] = USIDR; - младший из dFi.w
dFi.b[2] = USIDR; - второй из dFi.w
dFi.b[3] = USIDR; - третий из dFi.w
т.е. в fG <b[1]><b[1]><b[0]>< 00 > (здесь b[] - из dFi.w передатчика)
а в dFi.w <b[3]><b[2]><b[1]><b[0]>
Добавлено after 11 minutes 45 seconds:
а между тем, если б передавать 5 байт (номер, float)
результат вычисления переправлялся бы целёхоньким
dFi.f = F*167.77216; - без преобразования типа
typedef union
{
float f;
unsigned long int w ; // w as WORD
unsigned int h[2]; // h as HALF-WORD
unsigned char b[4]; // b as BYTE
} Union32;
SPI_MasterTransmit(Cnt & 0x03); //1-й байт
SPI_MasterTransmit(dFi.b[0]); //2-байт - младший из dFi.f
SPI_MasterTransmit(dFi.b[1]); //3-байт - второй из dFi.f
SPI_MasterTransmit(dFi.b[2]); //4-байт - третий из dFi.f
SPI_MasterTransmit(dFi.b[3]); //5-байт - четвертый из dFi.f
и ничего не надо ручками склеивать в приемнике dFi.f = dFi.f передатчика, он же fG
a797945, а вот как этот 3 байт ловить не понятно, в прерывании программа не доходит до case 3. Вероятно отправляются 4 байта вместо 5 - для экономии памяти attiny.
Частота SCK задана минимальной на мастере(прескейлер 128/7813Гц). Не понимаю как мне подогнать Attiny под частоту тактирования мастера.
я не специалист, и тем паче не специалист по avr,
мне сложно Вам что-то советовать связанное со структурой МК или его перефирией.
если Вы предполагаете нехватку ресурсов на слейве, попробуйте
(для эксперимента) отправлять на слейв фиксированные "частоты", идея в том чтоб наладить передачу и генерацию, а математику со слейва пока убрать
эту:
for(i=0;i<=4;i++) {
TimDiv = (F_CPU/(2*(Foc/167.77216)*N)-1)+0.5;
if(TimDiv >= 0 && TimDiv<VFG_TIMER_MAX){
ClockSelect=i+1;
break;
}
}
т.е. посчитать несколько значений для таймера(ов) слейва, мастером эти значения (через паузу или по кнопке) отсылать на слейв, там принимать и без математики просто записывать в таймер(а).
Проблема довольно сильно затянулась и до сих пор не удалось отладить в железе прошивку на attiny2313, так что я решил параллельно заниматься написанием прошивки на контроллер с полноценной поддержкой SPI ATMEGA8.
Код слейва:
Спойлер
#include <mega8.h>
#include <math.h>
#include <io.h>
#include <delay.h>
#define F_CPU (8000000)
#define VFG_TIMER_MAX (65535)
#define VFG_DDR DDRD
#define VFG_PORT PORTD
volatile unsigned long int fG;
unsigned char nG;
unsigned int N[]={1,8,64,256,1024};
unsigned char flag_RT = 0;
unsigned char ch_num = 0;
// Массив корторый приходит от мастера
volatile unsigned char master_arr [3];
// Массив который отдаем от мастеру
volatile unsigned char slave_arr [3];
// счетчик входящих байт
volatile int countSPIb = -1;
void setup (void)
{
// Настройка SPI как SLAVE
DDRB |= (1 << PORTB4); // Настроить вывод MISO1 на выход
SPCR |= (1 << SPIE) | (1 << SPE) | (0 << MSTR); // Прерывание включено и сам SPI как SLAVE
}
interrupt [SPI_STC] void spi_isr(void) // Прерывание SPI1 - пришел байт
{
if (countSPIb < 0) { // пришла "пустышка"
countSPIb++; // увеличивам счетчик
SPDR = slave_arr [countSPIb]; // подгружаем нулевой байт массива ведомого
PORTC |= (1<<0);
return; // выходим из процедуры
}
master_arr [countSPIb] = SPDR1; // получаем байт от мастера
countSPIb++; // увеличиваем счетчик
SPDR = slave_arr [countSPIb]; // отдаем байт ведомого (+1 индекс)
PORTC |= (1<<1);
if (countSPIb >= sizeof(master_arr)) { // если кончился массив
PORTC |= (1<<2);
countSPIb = -1; // обнуляем счетчик и ждем следующий обмен
}
} // Конец SPI - пришел байт
//***********************************************timer1************************************************
void Tim1Init(void)
{
#asm("cli")
TCCR1A = (1<<COM1A0); //toggle on compare
TCCR1B = (1<<WGM12)|(1<<CS12)|(1<<CS10); // set timer CTC mode, prescaler 1024
TIMSK = (1<<OCIE1A);
#asm("sei")
}
void SetUpTim1A(unsigned long int Foc) //calculate value OCR1A register
{
unsigned long int TimDiv;
unsigned char ClockSelect=0;
unsigned char i=0;
for(i=0;i<=4;i++) {
TimDiv = 1*((F_CPU/(2*Foc*N[i])-1));
if(TimDiv >= 0 && TimDiv<VFG_TIMER_MAX){
ClockSelect=i+1;
break;
}
}
#asm("cli")
OCR1A=TimDiv;
TCCR1B = (1<<WGM12) | (ClockSelect<<CS10);
#asm("sei")
}
void UpdateTim1A(unsigned long int freq) //old value storage
{
static unsigned long int fG_old = 0;
if (fG_old != freq)
{
SetUpTim1A(freq);
fG_old = freq;
}
}
interrupt [TIM1_COMPA] void timer1_compa_isr(void)
{
VFG_PORT = (VFG_PORT^nG)&(nG);
}
void main(void)
{
static unsigned long int fG_old = 0;
unsigned long int tmp;
int i;
VFG_DDR = 0b00000111;
DDRC = 0b11111111;
setup();
Tim1Init();
#asm("sei")
for(;;) {
if(PINB.2==0) {countSPIb = -1;} //сброс в случае глюка связи
if (fG_old != fG) { //old value detction
SetUpTim1A(fG);
fG_old = fG;
}
for (i = 0; i < sizeof(master_arr); i++) {
nG = master_arr[i]; //generator number
fG = master_arr[i] << 8;
tmp = master_arr[i] << 16;
fG |= tmp;
tmp = master_arr[i] << 24;
fG |= tmp;
//generator frequency
UpdateTim1A(fG);
SetUpTim1A(fG);
}
}
}
Мне кажется этот код интуитивно более понятен и будет проще его отладить, хотябы в протеусе, чтобы были дополнительные варианты в случае неудачи с аттини.
Ну проблема в том, что программа записывает только первый байт информации(nG - выбор выходной ножки котроллера), а в остальные записывает нули, так что вопрос к экспертам, где я облажался?
Добавлено after 38 minutes 21 second:
Теперь работает только PC0 и на нем частота 32 Гц, хотя ничего не менял в коде.
MOHCTEP, первый полученный байт по SPI, я присваиваю значению массива master_arr[0], а это значение присваиваю переменной nG(номер выхода генератора), master_arr[1],master_arr[2],master_arr[3] я пытаюсь присвоить переменной fG(частота генератора, подставляемая в формулу расчета регистра OCR1A).
Идея такова, что каждое прерывание я записываю по байту информации в массив master_arr, а в основном цикле я пытаюсь присвоить эти значения переменным nG и fG. Ну такова была идея по крайней мере.
Добавлено after 3 minutes 21 second:
Функция for (i = 0; i < sizeof(master_arr); i++) нужна для того чтобы подставить эти значения в правильном порядке, но опят таки я не уверен что это работает правильно.
Нет. Вы циклом всегда присваиваете каждый байт массива, по очереди, обеим переменным сразу. В итоге, после отработки этого странного цикла, обеим переменным будет присвоен последний байт массива.
Уберите этот цикл и модифицируйте переменные обращаясь к массиву по числовым индексам master_arr[0],master_arr[1]... и master_arr[3](четвертый байт) вам недоступен, так как длина массивов у вас = 3 байтам.
Ну... сложно что то умное подсказать на расстоянии, да еще и для протеуса. В качестве версии: проблема возможна в том, что вы постоянно перестраиваете переменные nG и fG в главном цикле и вместе с ними таймер. Независимо от того - пришло там что-то по SPI или нет. Возможно ваш таймер, от постоянных перенастроек, просто не успевает нормально отрабатывать цикл. Вот у вас есть счетчик заполнения массива countSPIb. Можно, к примеру, в майн цикле проверять его на равенство длине массива (это значит, что пришли все 5 байт от мастера и данные подготовлены) и только в этом случае пересчитать переменные + таймер и сбросить счетчик countSPIb, подготовившись к следующему приему.
Как-то так примерно...
MOHCTEP, в протеусе мастер отправляет верные данные, в хексадермальном формате 02 5C 8F 02(первый байт номер генератора, остальные 3 значения частоты), а вот в ответ мастеру - слейв шлет 00 01 01 01 в ответ, это значит, что по какой-то причине, байты, полученные от мастера записываются в единицы. Если я правильно понимаю, как работает SPI, то при получении байта, слейв отправляет полученный байт мастеру, в неизмененном виде, а у меня не так получается. Это значит что скорее всего код в прерывании криво написан, но пока конкретно не ясно, что там не так.
Добавлено after 7 minutes 56 seconds:
А для хранения значения переменной fg, у меня есть такая функция, которая сравнивает старое и новое значение и не изменилось ли значение fG
Спойлер
void UpdateTim1A(unsigned long int freq) //old value storage
{
static unsigned long int fG_old = 0;
if (fG_old != freq)
{
SetUpTim1A(freq);
fG_old = freq;
}
}
if (fG_old != fG) { //old value detction
SetUpTim1A(fG);
fG_old = fG;
}
Добавлено after 4 minutes 34 seconds:
Еще может быть проблема в том, чт переменная fG - 32-х битная(unsigned long int), а мы записываем в нее 24 бита. Но я даже не знаю как это проверить, просто предположение.