|
Внешние прерывания, STM32F103VB |
|
|
|
Jul 27 2014, 06:35
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Я пытаюсь включить внешние прерывания в свою программу на STM32F103VB. CODE void ExtIntInit(void) { // enable clock for Alternate Function RCC->APB2ENR |= RCC_APB2ENR_AFIOEN;
//BUT - PE0 GPIOE->CRL &= ~GPIO_CRL_MODE0; //Configure as input GPIOE->CRL &= ~GPIO_CRL_CNF0; GPIOE->CRL |= GPIO_CRL_CNF0_1; //input with pull-up/pull-down resistor GPIOE->BSRR |= GPIO_BSRR_BS0; //set pull-up //ENC1 - PE1 GPIOE->CRL &= ~GPIO_CRL_MODE1; //Configure as input GPIOE->CRL &= ~GPIO_CRL_CNF1; GPIOE->CRL |= GPIO_CRL_CNF1_1; //input with pull-up/pull-down resistor GPIOE->BSRR |= GPIO_BSRR_BS1; //set pull-up //ENC2 - PE2 GPIOE->CRL &= ~GPIO_CRL_MODE2; //Configure as input GPIOE->CRL &= ~GPIO_CRL_CNF2; GPIOE->CRL |= GPIO_CRL_CNF2_1; //input with pull-up/pull-down resistor GPIOE->BSRR |= GPIO_BSRR_BS2; //set pull-up
//interrupt source PE AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI0_PE; //PE0 - BUT AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI1_PE; //PE1 - ENC1 AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI2_PE; //PE2 - ENC2 AFIO->EXTICR[0] |= AFIO_EXTICR1_EXTI3_PE; //PE3 - ENC3
//interrupt edge EXTI->RTSR |= 0; //EXTI_RTSR_TR0; //rising EXTI->FTSR |= EXTI_FTSR_TR0; //falling EXTI->FTSR |= EXTI_FTSR_TR1; //falling EXTI->FTSR |= EXTI_FTSR_TR2; //falling EXTI->FTSR |= EXTI_FTSR_TR3; //falling
//enable ext interrupt EXTI->IMR |= EXTI_IMR_MR0; EXTI->IMR |= EXTI_IMR_MR1; EXTI->IMR |= EXTI_IMR_MR2; EXTI->IMR |= EXTI_IMR_MR3;
NVIC_EnableIRQ(EXTI0_IRQn); NVIC_EnableIRQ(EXTI1_IRQn); NVIC_EnableIRQ(EXTI2_IRQn); NVIC_EnableIRQ(EXTI3_IRQn); }
И обработчик CODE void EXTI0_IRQHandler(void) //button pressed { EXTI->PR = EXTI_PR_PR0; //clear flag??? TIM4->CNT = 0; TIM4->CR1 |= TIM_CR1_CEN; //but_int = true; //NVIC_EnableIRQ(TIM7_IRQn);
} void EXTI1_IRQHandler(void) //encoder1 { EXTI->PR = EXTI_PR_PR1; //clear flag??? enc1_int = 1; if(DIR1) { mot1_position++; } else { if(mot1_position > 0) { mot1_position--; } } }
void EXTI2_IRQHandler(void) //encoder2 { EXTI->PR = EXTI_PR_PR2; //clear flag??? if(DIR2) { mot2_position++; } else { if(mot2_position > 0) { mot2_position--; } } }
void EXTI3_IRQHandler(void) //encoder3 {
EXTI->PR = EXTI_PR_PR3; //clear flag??? if(DIR3) { mot3_position++; } else { if(mot3_position > 0) { mot3_position--; } } }
До этого кода все работает нормально. После включения этого куска кода камень умирает и не реагирует ни на что. Я уже пару дней бьюсь над проблемой. Читаю документацию вдоль и поперек не могу понять где проблема.
|
|
|
|
|
Jul 27 2014, 12:33
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
Цитата(Jenya7 @ Jul 27 2014, 13:16)  Я так понимаю что EXTI->PR = EXTI_PR_PR0; сбрасывает запрос. Спасибо за код. EXTI->PR = EXTI_PR_PR0; - как-то странно выглядит. Вот пример обработчика. Может поможет чем. Код void EXTI0_IRQHandler (void) { EXTI->PR |= (1UL<<0); // Сбросить флаг EXTI0
}
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
|
Jul 27 2014, 14:09
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(smk @ Jul 27 2014, 18:04)  Но ведь не сбрасывает же. Может и лишнего сбросить. Цитата EXTI->PR = EXTI_PR_PR0; - годная конструкция. Из кода не понятна судьба ноги PE3 - ее, вроде, не инициализоровали?
|
|
|
|
|
Jul 27 2014, 14:39
|
Гуру
     
Группа: Свой
Сообщений: 2 246
Регистрация: 17-03-05
Из: Украина, Киев
Пользователь №: 3 446

|
Цитата(adnega @ Jul 27 2014, 17:09)  Может и лишнего сбросить.
- годная конструкция. Из кода не понятна судьба ноги PE3 - ее, вроде, не инициализоровали? Код #define EXTI_PR_PR0 ((uint32_t)0x00000001) /*!< Pending bit for line 0 */ Тогда уже Код EXTI->PR |=EXTI_PR_PR0; Хотя конечно если принять во внимание что сброс записью единицы...
--------------------
Живи днем так, чтобы ночью ты спал спокойно.
|
|
|
|
|
Jul 27 2014, 16:00
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Цитата(smk @ Jul 27 2014, 20:39)  Код #define EXTI_PR_PR0 ((uint32_t)0x00000001) /*!< Pending bit for line 0 */ Тогда уже Код EXTI->PR |=EXTI_PR_PR0; Хотя конечно если принять во внимание что сброс записью единицы... Вставив метку я обнаружил что камень не выходит из прерывания, причем что так Код EXTI->PR =EXTI_PR_PR0 что так Код EXTI->PR |=EXTI_PR_PR0 ничего не помогает По моему я нашел в чем проблема. У меня на этих ногах сидит синус частотой 41.5 Мгц. Буду разбираться как он там появился.
|
|
|
|
|
Jul 27 2014, 16:45
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата что так что так В таком случае предлагаю попробовать еще один "рабочий" вариант EXTI->PR |= 0 И оставить в итоге EXTI->PR = EXTI_PR_PR0, когда синус с этих ног уйдет. Во всем остальном соглашусь с smk - код очень странный из-за смеси SPL и доступа к регистрам. Куда читабельнее записать просто EXTI->PR = (1 << 0) PS. В cortex-m хорошо, что прерывания защелкиваются в противном случае потери из-за "|=" были бы гарантированы. PS2. По хорошему еще нужно проверять есть ли условие Код void EXTI9_5_IRQHandler(void) { cnt1++; if(EXTI->PR & (1 << 6)) { cnt2++; EXTI->PR = (1 << 6); } } Без этого никак при консолидированных обработчиках сразу от нескольких источников. И в некоторых случаях возможно повторное вхождение в прерывание - данную тему затрагивали на форуме, и некоторые участники, можно сказать, ощутили это явление на "собственной шкуре" (я в их числе). Например, попробуйте на высоких уровнях оптимизации очищать флаг в самом конце прерывания без предварительной проверки условия. Есть мнение, что cnt1 и cnt2 будут сильно отличаться даже при единственном источнике EXTI6...
|
|
|
|
|
Jul 27 2014, 19:20
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
Цитата(adnega @ Jul 27 2014, 19:45)  Например, попробуйте на высоких уровнях оптимизации очищать флаг в самом конце прерывания без предварительной проверки условия. Есть мнение, что cnt1 и cnt2 будут сильно отличаться даже при единственном источнике EXTI6... Чтобы этого не происходило надо добавить после сброса флага инструкцию DSB().
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jul 27 2014, 19:22
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
Цитата PS. В cortex-m хорошо, что прерывания защелкиваются в противном случае потери из-за "|=" были бы гарантированы. Можно чуть подробнее что есть защелкиваются? Потери от |= появляются в следствие того, что если в самом регистре уже стоят еще какие то флаги, то вы им их сбросите. Это старая и разжеванная тема почему прерывания надо сбрасывать через равно, а не через |= &= (для сбросов нулем) Цитата Есть мнение, что cnt1 и cnt2 будут сильно отличаться даже при единственном источнике EXTI6... ЧуднО это как-то, может это связано с обработкой 6 прерывания в функции с именем 5? Ну то есть есть еще какое-то объяснение? А то как так может быть что вы попадаете в прерывание от единственного источника, при этом флага этого прерывания не стоит? Ай... ну вот Сергей уже и ответил
|
|
|
|
|
Jul 28 2014, 13:13
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(Jenya7 @ Jul 28 2014, 14:16)  Я смотрел ремап, таймера сидят вместе с другими важными модулями. Жаль. Значит энкодеру не повезло - будет самым "глючным", т.к. все ушло важным модулям. С одной стороны можно не обращать внимание - вряд ли энкодер будут крутить часто. С другой стороны - это средство взаимодействия с пользователем и если энкодер будет работать неустойчиво - многим это не понравится. Например, купил я оциллогаф SDS7102, и со старой прошивкой его энкодеры просто сносили башню: крутишь в одну сторону, а реагирует не так как ожидалось. В новой проше поправили. Сейчас отлично все работает, плюс ускорение приделали - очень удобно)) Давным-давно делал энкодер на atmega8 опросом (t=10ms) - до сих пор все работает без замечаний (а это турникеты KABA на оживленной проходной). Если скорось большая не нужна, то лучше делать опросом, чтобы не повторилась ситуация с мегагерцами (мало ли что там может появиться). Удачи.
|
|
|
|
|
Jul 28 2014, 13:54
|
Знающий
   
Группа: Участник
Сообщений: 643
Регистрация: 29-05-09
Из: Германия
Пользователь №: 49 725

|
Цитата(Jenya7 @ Jul 28 2014, 07:50)  Но все не напрасно, благодаря этому я узнал как правильно чистить интерапт. Так что спасибо вам друзья.  Кстати, о чистке. У STM есть определенный "разброс и шатание" в части, как сбрасывать биты в регистрах флагов прерываний и статуса: в некоторых случаях для сброса битов регистра надо записать в другой, связаный, регистр единицы для сброса, а в некоторых случаях - нули в сам регистр, биты которого надо сбросить. То есть, для первого случая это конструкция REG = BIT; как это для EXT, а во втором случае REG = ~BIT; Поэтому надо постоянно обращать внимание на особенности регистра. Например, в доке на какой-нибудь SR (регистр статуса) при описании бита может стоять rc1_w0, что означает, что бит сбрасывается записью в него нуля. Вроде логично, но применять надо не SR &= ~BIT, а именно SR = ~BIT. Из этого следует, что удобно применить SR = ~SR, если надо одним махом сбросить все установленные биты в SR, не заморачиваясь на константы в правой части выражения  .
|
|
|
|
|
Jul 28 2014, 14:12
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Я тут посмотрел...возможно я таки найду свободные пины для таймера. А насчет чистки битов, может как в AVR? Код #define SETBIT(ADDRESS,BIT) (ADDRESS |= (1<<BIT)) #define CLEARBIT(ADDRESS,BIT) (ADDRESS &= ~(1<<BIT)) Кстати я не вижу большого криминала обрабатывать энкодер на прерываниях. На AVR прекрасно работало, а STM32 все таки в 5 раз быстрее.
Сообщение отредактировал Jenya7 - Jul 28 2014, 14:08
|
|
|
|
|
Jul 28 2014, 15:52
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
Опять нужна лекция... попробуем укороченный вариант
возникает прерывание 1 и 2, ставиться флаг 1 и 2 биты. FLAG_REG = 0x03; вы входите в обработчик первого прерывания в нем пишите FLAG_REG |= 0x01; для сброса этого флага, получаете FLAG_REG = FLAG_REG | 0x01 = 0x03 | 0x01 = 0x03 то есть сбрасываете оба флага, теряете второе прерывание
теперь для сброса нулем возникает прерывание 1 и 2, ставиться флаг 1 и 2 биты. FLAG_REG = 0x03; вы входите в обработчик первого прерывания в нем пишите FLAG_REG &= ~0x01; это на самом деле выглядит так 1. TEMP = FLAG_REG; 2. TEMP = TEMP & 0xFE = 0x03 & 0xFE = 0x02; 3. FLAG_REG = TEMP = 0x02;
казалось бы вот оно счастье все вышло,
но что если между 1 и 2, или 2 и 3 возникло еще прерывания и FLAG_REG стал 0x07, что в нем останется после 3? правильно, потеря третьего прерывания...
именно по этому флаги прерываний в подавляющем большинстве делают чувствительными только к одному уровню сигнала, и сбрасывают их через =, то есть через 1 тактовую операцию...
|
|
|
|
|
Jul 29 2014, 06:15
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Golikov A. Спасибо за подробное объяснение. Цитата(adnega @ Jul 28 2014, 22:27)  Вы же сами жаловались, что ничего не работает! Когда CPU постоянно сидит в обработчиках прерываний mainloop не крутится и не работают менее приоритетные (и этого же уровня) прерывания - что есть зло. С учетом того, что в энкодере будет дребезг - в это время ничего работать не будет. А начнет пользователь крутить быстро - так вообще много чудес можно словить в устройстве, с таким количеством "важной" периферии, что не остается свободных ног таймера. Тут конечно все дело в частоте прерываний. У иеня максимальная 200 Герц значит 5 ms. Дьюти 50% - 2.5 ms между перепадами. Это достаточно большое время как мне кажется.
|
|
|
|
|
Jul 29 2014, 08:30
|
Гуру
     
Группа: Свой
Сообщений: 2 724
Регистрация: 14-05-07
Из: Ярославль, Россия
Пользователь №: 27 702

|
Цитата(Jenya7 @ Jul 29 2014, 10:15)  Тут конечно все дело в частоте прерываний. У иеня максимальная 200 Герц значит 5 ms. Дьюти 50% - 2.5 ms между перепадами. Это достаточно большое время как мне кажется. Да уж - ни туда, ни сюда. Возможно, в этом случае опрос может проиграть EXTI. Я бы все равно постарался подключить к таймеру. Может, МК другой посмотреть: в F100 таймеров обычно больше (правда частота CPU до 24МГц).
|
|
|
|
|
Jul 29 2014, 19:02
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
ага, где то полдня работы  ... у нас 5 моторами рулит ПЛИС и АРМ, АРМ - езернет со стэком и всей фигней, по 2 SSP (аналог SPI) он общается с ПЛИС. В ПЛИС автоматы которые все делают, на протоколе даже контрольная сумма есть... SPI много легче UART в случае плис, а протокол может быть простейшим, АДРЕС, НАПРАВЛЕНИЕ (чтение-запись), ДАННЫЕ (фиксированной длинны 32 бита), контрольная сумма...
|
|
|
|
|
Jul 30 2014, 05:20
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Цитата(Golikov A. @ Jul 30 2014, 01:02)  ага, где то полдня работы  ... у нас 5 моторами рулит ПЛИС и АРМ, АРМ - езернет со стэком и всей фигней, по 2 SSP (аналог SPI) он общается с ПЛИС. В ПЛИС автоматы которые все делают, на протоколе даже контрольная сумма есть... SPI много легче UART в случае плис, а протокол может быть простейшим, АДРЕС, НАПРАВЛЕНИЕ (чтение-запись), ДАННЫЕ (фиксированной длинны 32 бита), контрольная сумма... Может подгоните имплементацию парсера комманд на ПЛИС?  Цитата(adnega @ Jul 30 2014, 01:23)  А если вместо ПЛИС поставить мелкоту, типа STM32F0xx? Там и таймеров - что ног не хватит. И трехфазные комплиментарные выходы с аппаратным deadtime. И возможность энкодер подключить. Плюс всякие аналоговые компараторы для защит. Это кстати очень даже рабочее решение. У меня были проекты с двумя контроллерами, так сказать parallel tasking.
|
|
|
|
|
Jul 30 2014, 07:03
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
да фигня... общий принцип перевести все входы под единый клок внутри ПЛИС и забыть об этом. CODE `timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: *** // Engineer: *** // // Create Date: *** // Design Name: // Module Name: SPIComModule // Project Name: // Target Devices: // Tool versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // // "spi_cs" and "data_rdy" signal must be in // stable state before any "spi_clk" actions // min 2 "clk" for "data_rdy" // and 2 "clk" for "spi_cs" // ////////////////////////////////////////////////////////////////////////////////// module SPIComModule_v4_00( clk, //основной клок spi_cs, //сигнал выбора SPI spi_clk, //SPI клок данных spi_in, //входные данные SPI spi_out, //выходные данные SPI module_sel, //выбор модуля submodule_sel, //выбор подмодуля data_to_module, //данные для модуля data_from_module, //данные от модуля wr_rd_sync, //сигнал синхронизации чтения - записи wr_strb, //строб записи wr_ack, //подтверждение записи rd_strb, //строб чтения rd_ack, //подтверждение чтения check_sum_ok, //сигнал подтверждения правильной контрольной суммы data_write_done, //сигнал подтверждения записи данных data_rdy //сигнал готовности данных для чтения );
//================================================================================ == parameter MODULE_DATA_SIZE = 32; //размер данных модуля parameter MODULE_ADDR_SIZE = 4; //размер поля адреса parameter MODULE_ADDR_VECTOR_SIZE = (32'b01 << MODULE_ADDR_SIZE); //размер вектора выбора модуля (4 бита адрес) parameter SUB_ADDR_SIZE = 3; //размер поля подадреса parameter SUB_ADDR_VECTOR_SIZE = (32'b01 << SUB_ADDR_SIZE); //размер вектора выбора подмодуля (3 бита подадрес)
parameter READ_DIRRECTION_VAL = 1; //значение бита при котором идет чтение parameter ADDR_FIELD_SIZE = 8; //8 бит поле адреса parameter CHECKSUM_FIELD_SIZE = 8; //8 бит поле контрольной суммы localparam DATA_FIELD_SIZE = MODULE_DATA_SIZE; //размер поля данных //размер регистра расчета длинны сообщения для определения //окончания приема parameter MES_LEN_COUNTER_SIZE = 6; //размер регистра расчета длинны слова контрольной суммы //для определения окончания приема очередного слова parameter CHECKSUM_LEN_COUNTER_SIZE = 3; //контрольная сумма по всем сообщению должна дать 0 //чтобы полностью нулевое сообщение не прошло как верное //начинаем считать сумму с этого значения parameter START_CHECKSUM_VAL = 'hA5; //================================================================================ == input clk; input spi_cs; input spi_clk; input spi_in; output spi_out; output reg [MODULE_ADDR_VECTOR_SIZE - 1 : 0] module_sel = 0; output reg [SUB_ADDR_VECTOR_SIZE - 1 : 0] submodule_sel = 0; output reg [MODULE_DATA_SIZE - 1 : 0] data_to_module = 0; input [MODULE_DATA_SIZE - 1 : 0] data_from_module; //этот сигнал должен быть лишен мета-стабильности //во внешних схемах input wr_rd_sync; output wr_strb; input wr_ack; output rd_strb; input rd_ack; output reg check_sum_ok = 0; output reg data_write_done = 0; output reg data_rdy = 0; //================================================================================ == //регистры для задания состояние строба чтения и записи //эти регистры задает схема, а выходное значение получается //с учетом сигнала синхронизации reg wr_strb_reg = 0; reg rd_strb_reg = 0; //если разрешена запись или чтение, выдаем наружу стробы assign wr_strb = (wr_strb_reg & wr_rd_sync); assign rd_strb = (rd_strb_reg & wr_rd_sync); //для синхронизации 2 клоковых доменов //основного и SPI используем систему семафоров //задание и подтверждение задания reg need_start_recive = 0; //необходимо начать прием с 1 клоком SPI reg need_start_recive_ack = 0; //подтверждение, прием начат reg recive_done = 0; //окончание приема //так как сигнал из другого клокового домена //пропустим его через 2 триггера, это создаст //доп задержку на стабилизацию данных //по констрайнам частота процессора в 2 раза выше частоты SPI //значит через 2 клока данные SPI гарантированно верны reg recive_done_1 = 0; reg recive_done_2 = 0; reg recive_done_ack = 0; //подтверждение окончания приема reg need_start_transllate = 0; //необходимо начать передачу данных reg need_start_transllate_ack = 0; //передача данных начата //вектор входного сообщения localparam FULL_MESSAGE_LEN = ADDR_FIELD_SIZE + DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE; reg [FULL_MESSAGE_LEN - 1 : 0] input_data_vector = 0; //для удобства работы разделим поля входного вектора wire [DATA_FIELD_SIZE - 1 : 0] data_w; assign data_w = input_data_vector[DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE - 1 -: DATA_FIELD_SIZE]; wire [SUB_ADDR_SIZE - 1 : 0] subaddr_w; assign subaddr_w = input_data_vector[SUB_ADDR_SIZE + DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE - 1 -: SUB_ADDR_SIZE]; wire [MODULE_ADDR_SIZE - 1 : 0 ] addr_w; assign addr_w = input_data_vector[MODULE_ADDR_SIZE + SUB_ADDR_SIZE + DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE - 1 -: MODULE_ADDR_SIZE]; wire read_direction_w; assign read_direction_w = input_data_vector[1 + MODULE_ADDR_SIZE + SUB_ADDR_SIZE + DATA_FIELD_SIZE + CHECKSUM_FIELD_SIZE - 1 -: 1]; //вектор расчета контрольной суммы reg [CHECKSUM_FIELD_SIZE - 1 : 0] checksum_calc_vector = START_CHECKSUM_VAL; //вектор приема данных для расчета контрольной суммы reg [CHECKSUM_FIELD_SIZE - 1 : 0] checksum_vector = 0; //вектор выходных данных, на 1 бит меньше чем данные //так как первый отправляемый бит в него не сохраняется reg [DATA_FIELD_SIZE - 2 : 0] output_vector = 0; //значение выходного бита данных SPI должно быть готово //до первого клока, еще до защелки данных в сдвиговый регистр //поэтому первый бит берется из входного вектора //а последующие уже из сдвигового регистра //в начальный момент будет стоять семафор assign spi_out = (need_start_transllate != need_start_transllate_ack) ? data_from_module [MODULE_DATA_SIZE - 1] : //начальный момент output_vector[MODULE_DATA_SIZE - 2]; //последующие посылки (вектор на 1 бит меньше данных) //счетчик длинны сообщения reg [MES_LEN_COUNTER_SIZE - 1 : 0] mes_len_counter = 0; //счетчик длинны слова контрольной суммы reg [CHECKSUM_LEN_COUNTER_SIZE - 1 : 0] checksum_len_counter = 0; //================================================================================ == // обработка основного клока //================================================================================ == always @(posedge clk) begin //-------------------- переход между клоковыми доменами recive_done_1 <= recive_done; recive_done_2 <= recive_done_1; //-------------------- обмен данными if(spi_cs == 1'b1) //если нет выбора SPI begin //чип селект имеет гарантированную паузу, он асинхронный //гарантированная пауза приведет схему в правильное состояние //выборы адреса - one hot, один бит в векторе, //при снятии не может так получиться что бит измениться //или возникнут еще какие-то соседние биты, //потому даже если адрес изменится до снятия wr(rd)_strb //так как сигнал cs асинхронный, это не страшно, другие //адреса не выберутся и неправильных транзакций не будет //значение битов ответов тоже не важно, если чип селект //вверху их уже не проверяют
//подготовимся к приему следующего сообщения //первый фронт SPI клока - начало приема need_start_recive <= ~need_start_recive_ack; //снимем все стробы wr_strb_reg <= 0; rd_strb_reg <= 0; //снимем все выборы, module_sel <= 0; submodule_sel <= 0; //снимаем все ответы check_sum_ok <= 0; data_write_done <= 0; data_rdy <= 0; //на всякий случай, если было поднятие чипселекта //до окончания обмена, снимем флаг окончания приема recive_done_ack <= recive_done_2; end else //если есть выбор SPI begin if(recive_done_2 != recive_done_ack) //если закончен прием begin //ставим подтверждение обработки флага recive_done_ack <= recive_done_2;
//если сошлась контрольная сумма //последние сложение надо сделать в этой процедуре //так как после приема последнего бита //нет больше тактов (клоков SPI) на выполнение if((checksum_calc_vector + checksum_vector) == {CHECKSUM_FIELD_SIZE{1'b0}}) begin check_sum_ok <= 1'b1; //ставим признак //ставим выбор модуля и подадреса //сразу производим частичную дешифрацию адреса module_sel <= ({{(MODULE_ADDR_VECTOR_SIZE - 1){1'b0}}, 1'b1} << $unsigned (addr_w)); submodule_sel <= ({{(SUB_ADDR_VECTOR_SIZE - 1){1'b0}}, 1'b1} << $unsigned (subaddr_w)); if(read_direction_w == READ_DIRRECTION_VAL) //если режим чтения begin rd_strb_reg <= 1'b1; //строб чтения //отмечаем что нужно будет начать передачу //захват данных в регистр произойдет по //SPI клоку, а его выдаст мастер только после //получения сигнала готовности данных //так что это значение можно тут выставить //и не заботится о нем, даже если будет прерывание обмена //без фазы чтения need_start_transllate <= ~need_start_transllate_ack; end else //режим записи begin data_to_module <= data_w; //выставляем данные wr_strb_reg <= 1'b1; //строб записи end end //контрольная сумма end //закончен прием //если не будет признака окончания приема //не будет проверки контрольной суммы //и не будет запросов на чтение или запись //и сигналы ниже не появятся //если есть запрос на запись и подтверждение if((wr_strb_reg == 1'b1)&&(wr_ack == 1'b1)) data_write_done <= 1'b1; //отмечаем что данные записаны //если есть запрос на чтение и подтверждение if((rd_strb_reg == 1'b1)&&(rd_ack == 1'b1)) data_rdy <= 1'b1; //отмечаем что данные готовы end //есть выбор SPI end //конец обработки клока //================================================================================ == // обработка клока SPI //================================================================================ == //выбор SPI не проверяем, так как без выбора не //пройдет конец обмена, весь обмен идет внутри //одного выбора, клоков вне обмена нет always @(posedge spi_clk) begin //---------------------------------------------------------------- if (need_start_recive != need_start_recive_ack) //если начало приема ы begin //отмечаем начало приема need_start_recive_ack <= need_start_recive; //сохраняем первый принятый бит input_data_vector <= {{(FULL_MESSAGE_LEN - 1){1'b0}},spi_in}; checksum_vector <= {{(CHECKSUM_FIELD_SIZE - 1){1'b0}},spi_in}; //сохраняем начальное значение вектора расчета контрольной суммы checksum_calc_vector <= START_CHECKSUM_VAL; //заряжаем счетчики, первый бит уже принят, //окончание приема всего сообщения по равенству нулю //счетчик всего сообщения на 1 бит меньше чтобы //он стал нулем в момент приема последнего бита //а не после приема, вместе с приемом последнего //бита выставиться и признак окончания приема mes_len_counter <= (FULL_MESSAGE_LEN - 2); checksum_len_counter <= (CHECKSUM_FIELD_SIZE - 1); end else //прием begin //уменьшаем счетчики с каждым принятым битом mes_len_counter <= mes_len_counter - 1'b1; checksum_len_counter <= checksum_len_counter - 1'b1; //прием сообщения, сдвигаем входной вектор дополняя входным битом input_data_vector <= {input_data_vector[FULL_MESSAGE_LEN - 2 : 0], spi_in}; //параллельно сохраняем вектор контрольной суммы checksum_vector <= {checksum_vector [CHECKSUM_FIELD_SIZE - 2 : 0], spi_in}; //если приняли все сообщение if(mes_len_counter == 0) recive_done <= ~recive_done_ack; //отмечаем окончание приема //если приняли очередное слово контрольной суммы if(checksum_len_counter == 0) begin //заряжаем счетчик заново checksum_len_counter <= (CHECKSUM_FIELD_SIZE - 1); //добавляем принятый вектор в сумму checksum_calc_vector <= checksum_calc_vector + checksum_vector; end end //прием //----------------------------------------------------------------- //значение передаваемых данных во время приема не важны, //потому всегда передаем, к фазе передачи данные будут //запрошены и корректно обновлены if (need_start_transllate != need_start_transllate_ack) //если начало передачи begin //отмечаем начало передачи need_start_transllate_ack <= need_start_transllate; //сохраняем данные от модуля без старшего бита, //так как первый бит уже передан (взят из данных на отправку) //после этого такта, на выход будет передаваться старший бит //этого вектора output_vector <= data_from_module[DATA_FIELD_SIZE - 2 : 0]; end else //передача begin //просто сдвигаем вектор данных //значение дополнения не важно output_vector <= (output_vector << 1); end //передача end //конец обработки клока SPI endmodule
wr_rd_sync - этот сигнал в 1 зажмите, он для синхронизации служит, это для нашей специфики было нужно.
|
|
|
|
|
Jul 30 2014, 07:07
|
Профессионал
    
Группа: Участник
Сообщений: 1 778
Регистрация: 29-03-12
Пользователь №: 71 075

|
Цитата(adnega @ Jul 30 2014, 12:35)  STM32F030K6T6 по $1. Можно и не один поставить при желании)
ПЛИС, может, и хорошо, но дорого. И для меня (только начавшего вникать в ПЛИС) много вопросов в синхронизации разных частей проекта - т.е. нужно заниматься более низкоуровневым проектированием (типа, как на ASM для схем пересесть). ПЛИС очень сильная штука. Во первых real time действительно real - несколько процессов могут бежать параллельно. Во вторых полная свобода выбора ног - куда посадить рвм, куда энкодер - соответственно разводка платы существенно оптимизируется. Кроме того как говорят надежней в работе. Ну конечно пересесть с С на VHDL тут придется немного попотеть. Golikov A., Большое спасибо !
|
|
|
|
|
Jul 30 2014, 07:14
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
Огромный бонус ПЛИС - это возможность создание реально параллельных блоков, а еще в добавок там куча памяти теперь и прочих радостей. Можно и проц запихать, отказавшись от внешнего. Для частной маленькой задачи обработки энкодеров можно взять малюсенькую CPLD. Я вот тут пытался представить как обработать энкодер на таймере, у меня не получилось 4 электронных отсчетов на реальную линию, и направление надо учитывать, а в ПЛИС все будет четко, 4 электронных отсчета на реальный, и регистры накопления внутри ПЛИС. да еще забыл spi_clk - вот этот сигнал лучше завести на клоковый вход ПЛИС (это спец ножки, их много). Можно и на обычный, но времянка просядет, на клоковый лучше. И я вам настоятельно рекомендую пересаживаться на Verilog, я начинал с VHDL, а теперь на верилог пересел, одни положительные эмоции во я еще тестбенч нашел от модуля CODE `timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 15:18:31 04/16/2014 // Design Name: SPIComModule_v4_00 // Module Name: E:/SYNC_MOTOR/FPGA/SPIComModule_v4_00_tb.v // Project Name: SyncMotor // Target Device: // Tool versions: // Description: // // Verilog Test Fixture created by ISE for module: SPIComModule_v4_00 // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////
module SPIComModule_v4_00_tb; localparam DATA_SIZE = 16;
// Inputs reg clk; reg spi_cs; reg spi_clk; reg spi_in; reg [DATA_SIZE - 1 : 0] data_from_module; reg wr_rd_sync; reg wr_ack; reg rd_ack;
// Outputs wire spi_out; wire [15:0] module_sel; wire [7:0] submodule_sel; wire [DATA_SIZE - 1 : 0] data_to_module; wire wr_strb; wire rd_strb; wire check_sum_ok; wire data_write_done; wire data_rdy;
reg [DATA_SIZE - 1 : 0] datareg_00; reg [DATA_SIZE - 1 : 0] datareg_01; reg [DATA_SIZE - 1 : 0] datareg_10; reg [DATA_SIZE - 1 : 0] datareg_11; reg [DATA_SIZE - 1 : 0] data ; reg [7 : 0] addr ; reg [7 : 0] check_sum; // Instantiate the Unit Under Test (UUT) SPIComModule_v4_00 #(.MODULE_DATA_SIZE(DATA_SIZE) ) uut ( .clk(clk), .spi_cs(spi_cs), .spi_clk(spi_clk), .spi_in(spi_in), .spi_out(spi_out), .module_sel(module_sel), .submodule_sel(submodule_sel), .data_to_module(data_to_module), .data_from_module(data_from_module), .wr_rd_sync(wr_rd_sync), .wr_strb(wr_strb), .wr_ack(wr_ack), .rd_strb(rd_strb), .rd_ack(rd_ack), .check_sum_ok(check_sum_ok), .data_write_done(data_write_done), .data_rdy(data_rdy) );
integer i; initial begin // Initialize Inputs spi_cs = 0; spi_clk = 0; spi_in = 0; wr_rd_sync = 0; data = 0; addr = 0; check_sum = 0; // Wait 100 ns for global reset to finish #100; // Add stimulus here spi_cs <= 1; spi_clk <= 1; #100;
//----------------------------------------------------------- data = 16'h0000; addr = 8'b10010001; check_sum = 8'hFF + data[7:0] + data[15:8] + addr; check_sum = 0 - check_sum; wr_rd_sync = 0; spi_cs <= 0; for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = addr[7-i]; #2; spi_clk = 1; #10; end for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #8; spi_in = data[DATA_SIZE - 1 -i]; #2; spi_clk = 1; #10; end for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = check_sum[7-i]; #2; spi_clk = 1; #10; end
#100; wr_rd_sync = 1; #100;
if(data_rdy == 1) for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #10; spi_clk = 1; #10; end spi_cs <= 1; //----------------------------------------------------------- //----------------------------------------------------------- #50; data = 16'hAABB; addr = 8'b00001001; check_sum = 8'hFF + data[7:0] + data[15:8] + addr; check_sum = 0 - check_sum; wr_rd_sync = 1; spi_cs <= 0; for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = addr[7-i]; #2; spi_clk = 1; #10; end for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #8; spi_in = data[DATA_SIZE - 1 -i]; #2; spi_clk = 1; #10; end for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = check_sum[7-i]; #2; spi_clk = 1; #10; end
#100;
if(data_rdy == 1) for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #10; spi_clk = 1; #10; end spi_cs <= 1; //-----------------------------------------------------------
//----------------------------------------------------------- #50; data = 16'hCCDD; addr = 8'b00010010; check_sum = 8'hFF + data[7:0] + data[15:8] + addr; check_sum = 0 - check_sum; wr_rd_sync = 1; spi_cs <= 0; for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = addr[7-i]; #2; spi_clk = 1; #10; end for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #8; spi_in = data[DATA_SIZE - 1 -i]; #2; spi_clk = 1; #10; end for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = check_sum[7-i]; #2; spi_clk = 1; #10; end
#100;
if(data_rdy == 1) for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #10; spi_clk = 1; #10; end spi_cs <= 1; //-----------------------------------------------------------
//----------------------------------------------------------- #50; data = 16'hAABB; addr = 8'b10001001; check_sum = 8'hFF + data[7:0] + data[15:8] + addr; check_sum = 0 - check_sum; wr_rd_sync = 1; spi_cs <= 0; for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = addr[7-i]; #2; spi_clk = 1; #10; end for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #8; spi_in = data[DATA_SIZE - 1 -i]; #2; spi_clk = 1; #10; end for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = check_sum[7-i]; #2; spi_clk = 1; #10; end
#100;
if(data_rdy == 1) for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #10; spi_clk = 1; #10; end spi_cs <= 1; //----------------------------------------------------------- //----------------------------------------------------------- #50; data = 16'hAABB; addr = 8'b10010010; check_sum = 8'hFF + data[7:0] + data[15:8] + addr; check_sum = 0 - check_sum; wr_rd_sync = 1; spi_cs <= 0; for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = addr[7-i]; #2; spi_clk = 1; #10; end for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #8; spi_in = data[DATA_SIZE - 1 -i]; #2; spi_clk = 1; #10; end for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = check_sum[7-i]; #2; spi_clk = 1; #10; end
#100;
if(data_rdy == 1) for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #10; spi_clk = 1; #10; end spi_cs <= 1; //-----------------------------------------------------------
//----------------------------------------------------------- #50; data = 16'hEEFF; addr = 8'b00000000; check_sum = 8'hFF + data[7:0] + data[15:8] + addr; check_sum = 0 - check_sum; wr_rd_sync = 1; spi_cs <= 0; for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = addr[7-i]; #2; spi_clk = 1; #10; end for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #8; spi_in = data[DATA_SIZE - 1 -i]; #2; spi_clk = 1; #10; end for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = check_sum[7-i]; #2; spi_clk = 1; #10; end
#100;
if(data_rdy == 1) for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #10; spi_clk = 1; #10; end spi_cs <= 1; //-----------------------------------------------------------
//----------------------------------------------------------- #50; data = 16'h0000; addr = 8'b10000000; check_sum = 8'hFF + data[7:0] + data[15:8] + addr; check_sum = 0 - check_sum; wr_rd_sync = 1; spi_cs <= 0; for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = addr[7-i]; #2; spi_clk = 1; #10; end for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #8; spi_in = data[DATA_SIZE - 1 -i]; #2; spi_clk = 1; #10; end for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = check_sum[7-i]; #2; spi_clk = 1; #10; end
#100;
if(data_rdy == 1) for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #10; spi_clk = 1; #10; end spi_cs <= 1; //-----------------------------------------------------------
#1000; //----------------------------------------------------------- #50; data = 16'hCCCC; addr = 8'b10010010; check_sum = 8'hFF + data[7:0] + data[15:8] + addr; check_sum = 0 - check_sum; wr_rd_sync = 0; spi_cs <= 0; for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = addr[7-i]; #2; spi_clk = 1; #10; end for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #8; spi_in = data[DATA_SIZE - 1 -i]; #2; spi_clk = 1; #10; end for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = check_sum[7-i]; #2; spi_clk = 1; #10; end
#100;
if(data_rdy == 1) for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #10; spi_clk = 1; #10; end spi_cs <= 1; //-----------------------------------------------------------
//----------------------------------------------------------- #50; data = 16'hCCCC; addr = 8'b00000000; check_sum = 8'hFF + data[7:0] + data[15:8] + addr; check_sum = 0 - check_sum; wr_rd_sync = 0; spi_cs <= 0; for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = addr[7-i]; #2; spi_clk = 1; #10; end for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #8; spi_in = data[DATA_SIZE - 1 -i]; #2; spi_clk = 1; #10; end for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = check_sum[7-i]; #2; spi_clk = 1; #10; end
#100;
if(data_rdy == 1) for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #10; spi_clk = 1; #10; end spi_cs <= 1; //-----------------------------------------------------------
//----------------------------------------------------------- #50; data = 16'h0000; addr = 8'b10000000; check_sum = 8'hFF + data[7:0] + data[15:8] + addr; check_sum = 0 - check_sum; wr_rd_sync = 0; spi_cs <= 0; for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = addr[7-i]; #2; spi_clk = 1; #10; end for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #8; spi_in = data[DATA_SIZE - 1 -i]; #2; spi_clk = 1; #10; end for (i=0;i<8;i=i+1) begin spi_clk = 0; #8; spi_in = check_sum[7-i]; #2; spi_clk = 1; #10; end
#100;
if(data_rdy == 1) for (i=0;i<DATA_SIZE;i=i+1) begin spi_clk = 0; #10; spi_clk = 1; #10; end spi_cs <= 1; //----------------------------------------------------------- end initial begin // Initialize Inputs clk = 0;
// Wait 100 ns for global reset to finish #98; // Add stimulus here forever begin #5; clk <= ~clk; end end
initial begin // Initialize Inputs datareg_00 = 0; datareg_01 = 0; datareg_10 = 0; datareg_11 = 0; wr_ack = 0; rd_ack = 0; data_from_module = 0; // Wait 100 ns for global reset to finish #100; // Add stimulus here forever begin #1; wr_ack <= 0; rd_ack <= 0; data_from_module <= 'hFFFF; if((module_sel == 2'b1)&&(submodule_sel == 2'b1)) begin if(wr_strb == 1) begin datareg_00 <= data_to_module; wr_ack <= 1; end if(rd_strb == 1) begin data_from_module <= datareg_00; rd_ack <= 1; end end
if((module_sel == 2'b10)&&(submodule_sel == 2'b1)) begin if(wr_strb == 1) begin datareg_10 <= data_to_module; wr_ack <= 1; end if(rd_strb == 1) begin data_from_module <= datareg_10; rd_ack <= 1; end end
if((module_sel == 2'b1)&&(submodule_sel == 2'b10)) begin if(wr_strb == 1) begin datareg_01 <= data_to_module; wr_ack <= 1; end if(rd_strb == 1) begin data_from_module <= datareg_01; rd_ack <= 1; end end
if((module_sel == 2'b10)&&(submodule_sel == 2'b10)) begin if(wr_strb == 1) begin datareg_11 <= data_to_module; wr_ack <= 1; end if(rd_strb == 1) begin data_from_module <= datareg_11; rd_ack <= 1; end end end end
endmodule
|
|
|
|
|
Jul 30 2014, 07:59
|
Знающий
   
Группа: Свой
Сообщений: 583
Регистрация: 7-06-06
Из: Таганрог
Пользователь №: 17 840

|
Цитата(Jenya7 @ Jul 30 2014, 11:30)  Перед тем как начать програмироать на ПЛИС я читал много статей VHDL vs Verilog - все таки общий тренд в пользу VHDL. Добавлю еше что VHDL ГОСТирован, а Verilog - нет. общий тренд в пользу VHDL.
|
|
|
|
|
Jul 30 2014, 09:23
|
Гуру
     
Группа: Свой
Сообщений: 4 256
Регистрация: 17-02-06
Пользователь №: 14 454

|
Поскольку мне надо делать дело, а не быть в тренде я выбираю Verilog  Это священная война, и вы можете выбирать любую сторону. Я выбрал верилог по причине того что писать на нем легче, меньше букв, меньше атавистических библиотек которые перекрывают друг друга вызывая путаницы и несостыковки проекта. VHDL - старинный монстр, который несет на себе груз не только работы с ПЛИС, но и общего описания железа, в нем можно описать все что хочешь и максимально строго, плата за это что там надо топтать клавиатуру не по детски, и самая простая схема требует много букв. Если же ограничиться только работой с ПЛИС, то верилога 100% хватит, и писать будет удобнее. Общая тенденция что плисовики знают оба языка, потому что они представленны одинаково на рынке, а с какого начать вам решать)
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|