Closed sadr0b0t closed 6 years ago
Немного теории
В даташите на PIC32MX3-4 https://github.com/sadr0b0t/snippets/blob/master/chipkit-timer/doc/PIC32MX3-4%20Data%20Sheet.pdf
глава 14.0 Timer2/3 and Timer4/5, стр. 105 Two 32-
bit synchronous timers are available by combining Timer2 with Timer3 and Timer4 with Timer5. The 32-bit timers can operate in three modes: • Synchronous Internal 32-bit Timer • Synchronous Internal 32-bit Gated Timer • Synchronous External 32-bit Timer
Note: Throughout this chapter, references to registers TxCON, TMRx and PRx use ‘x’ to represent Timer2 through 5 in 16-bit modes. In 32-bit modes, ‘x’ represents Timer2 or 4; ‘y’ represents Timer3 or 5.
Т.е. 1) Для 32-битных таймеров объединяем 16-битные таймеры Timer2+3 или Timer4+5 2) Когда ссылаемся на имена регистров в контексте беседы о 32-битном таймере, x обозначает таймеры 2 или 4, y - таймеры 3 или 5.
Дальше смотрим референс мануал, раздел 14 - про таймеры, в отдельном файле здесь: PIC32 family reference manual Section 14 Timers https://github.com/1i7/snippets/blob/master/chipkit-timer/doc/61105F-pic32-rm-sec14-Timers.pdf
Там идем в раздел 14.4 Interrupts > Example 14-9: Timer ISR code example
/*
This code example demonstrates a simple interrupt service routine for Timer
interrupts. The user’s code at this ISR handler should perform any application
specific operations and must clear the corresponding Timer interrupt status flag
before exiting.
*/
void __ISR(_Timer_1_Vector,ipl3)Timer1Handler(void)
{
... perform application specific operations in response to the interrupt
IFS0CLR = 0x00000010; // Be sure to clear the Timer1 interrupt status
}
Example 14-10: 32-bit timer interrupt initialization code example
/*
This code example enables Timer5 interrupts, loads the Timer4:Timer5 period
register pair, and starts the 32-bit Timer module.
When a 32-bit period match interrupt occurs, the user must clear the Timer5 interrupt
status flag in software.
*/
T4CON = 0x0; // Stop 16-bit Timer4 and clear control register
T5CON = 0x0; // Stop 16-bit Timer5 and clear control register
T4CONSET = 0x0038; //Enable 32-bit mode, prescaler at 1:8,
// internal clock source
TMR4 = 0x0; // Clear contents of the TMR4 and TMR5
PR4 = 0xFFFFFFFF; // Load PR4 and PR5 registers with 32-bit value
IPC5SET = 0x00000004; // Set priority level = 1
IPC5SET = 0x00000001; //Set sub-priority level = 1
// Can be done in a single operation by assigning
// IPC5SET = 0x00000005
IFS0CLR = 0x00100000; // Clear the Timer5 interrupt status flag
IEC0SET = 0x00100000; // Enable Timer5 interrupts
T4CONSET = 0x8000; // Start the timer
Здесь можно обратить внимание, что настройки отправляются в регистры для таймера 4, а прерывания - через настройки таймера 5.
Код, внезапно, оказался рабочим, только если добавить еще в начало вызов (вектор именно для таймера5):
setIntVector(_TIMER_5_VECTOR, T45_IntHandler);
плюс мой вариант обработчика (по шаблону из Servo.cpp)
void __attribute__((interrupt(),nomips16)) T45_IntHandler (void) {
timer_handle_interrupts(TIMER4_32BIT);
// Timer4(32bit) == Timer4(16bit)+Timer5(16bit),
// Timer4 is a master timer, but interrupt flags come from Timer5
IFS0bits.T5IF = 0; // Clear timer interrupt status flag
}
Дальше причесал этот код так, чтобы он больше стал похож на то, что было в Servo.cpp
// set the vector up
setIntVector(_TIMER_5_VECTOR, T45_IntHandler);
// clear contents of the T4CON and T5CON registers
T4CON = 0x0; // stop 16-bit Timer4 and clear control register
T5CON = 0x0; // stop 16-bit Timer5 and clear control register
// set timer clock period on Timer4
T4CONbits.T32 = 1; // set 32-bit mode
T4CONbits.TCKPS = prescaler; // set prescaler
TMR4 = 0; // clear timer4 and timer5 registers
PR4 = adjustment; // period register (32-bit value)
// configure interrupt on Timer5
IPC5CLR = 0x0000001F;
IPC5SET = (_T5_IPL_IPC << 2) | _T5_SPL_IPC;
IFS0bits.T5IF = 0; // clear the Timer5 interrupt status flag
IEC0bits.T5IE = 1; // enable Timer5 interrupts
// start Timer4+5
T4CONbits.ON = 1; // start Timer4+5
Здесь пояснения к причесыванию
1) Избавились от волшебного числа 38 в T4CONSET
// Enable 32-bit mode, prescaler at 1:8,
// internal clock source
T4CONSET = 0x0038; // 0011 1000
вместо него идентичный участок с побитовым присвоением значений полям:
T4CON = 0x0; // 0 everywhere for internal clock source
T5CON = 0x0;
T4CONbits.T32 = 1; // set 32-bit mode
T4CONbits.TCKPS = prescaler; // set prescaler
T4CONSET=0x0038 в двоичном виде: 0011 1000
0011 - это, действительно, prescaler=1/8
const int TIMER_PRESCALER_1_8 = 0b011; // 0011 0000
1000 (4й-бит регистра, индекс 3) - это T4CON<3>=1:
- Set the T32 control bit (TxCON<3> = 1) to select 32-bit operations.
по поводу внутреннего источника сигнала (internal clock source):
- Clear the TCS control bit (TxCON<1> = 0) to select the internal PBCLK source. т.е. 2й бит T4CON нужно выставить в толь (T4CON<1> = 0) это произошло после глобального обнуления регистра T4CON = 0x0, поэтому тоже ок.
2) Настройки прерываний
Здесь все очевидно
ручная запись значений (xxxCLR - сбросить в 0, xxxSET - записать 1)
IFS0CLR = 0x00100000; // clear the Timer5 interrupt status flag
IEC0SET = 0x00100000; // enable Timer5 interrupts
идентична побитовой записи полей:
IFS0bits.T5IF = 0; // clear the Timer5 interrupt status flag
IEC0bits.T5IE = 1; // enable Timer5 interrupts
ключевой момент - настройки для Таймера5
3) Настройки приоритетов
Здесь вариант из мануала:
IPC5SET = 0x00000004; // Set priority level = 1
IPC5SET = 0x00000001; //Set sub-priority level = 1
заменил на вариант из Servo.cpp:
IPC5CLR = 0x0000001F;
IPC5SET = (_T5_IPL_IPC << 2) | _T5_SPL_IPC;
Проверять константы во 2м случае лень. Видно, что в 1м варианте только SET, а во 2м варианте еще какой-то бит сбрасывается в 0 с CLR. Но работает и так и так, поэтому пока пофик, оставлю вариант из Servo.cpp для единообразия.
Опять важно ,что найстройки выставляются для таймера5, а не 4.
И еще для полноты картины
Всё тот же референс мануал, раздел 14 - про таймеры, в отдельном файле здесь: PIC32 family reference manual Section 14 Timers https://github.com/1i7/snippets/blob/master/chipkit-timer/doc/61105F-pic32-rm-sec14-Timers.pdf
14.3.4.3. 32-BIT SYNCHRONOUS CLOCK COUNTER INITIALIZATION STEPS, стр 14.
The following s teps must be performed to configure the timer for 32-bit Synchronous Clock Counter mode.
- Clear the ON control bit (TxCON<15> = 0) to disable the timer.
- Clear the TCS control bit (TxCON<1> = 0) to select the internal PBCLK source.
- Set the T32 control bit (TxCON<3> = 1) to select 32-bit operations.
- Select the desired timer input clock prescale.
- Load/Clear the timer register TMRxy.
- Load the period register PRxy with the desired 32-bit match value.
- If interrupts are used: a) Clear the TyIF interrupt flag bit in the IFSx register. b) Configure the interrupt priority and subpriority levels in the IPCx register. c) Set the TyIE interrupt enable bit in the IECx register.
- Set the ON control bit (TxCON<15> = 1) to enable the timer.
b) Configure the interrupt priority and subpriority levels in the IPCx register.
Регистр должен быть не IPCx (т.е. IPC4), а IPCy (т.е. IPC5). С 4м нихера не работает, т.е. таймер, может и работает, но прерывание не вызывается.
// a) Clear the TyIF interrupt flag bit in the IFSx register.
Регистра IFSx (в смысле x, как номер таймера) нет, а есть общий на всей таймеры регистр IFS0 с полями T2IF, T3IF, T4IF, T5IF и т.п.
поэтому такой код (точно по инструкции):
IFS4bits.T5IF = 0;
не будет даже компиляться, а такой скомпиляется и даже заработает
IFS0bits.T5IF = 0;
аналогично: нет регистра IECx, есть IEC0
c) Set the TyIE interrupt enable bit in the IECx register.
еще
// 2. Clear the TCS control bit (TxCON<1> = 0) to select the internal PBCLK source.
По этой инструкции было бы логично написать код
T4CONbits.TCS = 0;
но он не скомпиляется, нет поля TCS. Но, к счастью, как-то обошлось и без него.
В итоге, исправленная инструкция
- Clear the ON control bit (TxCON<15> = 0) to disable the timer.
- Clear the TCS control bit (TxCON<1> = 0) to select the internal PBCLK source.
- Set the T32 control bit (TxCON<3> = 1) to select 32-bit operations.
- Select the desired timer input clock prescale.
- Load/Clear the timer register TMRxy.
- Load the period register PRxy with the desired 32-bit match value.
- If interrupts are used: a) Clear the TyIF interrupt flag bit in the IFS0 register. b) Configure the interrupt priority and subpriority levels in the IPCy register. c) Set the TyIE interrupt enable bit in the IEC0 register.
- Set the ON control bit (TxCON<15> = 1) to enable the timer.
То же смое с кодом:
// 1. Clear the ON control bit (TxCON<15> = 0) to disable the timer.
T4CONbits.ON = 0;
// 2. Clear the TCS control bit (TxCON<1> = 0) to select the internal PBCLK source.
//T4CONbits.TCS = 0; // won't compile
// 3. Set the T32 control bit (TxCON<3> = 1) to select 32-bit operations.
T4CONbits.T32 = 1;
// 4. Select the desired timer input clock prescale.
T4CONbits.TCKPS = prescaler; // set prescaler
// 5. Load/Clear the timer register TMRxy.
TMR4 = 0;
// 6. Load the period register PRxy with the desired 32-bit match value.
PR4 = adjustment;
// 7. If interrupts are used:
// a) Clear the TyIF interrupt flag bit in the IFS0 register.
IFS0bits.T5IF = 0;
// b) Configure the interrupt priority and subpriority levels in the IPCy register.
IPC5CLR = 0x0000001F;
IPC5SET = (_T5_IPL_IPC << 2) | _T5_SPL_IPC;
// c) Set the TyIE interrupt enable bit in the IEC0 register.
IEC0bits.T5IE = 1;
// 8. Set the ON control bit (TxCON<15> = 1) to enable the timer.
T4CONbits.ON = 1;
в таком виде все работает - таймер тикает, прерывания прилетают. Это 2й рабочий вариант кода, но оставлю предыдущий, опять же, для единообразия
Готово дело https://github.com/sadr0b0t/arduino-timer-api/commit/4c984b0833437ee92fcdea8e1efeeaca113708cd
Заодно добавил больше таймеров: _TIMER1, _TIMER2, _TIMER3, _TIMER4, _TIMER5, _TIMER2_32BIT, _TIMER4_32BIT
Все работают на PIC32/ChipKIT, на AVR/Arduino имена не зайдействованы.
На ChipKIT TIMER_DEFAULT=_TIMER4_32BIT (32-битный режим, 1Гц и 2Гц работают из коробки)
И к именам таймеров добавил префикс нижнее подчерчивание "_", т.к. TIMER2 внезапно стал конфликтовать с определением из Arduino.h (при этом другие имена таймеров, как ни странно, заняты не были).
Добавить варианты 32-битных таймеров на PIC32/ChipKIT
Во-первых, без 32-битного режима на PIC32 не получится добавить реализацию вспомогательных вызовов timer_init_ISR_1Hz и timer_init_ISR_2Hz, которые есть на Ардуине
Во-вторых, раз уж такие таймеры есть, то логично иметь в качестве варианта в библиотеке, работающей с таймерами.
В API можно решить вопрос следующим образом:
Доки по включению таймеров в разных режимах смотреть здесь https://github.com/1i7/snippets/tree/master/chipkit-timer/doc https://github.com/1i7/snippets/blob/master/chipkit-timer/doc/61105F-pic32-rm-sec14-Timers.pdf