Среднечастотный частотомер на AVR. Часть 1, динамическая индикация.

  Давно хотелось иметь функциональный генератор с приличными характеристиками и не фантастической сложностью. Поэтому на Ali была прикуплен чип XR-2206, поиском по инету найдена схема генератора — XR-2206 5Hz to 300kHz Function Generator. Конструкция достаточно хорошо проработана и описана.
  Собственно к схеме генератора претензий нет, некоторые вещи я бы сделал по другому, но это потом в процессе изготовления. Мне сразу не понравилась цифровая шкала генератора (там же, страница 2). Его программа представляет сборку из скетчей Arduino, я их не понимаю и не горю желанием изучать. Да и применение ATmega328 мне показалось неоправданно жирным. Короче решил спроектировать свою цифровую шкалу на ATmega8A. Результат представляю вашему вниманию. код написан на ассемблере AVR в среде AVR Studio 4.19.

  Принцип электронно-счетного способа измерения частоты заключается в непосредственном подсчете кол-ва импульсов измеряемой частоты за образцовый период времени. Частота и время являются обратными величинами и связаны формулой F = 1 / T, где частота представлена в герцах, а время в секундах.
  В данном измерителе образцовый период времени равен 1 сек, кол-во подсчитаных за этот период импульсов будет равно измеряемой частоте в герцах.
  Формирователем образцового периода является Таймер 2, работающий в асинхронном режиме от «часового» кварца 32768 Hz. Контроллер тактируется от внутреннего RC-генератора частотой 8 MHz. Максимальная частота измеряемых импульсов при этом равна 8 / 2,5 = 3,2 MHz.
По классификации DIHALT программа представляет собой «Флаговый автомат + прерывания».
  Я буду приводить куски программы с описанием для лучшего понимания работы. Полные исходники можно скачать в конце статьи. К исходникам приложен проект Proteus 7.10 с симуляцией работы программы. Статья описывает особенности примененных кодовых решений, схемотехническое построение и схему подключения можно посмотреть на схеме Proteus. Сразу скажу, на схеме Proteus могут отсутствовать некоторые детали, не нужные для симуляции, но обязательные при изготовлении конструкции. Но ориентироваться на нее можно, тем более статья не расчитана на новичков в схемотехнике AVR, больше на программистов.

Проект включает файл макросов FQMacros.inc. Применение макросов для RISC контроллеров делает текст проекта короче и более удобочитаемым. Содержание:

;------ MACROS -----------------------------------------------------------------;
; OUTI	@0 (константа)
.MACRO  OUTI                        ; вывод константы в порт
    LDI   Temp, @1                  ;
    OUT   @0, Temp                  ;
.ENDM
;
; LDIL	@0 (регистр (R0..R15)), @1 (константа)
.MACRO  LDIL                        ; загрузка константы в младший регистр
    LDI   Temp, @1                  ;
    MOV   @0, Temp                  ;
.ENDM
;
; LDIZ @0 (адрес памяти программ (слово))
.MACRO  LDIZ                        ; загрузка адреса перед LPM
    LDI   ZL, Low(@0*2)             ;
    LDI   ZH, High(@0*2)            ;
.ENDM
;
; LDIDX @0 (индексный регистр X, Y, Z), @1 (адрес (слово))
.MACRO  LDIDX                       ; загрузка адреса в индексный регистр
    LDI   @0L, Low(@1)              ;
    LDI   @0H, High(@1)             ;
.ENDM
;
; ADIDX @0 (индексный регистр X, Y, Z), @1 (регистр)
.MACRO  ADIDX                       ; сложение адреса со смещением
    ADD   @0L, @1                   ;
    LDI   Temp, 0x00                ;
    ADC   @0H, Temp                 ;
.ENDM

  Из названия классификации понятно, что логика работы флагового автомата построена на работе с флагами. Под флагом понимается логический примитив, который может принимать два состояния — выключен/включен. В двоичной логике это бит.
  И хотя в AVR нет полноценного битового процессора (как в MSC-51), построить флаговый автомат не так сложно. Принцип один и тот же, различается реализация.

;------ Предназначение регистров -----------------------------------------------;
.DEF    Counter     = R0            ; Счетчик динамической индикации
.DEF    FxOVF       = R1            ; Старший байт частоты (счетный)
.DEF    Safe        = R16           ; Сохранение SREG в прерывании
.DEF    Temp        = R17           ; Временный регистр
.DEF    Cycler      = R18           ; Временный регистр
.DEF    BITx1       = R19           ; Битовый регистр
.EQU    MRK_BCD     = 0             ; Флаг преобразования PackBCD <- BIN
.EQU    MRK_B2B     = 1             ; Флаг преобразования UnpackBCD <- PackBCD
.EQU    MRK_Symb    = 2             ; Флаг преобразования Symbols code <- UnpackBCD
.EQU    MRK_Key     = 6             ; Флаг опроса клавиатуры
.EQU    MRK_Dyn     = 7             ; Флaг обновления динамической индикации

Битовый регистр BITx1 назначен из «верхних» регистров как работающими со всеми командами AVR. Тут же предопределены и флаги.

Задействованы прерывания всех трех имеющихся таймеров ATMega8A:
  • Таймер 0, восьмиразрядный, выполняет функцию системного таймера для управления динамической индикацией. При частоте контроллера 8 МГц и предделителе 64 частота прерываний ~488 Гц (флаг MRK_Dyn), что дает обновление с частотой ~61 Гц для каждого разряда (при задействованных 8 разрядах). Если задействованы 7 разрядов, каждый разряд будет обновляться с частотой ~70 Гц и т.д.
  • Таймер 1, шестнадцатиразрядный, выполняет функцию счета импульсов с внешнего входа. Т.к. шестнадцатиразрядный счетчик может вместить не более 65536 импульсов, он программно расширен еще на 8 разрядов (FxOVF, в сумме 24 разряда), что дает емкость 16777216 импульсов. В данном случае это более чем достаточно, т.к. частота на входе таймера ограничена 2,5 тактовой (3,2 МГц), что дает 3200000 импульсов. Таким образом подсчет импульсов измеряемой частоты производится аппаратно шестнадцатиразрядным таймером, при переполнении его прерывание инкрементирует дополнительный регистр.
  • Таймер 2, восьмиразрядный, выполняет функцию формирования измерительного интервала. Для этого к нему подключен «часовой» кварц 32768 Гц, что при предделителе 128 позволяет получить импульсы с частотой 1 Гц. Прерывание переносит результат измерений из рабочих регистров в ОЗУ, обнуляет рабочие счетные регистры, устанавливает флаг готовности результата измерений (MRK_BCD) и перед новым измерением обнуляет свой регистр и предделитель. Начинается новое измерение, предыдущее обрабатывается флаговым автоматом.
Таким образом программа имеет три независимые ветви (потока) выполнения:
  1. Регенерация динамической индикации по прерыванию таймера 0 (~488 Гц).
  2. Инкремент старшего байта счетного регистра по прерыванию таймера 1 (~49 Гц при входной частоте 3,2 МГц).
  3. Перенос содержимого счетного регистра в ОЗУ и обработка входных данных по прерыванию таймера 2 (1 Гц).

;------ Таблица прерываний------------------------------------------------------;
.CSEG
;------ Начальная инициализация ------------------------------------------------;
.ORG 0                              ; 0x0000
    RJMP   _Reset                   ; Power-on Reset   
;------ Прерывание таймера 2 ---------------------------------------------------;
.ORG OVF2addr                       ; 0x0004 (в словах)
    RJMP   OVF2_VECTOR              ; Timer/Counter2 Overflow
;------ Прерывание таймера 1 ---------------------------------------------------;
.ORG OVF1addr                       ; 0x0008 (в словах)
    RJMP   OVF1_VECTOR              ; Timer/Counter1 Overflow
;------ Прерывание таймера 0 ---------------------------------------------------;
.ORG OVF0addr                       ; 0x0009 (в словах)
    IN     Safe, SREG               ; сохранить флаги SREG (SBR изменяет флаги Z,C,N,V,H,S)
    SBR    BITx1, (1<<MRK_Dyn)      ; установить флаг начала регенерации дисплея
    OUT    SREG, Safe               ; восстановить флаги SREG
    RETI                            ;
.ORG INT_VECTORS_SIZE               ; 0x0013 (в словах)

  Как обычно по нулевому адресу расположен переход на код инициализации внутренних используемых устройств контроллера, предустановка переменных и т.д. Здесь можно отметить не совсем привычный код инициализации таймера 2 в асинхронном режиме. После включения асинхронного режима (бит AS2 регистра ASSR) и установки режима таймера (регистр TCCR2) разрешать общие прерывания можно только после подтверждения таймером о изменении режима. В зависимости от того, какой регистр был изменен TCNT2, OCR2, или TCCR2 подтверждение будет выдано битами TCN2UB, OCR2UB, или TCR2UB регистра ASSR. Пока хоть один бит единичный, включать прерывания нельзя.

;------ внешние подключения ----------------------------------------------------;
.EQU    T1          = PORTD5        ; вход частотомера, ножка 11 PDIP
;------ подключение разрядов семисегментного индикатора ------------------------;
.EQU    Rat1        = PORTB0        ; разряд PORTB0, PORTB, ножка 14 PDIP
.EQU    Rat2        = PORTD7        ; разряд PORTD7, PORTD, ножка 13 PDIP
.EQU    Rat3        = PORTD6        ; разряд PORTD6, PORTD, ножка 12 PDIP
.EQU    Rat4        = PORTD4        ; разряд PORTD4, PORTD, ножка 6 PDIP
.EQU    Rat5        = PORTD3        ; разряд PORTD3, PORTD, ножка 5 PDIP
.EQU    Rat6        = PORTD2        ; разряд PORTD2, PORTD, ножка 4 PDIP
.EQU    Rat7        = PORTD1        ; разряд PORTD1, PORTD, ножка 3 PDIP
.EQU    Rat8        = PORTD0        ; разряд PORTD0, PORTD, ножка 2 PDIP
;------ подключение сегментов семисегментного индикатора -----------------------;
.EQU    SegA        = PORTC5        ; разряд PORTC5, PORTC, ножка 28 PDIP
.EQU    SegB        = PORTC4        ; разряд PORTC4, PORTC, ножка 27 PDIP
.EQU    SegC        = PORTC3        ; разряд PORTC3, PORTC, ножка 26 PDIP
.EQU    SegD        = PORTC2        ; разряд PORTC2, PORTC, ножка 25 PDIP
.EQU    SegE        = PORTC1        ; разряд PORTC1, PORTC, ножка 24 PDIP
.EQU    SegF        = PORTC0        ; разряд PORTC0, PORTC, ножка 23 PDIP
.EQU    SegG        = PORTB2        ; разряд PORTB1, PORTB, ножка 16 PDIP
.EQU    SegH        = PORTB1        ; разряд PORTB2, PORTB, ножка 15 PDIP
;
_Reset: ; Начальная инициализация
;------ Установить вершину стека -----------------------------------------------;
    OUTI  SPH, High(RAMEND)         ;
    OUTI  SPL, Low(RAMEND)          ;
;------ Отключить сторожевой таймер --------------------------------------------;
    OUTI  WDTCR, (1<<WDE)|(1<<WDCE) ;
    OUTI  WDTCR, (0<<WDE)           ;
;------ Отключить аналоговый компаратор ----------------------------------------;
    OUTI  ACSR, (1<<ACD)            ;
;------ Конфигурация портов ----------------------------------------------------;
    OUTI  DDRB, (1<<SegG)|(1<<SegH)|(1<<Rat1)
    OUTI  DDRC, (1<<SegA)|(1<<SegB)|(1<<SegC)|(1<<SegD)|(1<<SegE)|(1<<SegF)
    OUTI  DDRD, (1<<Rat8)|(1<<Rat7)|(1<<Rat6)|(1<<Rat5)|(1<<Rat4)|(1<<Rat3)|(1<<Rat2)
    OUTI  PORTD, (1<<T1)            ; Т1 - вход с поддтяжкой
;------ Очистка регистров ------------------------------------------------------;
    CLR   FxOVF                     ; обнуление счетного регистра
    CLR   BITx1                     ; обнуление битового регистра
    CLR   Counter                   ; обнуление счетчика динамической индикации
    CLRIBIT PORTD, Rat8             ; гашение
    CLRIBIT PORTD, Rat7             ; всех
    CLRIBIT PORTD, Rat6             ; разрядов
    CLRIBIT PORTD, Rat5             ; индикатора
    CLRIBIT PORTD, Rat4             ; -//-
    CLRIBIT PORTD, Rat3             ; -//-
    CLRIBIT PORTD, Rat2             ; -//-
    CLRIBIT PORTB, Rat1             ; -//-
;------ Настроить таймер 0, режим Normal ---------------------------------------;
    OUTI  TCCR0, (1<<CS01)|(1<<CS00); прескалер 64
;------ Настроить таймер 1, режим Normal, внешний импульс ----------------------;
    OUTI  TCCR1B, (1<<CS12)|(1<<CS11)|(1<<CS10)
;------ Настроить таймер 2, Asynchronous Operation -----------------------------;
    ; на входах TOSC1 (XTAL1) и TOCSC2 (XTAL2) "часовой" кварц 32768 Hz
    OUTI  ASSR, (1<<AS2) 
    ; Normal, прерывание по переполнению
    OUTI  TCCR2, (1<<CS22)|(1<<CS20); прескалер 128
;------ Включить прерывания ----------------------------------------------------;
    OUTI  TIMSK, (1<<TOIE0)|(1<<TOIE1)|(1<<TOIE2) ; разрешить прерывания таймеров
SEI_TEST_EN:                        ; проверка на допустимость разрешения прерываний
    IN    Temp, ASSR                ;
    ANDI  Temp, (1<<TCN2UB)|(1<<OCR2UB)| (1<<TCR2UB)
    TST   Temp                      ; проверка битов TCN2UB, OCR2UB, TCR2UB
    BRNE  SEI_TEST_EN               ; возврат, если биты не сброшены
    SEI                             ; разрешить прерывания
;------ Номер версии прошивки --------------------------------------------------;
    LDIDX Z, Ident*2+iSize-vSize-1  ; Загрузить адрес в идентификаторе
    LDIDX Y, RAM_Symbol+vSize       ; Загрузить адрес в буфере кодов цифр
    LDI   Cycler, vSize             ; длина строки версии
locIdent:
    LPM   Temp, Z+                  ; Загрузить байт строки версии
    ST    -Y, Temp                  ; перенести в буфер кодов цифр
    DEC   Cycler                    ;
    BRNE  locIdent                  ;

О макросе CLRIBIT и идентификаторе Ident см. ниже.
  Прерывания первой и второй ветви могут вносить погрешность в измерения за счет задержки счетного прерывания (AVR не имеет приоритетности прерываний). Для мимизации такой погрешности, подпрограммы обработки сделаны максимально короткими. Подпрограмма таймера 0 собственно и не оформлена в виде подпрограммы, ее код размещен прямо в таблице прерываний на месте прерываний USART. Разместить таким же образом подпрограмму таймера 1 не хватает места.
  Сокращению времени обработки служит и использование специально выделенного регистра Safe, работа с ним в два раза быстрее использования стека (POP, PUSH). Сохранение и восстановление SREG обязательно, т.к. команда SBR может изменить флаги Z,C,N,V,H,S.

;------ Прерывание таймера 1 ---------------------------------------------------;
OVF1_VECTOR:                        ; INC изменяет флаги Z,N,V,S
    IN     Safe, SREG               ; сохранить флаги SREG
    INC    FxOVF                    ; инкрементировать счетный регистр
    OUT    SREG, Safe               ; восстановить флаги SREG	
    RETI                            ;
;------ Прерывание таймера 2 (раз в 1 сек) -------------------------------------;
OVF2_VECTOR:                        ; SBR изменяет флаги Z,C,N,V,H,S; CLR - Z,C,N,V,S
    MOV    Safe, Temp               ; сохранить временный регистр
    STS    RAM_Val_H, FxOVF         ; перенос значения счетного регистра в память
    IN     Temp, TCNT1L             ; перенос значения
    STS    RAM_Val_L, Temp          ; счетного
    IN     Temp, TCNT1H             ; регистра
    STS    RAM_Val_M, Temp          ; в память
    LDI    Temp, 0x00               ; обнуление
    OUT    TCNT1H, Temp             ; счетного
    OUT    TCNT1L, Temp             ; регистра
    OUTI   SFIOR, (1<<PSR2)         ; сброс предделителя таймера 2
    MOV    Temp, Safe               ; восстановить временный регистр
    IN     Safe, SREG               ; сохранить флаги SREG
    CLR    FxOVF                    ; обнуление счетного регистра
    SBR    BITx1, (1<<MRK_BCD)      ; установить флаг начала обработки измерений
    OUT    SREG, Safe               ; восстановить флаги SREG	
    RETI                            ;

Распределение оперативной памяти:

;------ Разрядность дисплея ----------------------------------------------------;
.EQU    BufSize      =  7           ; Кол-во цифр на диcплее
.;----- Резервирование ячеек ОЗУ -----------------------------------------------;
.DSEG
.ORG SRAM_START
    RAM_Val_L:     .byte    1       ; Измеренное значение, младший байт
    RAM_Val_M:     .byte    1       ; Измеренное значение, средний байт
    RAM_Val_H:     .byte    1       ; Измеренное значение, старший байт
BegPackBCD:
    RAM_BCD_0:     .byte    1       ; Упакованный BCD, 1 байт
    RAM_BCD_1:     .byte    1       ; Упакованный BCD, 2 байт
    RAM_BCD_2:     .byte    1       ; Упакованный BCD, 3 байт
    RAM_BCD_3:     .byte    1       ; Упакованный BCD, 4 байт
EndPackBCD:
.EQU	SizePackBCD	= EndPackBCD - BegPackBCD
    RAM_UnBCD:     .byte    BufSize ; Неупакованные BCD
    RAM_Symbol:    .byte    BufSize ; Коды цифр (symbols)

Цикл обработки результата измерения при этом выглядит так:
  1. Прерывание таймера 2 (OVF2_VECTOR) переносит измеренное значение в ОЗУ (RAM_Val_X);
  2. Подпрограмма PackBCD < — BIN преобразует бинарное значение в упакованный BCD (RAM_BCD_X);
  3. Подпрограмма UnpackBCD < — PackBCD распаковывает BCD в неупакованный (RAM_UnBCD);
  4. Подпрограмма Symbols code < — UnpackBCD преобразует неупакованный BCD в коды символов семисегментного дисплея (RAM_Symbol);
  5. Цикл может в любое время прерываться регенерацией дисплея и инкрементом счетного регистра;
  На этом цикл заканчивается до следующего прерывания таймера 2. Размещение промежуточных данных в ОЗУ сделано для полной изоляции подпрограмм друг от друга, также это очень помогает в отладке. Из-за особеностей флагового автомата подпрограммы не оформлены как подпрограммы, нет обращения по метке, нет вызовов RCALL и RET в конце. Во флаговом автомате вызов подпрограммы осуществляется установкой соответствующего флага, в конце подпрограммы этот флаг снимается, и если требуется устанавливается флаг следующей операции. Основной цикл при этом выглядит так:

Main:                               ; Основной цикл
;
;== Преобразование Symbols code <- UnpackBCD с гашением 0, MRK_Symb = 1 ========;
    SBRS  BITx1, MRK_Symb           ; если нет  флага начала обработки, на выход
    RJMP  locMP_02                  ;
//  Код подпрограммы UnpackBCDtoSymbolsCode
    CBR   BITx1, (1<<MRK_Symb)      ; сбросить флаг текущего шага 
locMP_02: ;=====================================================================;
;
;== Преобразование UnpackBCD <- PackBCD, MRK_B2B = 1 ===========================;
    SBRS  BITx1, MRK_B2B            ; если нет  флага начала обработки, на выход
    RJMP  locMP_01                  ;
//  Код подпрограммы PackBCDtoUnpackBCD
    SBR   BITx1, (1<<MRK_Symb)      ; установить флаг следующего шага
    CBR   BITx1, (1<<MRK_B2B)       ; сбросить флаг текущего шага 
locMP_01: ;=====================================================================;
;
;== Преобразование PackBCD <- BIN, MRK_BCD = 1 =================================;
    SBRS  BITx1, MRK_BCD            ; если нет  флага начала обработки, на выход
    RJMP  locMP_00                  ;
//  Код подпрограммы BINtoPackBCD
    SBR   BITx1, (1<<MRK_B2B)       ; установить флаг следующего шага
    CBR   BITx1, (1<<MRK_BCD)       ; сбросить флаг текущего шага
locMP_00: ;=====================================================================;
;
;== Регенерация дисплея, MRK_Dyn = 1 ===========================================;
    SBRS  BITx1, MRK_Dyn            ; если нет  флага обновления, на выход
    RJMP  locMP_Dn                  ;
;*******************************************************************************;
; 1. Гасится текущий отображаемый разряд;                                       ;
;*******************************************************************************;
; 2. Счетчик разрядов декрементируется;                                         ;
; . . . . . . . . . . . . . .
; После цикла сканирования во время перезагрузки счетчика
    SBR   BITx1, (1<<MRK_Key)       ; установить флаг опроса клавиатуры
; . . . . . . . . . . . . . .
;*******************************************************************************;
; 3. Устанавливается семисегментный код символа;                                ;
;*******************************************************************************;
; 4. Зажигается следующий разряд;                                               ;
;*******************************************************************************;
    CBR   BITx1, (1<<MRK_Dyn)       ; сбросить флаг обновления индикации
locMP_Dn: ;=====================================================================;
;
;== Опрос клавиатуры, MRK_Key = 1 ==============================================;
    SBRS  BITx1, MRK_Key            ; если нет  флага начала обработки, на выход
    RJMP  locMP_Kb                  ;
//  Обработка кнопок, не реализовано
    CBR   BITx1, (1<<MRK_Key)       ; сбросить флаг 
locMP_Kb:
;
    RJMP  Main                      ; бесконечный цикл

  Обратите внимание, что подпрограммы обработки результата измерений размещены в обратной последовательности. Это нужно для того, чтобы после каждого действия происходил выход в главный цикл для проверки флагов регенерации дисплея и опроса клавиатуры.
  Код первой подпрограммы обрабатывающей результаты измерения BINtoPackBCD основан на аппноте Atmel AVR204 и преобразует трехбайтовое бинарное представление числа в четырехбайтовый упакованный BCD формат. Название подпрограмм даны условно, т.к. во флаговом автомате они оформлены нестандартно, без метки. Полный код BINtoPackBCD:

.DEF    tBCD0       = R2            ; BCD value digits 1 and 0 
.DEF    tBCD1       = R3            ; BCD value digits 3 and 2 
.DEF    tBCD2       = R4            ; BCD value digits 4 and 5 
.DEF    tBCD3       = R5            ; BCD value digits 6 and 7
.EQU    AdBCD0      = 2             ; address of tBCD0 
.EQU    AdBCD3      = 5             ; address of tBCD3 
.DEF    FxBUF_L     = R20           ; Младший байт частоты (рабочий)
.DEF    FxBUF_M     = R21           ; Средний байт частоты (рабочий)
.DEF    FxBUF_H     = R22           ; Старший байт частоты (рабочий)
;
;== Преобразование PackBCD <- BIN, MRK_BCD = 1 =================================;
    SBRS  BITx1, MRK_BCD            ; если нет  флага начала обработки, на выход
    RJMP  locMP_00                  ;
//
    IN    Temp, MCUCSR              ; проверка на холодный старт
    ANDI  Temp, (1<<EXTRF)|(1<<PORF);
    BREQ  locBCD0                   ; если нет - выход
    CLR   Temp                      ;
    OUT   MCUCSR, Temp              ;
    RJMP  locBCD4                   ; отбрасывание первого измерения
locBCD0:
    LDS   FxBUF_H, RAM_Val_H        ; загрузка
    LDS   FxBUF_M, RAM_Val_M        ; измеренного
    LDS   FxBUF_L, RAM_Val_L        ; значения из ОЗУ
;--- Код на основе аппноты AVR204 ----------------------------------------------;
    LDI   Cycler, 24                ; Кол-во итераций (24 бита) 
    CLR   tBCD3                     ; очистить
    CLR   tBCD2                     ; регистры 
    CLR   tBCD1                     ; результата
    CLR   tBCD0                     ; -//-
    CLR   ZH                        ; очистить старший байт индексного регистра
locBCD1:
    LSL   FxBUF_L                   ; сдвинуть
    ROL   FxBUF_M                   ; входное
    ROL   FxBUF_H                   ; значение
    ROL   tBCD0                     ; и вдвинуть
    ROL   tBCD1                     ; его
    ROL   tBCD2                     ; в
    ROL   tBCD3                     ; результат
    DEC   Cycler                    ; декрементировать счетчик цикла
    BREQ  locBCD3                   ; выход при нуле
    LDI   r30, AdBCD3+1             ; в Z адрес старшего регистра результата + 1
locBCD2: 
    LD    Temp, -Z                  ; загрузить старший регистр результата (предекремент)
    SUBI  Temp, -$03                ; сложить с 0x03
    SBRC  Temp, 3                   ; если бит 3 очищен, пропустить след. команду 
    ST    Z, Temp                   ; сохранить в регистре результата
    LD    Temp, Z                   ; загрузить старший регистр результата
    SUBI  Temp, -$30                ; сложить с 0x30 
    SBRC  Temp, 7                   ; если бит 7 очищен, пропустить след. команду
    ST    Z, Temp                   ; сохранить в регистре результата
    CPI   ZL, AdBCD0                ; конец достигнут?
    BRNE  locBCD2                   ; если нет, повторить
    RJMP  locBCD1                   ; продолжить цикл 
locBCD3:
    STS   RAM_BCD_0, tBCD0          ; сохранение
    STS   RAM_BCD_1, tBCD1          ; упакованного
    STS   RAM_BCD_2, tBCD2          ; BCD
    STS   RAM_BCD_3, tBCD3          ; в ОЗУ
//
    SBR   BITx1, (1<<MRK_B2B)       ; установить флаг следующего шага
locBCD4:
    CBR   BITx1, (1<<MRK_BCD)       ; сбросить флаг текущего шага
locMP_00: ;=====================================================================;
;
.UNDEF 	tBCD0                       ; R2
.UNDEF 	tBCD1                       ; R3
.UNDEF 	tBCD2                       ; R4
.UNDEF 	tBCD3                       ; R5
.UNDEF  FxBUF_L                     ; R20
.UNDEF  FxBUF_M                     ; R21
.UNDEF  FxBUF_H                     ; R22

  Обратите внимание, в коде проверяется наличие как флага «холодного» старта PORF, так и флага внешнего сброса EXTRF. Это связано с тем, что в модели Proteus вместо PORF устанавливается EXTRF. Пришлось извернуться, для железа это безразлично, реакция на «холодный» и «теплый» старт будет одинакова.
  В AVR очень неудобно работать с упакованными BCD числами, поэтому следующей подпрограммой PackBCDtoUnpackBCD мы их распакуем:

.DEF    Mask_4      = R20           ; маска ниббла
.DEF    PackBCD     = R21           ; упакованный BCD
.DEF    CopyBCD     = R22           ; копия -//-
;
;== Преобразование UnpackBCD <- PackBCD, MRK_B2B = 1 ===========================;
    SBRS  BITx1, MRK_B2B            ; если нет  флага начала обработки, на выход
    RJMP  locMP_01                  ;
//
    LDIDX X, RAM_UnBCD              ; адрес буфера неупакованного BCD
    LDIDX Y, RAM_BCD_0              ; адрес буфера упакованного BCD
    LDI   Mask_4, 0b00001111        ; маска ниббла
    LDI   Cycler, SizePackBCD       ; кол-во байт упакованного BCD
locP2U:
    LD    PackBCD, Y+               ; чтение упакованного BCD
    MOV   CopyBCD, PackBCD          ; копия
    AND   PackBCD, Mask_4           ; младший ниббл
    ST    X+, PackBCD               ; в буфер неупакованного BCD
    SWAP  CopyBCD                   ; перевернем копию
    AND   CopyBCD, Mask_4           ; старший ниббл
    ST    X+, CopyBCD               ; в буфер неупакованного BCD
    DEC   Cycler                    ;
    BRNE  locP2U                    ;
//
    SBR   BITx1, (1<<MRK_Symb)      ; установить флаг следующего шага
    CBR   BITx1, (1<<MRK_B2B)       ; сбросить флаг текущего шага 
locMP_01: ;=====================================================================;
;
.UNDEF    Mask_4                    ; R20
.UNDEF    PackBCD                   ; R21
.UNDEF    CopyBCD                   ; R22

Чтобы полностью подготовить результат к выводу на семисегментные индикаторы, необходим программный дешифратор BCD кода в семисегментный. Заодно можно погасить незначащие нули.

;== Преобразование Symbols code <- UnpackBCD с гашением 0, MRK_Symb = 1 ========;
    SBRS  BITx1, MRK_Symb           ; если нет  флага начала обработки, на выход
    RJMP  locMP_02                  ;
//
    CLT                             ; очистить флаг Т
    LDIDX X, RAM_UnBCD+BufSize      ; конец буфера неупакованного BCD
    LDIDX Y, RAM_Symbol+BufSize     ; конец буфера кодов цифр
    LDI   Cycler, BufSize           ; кол-во знакомест
locSm0:
    DEC   Cycler                    ; флаг Z=1 - последняя цифра
    LDIZ  TBL_DIGITS                ; Загрузить адрес таблицы кодов символов
    LD    Temp, -X                  ; загрузить неупакованный BCD 
    BRTS  locSm1                    ; если Т установлен
    BREQ  locSm1                    ; если последняя цифра
    TST   Temp                      ;
    BREQ  locSm2                    ; если ноль
    SET                             ; установить флаг Т
locSm1:
    ADD   ZL, Temp                  ; Найти
    CLR   Temp                      ; нужный 
    ADC   ZH, Temp                  ; символ
    LPM   Temp, Z                   ; загрузить код символа
locSm2:
    ST    -Y, Temp                  ; сохранить код символа
    TST   Cycler                    ;
    BRNE  locSm0                    ;
//
    CBR   BITx1, (1<<MRK_Symb)      ; сбросить флаг текущего шага 
locMP_02: ;=====================================================================;

  Код перебирает все цифры из буфера неупакованных BCD начиная со старшей. Пока в буфере 0 он будет перенесен в буфер кодов цифр без преобразования, а т.к. все биты равны нулю, для семисегментного индикатора это гашение. Как только встретился не ноль, будет установлен флаг Т. После этого текущий и все последующие разряды будут пребразованы в коды семисегментного индикатора. Последняя (самая младшая) цифра в числе всегда переносится в буфер кодов цифр с преобразованием (это если на индикаторе одни нули).

  В представленной программе особенностью управления сегментами и разрядами индикатора является их индивидуальное управление. Сегментам и разрядам можно назначить выводы любых портов в произвольном порядке. Это упрощает разводку печатной платы. Например у данного варианта программы для корпуса PDIP выводы разрядов индикатора с одной стороны корпуса, сегментов с другой.

;*******************************************************************************;
; 1. Гасится текущий отображаемый разряд;                                       ;
;*******************************************************************************;
    MOV   TmpDyn, Counter           ;
    ANDI  TmpDyn, 0b00000111        ; не более 8 разрядов
    LSL   TmpDyn                    ; Команда занимает 2 слова
    LDIDX Z, LocEn0                 ; адрес таблицы команд
    ADIDX Z, TmpDyn                 ; адрес команды в таблице
    IJMP                            ; переход на команду
LocEn0:
;--- Разряд 1 ------------------------------------------------------------------;
    CLRIBIT PORTB, Rat1             ;
    RJMP  LocEn1                    ;
;--- Разряд 2 ------------------------------------------------------------------;
    CLRIBIT PORTD, Rat2             ;
    RJMP  LocEn1                    ;
;--- Разряд 3 ------------------------------------------------------------------;
    CLRIBIT PORTD, Rat3             ;
    RJMP  LocEn1                    ;
;--- Разряд 4 ------------------------------------------------------------------;
    CLRIBIT PORTD, Rat4             ;
    RJMP  LocEn1                    ;
;--- Разряд 5 ------------------------------------------------------------------;
    CLRIBIT PORTD, Rat5             ;
    RJMP  LocEn1                    ;
;--- Разряд 6 ------------------------------------------------------------------;
    CLRIBIT PORTD, Rat6             ;
    RJMP  LocEn1                    ;
;--- Разряд 7 ------------------------------------------------------------------;
    CLRIBIT PORTD, Rat7             ;
    RJMP  LocEn1                    ;
;--- Разряд 8 ------------------------------------------------------------------;
    CLRIBIT PORTD, Rat8             ;
LocEn1:


;*******************************************************************************;
; 2. Счетчик разрядов декрементируется;                                         ;
;*******************************************************************************;
    MOV   TmpDyn, Counter           ;
    DEC   TmpDyn                    ; счетчик разрядов декрементируется
    BRPL  LocDn0                    ; обработаны еще не все разряды
    LDI   TmpDyn, BufSize-1         ; предустановка счетчика
    SBR   BITx1, (1<<MRK_Key)       ; установить флаг опроса клавиатуры
LocDn0:
    MOV   Counter, TmpDyn           ; сохраним до следующей регенерации


;*******************************************************************************;
; 3. Устанавливается семисегментный код символа;                                ;
;*******************************************************************************;
    LDIDX Z, RAM_Symbol             ; адрес буфера кодов цифр
    ADIDX Z, TmpDyn                 ; Найти нужный символ
    LD    Pattern, Z                ; Загрузить код цифры
;--- 1 сегмент -----------------------------------------------------------------;
    LSL   Pattern                   ; сдвиг влево, старший разряд в С
    BRCS  loc70                     ; если С = 1
    CLRIBIT PortB, SegH             ;
loc70:
    BRCC  loc71                     ; если С = 0
    SETIBIT PortB, SegH             ;
loc71:
;--- 2 сегмент -----------------------------------------------------------------;
    LSL   Pattern                   ; сдвиг влево, следующий разряд в С
    BRCS  loc60                     ; если С = 1
    CLRIBIT PortB, SegG             ;
loc60:
    BRCC  loc61                     ; если С = 0
    SETIBIT PortB, SegG             ;
loc61:
;--- 3 сегмент -----------------------------------------------------------------;
    LSL   Pattern                   ; сдвиг влево, следующий разряд в С
    BRCS  loc50                     ; если С = 1
    CLRIBIT PortC, SegF             ;
loc50:
    BRCC  loc51                     ; если С = 0
    SETIBIT PortC, SegF             ;
loc51:
;--- 4 сегмент -----------------------------------------------------------------;
    LSL   Pattern                   ; сдвиг влево, следующий разряд в С
    BRCS  loc40                     ; если С = 1
    CLRIBIT PortC, SegE             ;
loc40:
    BRCC  loc41                     ; если С = 0
    SETIBIT PortC, SegE             ;
loc41:
;--- 5 сегмент -----------------------------------------------------------------;
    LSL   Pattern                   ; сдвиг влево, следующий разряд в С
    BRCS  loc30                     ; если С = 1
    CLRIBIT PortC, SegD             ;
loc30:
    BRCC  loc31                     ; если С = 0
    SETIBIT PortC, SegD             ;
loc31:
;--- 6 сегмент -----------------------------------------------------------------;
    LSL   Pattern                   ; сдвиг влево, следующий разряд в С
    BRCS  loc20                     ; если С = 1
    CLRIBIT PortC, SegC             ;
loc20:
    BRCC  loc21                     ; если С = 0
    SETIBIT PortC, SegC             ;
loc21:
;--- 7 сегмент -----------------------------------------------------------------;
    LSL   Pattern                   ; сдвиг влево, следующий разряд в С
    BRCS  loc10                     ; если С = 1
    CLRIBIT PortC, SegB             ;
loc10:
    BRCC  loc11                     ; если С = 0
    SETIBIT PortC, SegB             ;
loc11:
;--- 8 сегмент -----------------------------------------------------------------;
    LSL   Pattern                   ; сдвиг влево, следующий разряд в С
    BRCS  loc00                     ; если С = 1
    CLRIBIT PortC, SegA             ;
loc00:
    BRCC  loc01                     ; если С = 0
    SETIBIT PortC, SegA             ;
loc01:


;*******************************************************************************;
; 4. Зажигается следующий разряд;                                               ;
;*******************************************************************************;
    MOV   TmpDyn, Counter           ;
    ANDI  TmpDyn, 0b00000111        ; не более 8 разрядов
    LSL   TmpDyn                    ; Команда занимает 2 слова
    LDIDX Z, (LocDs0)               ; адрес таблицы команд
    ADIDX Z, TmpDyn                 ; адрес команды в таблице
    IJMP                            ; переход на команду
LocDs0:
;--- Разряд 1 ------------------------------------------------------------------;
    SETIBIT PORTB, Rat1             ;
    RJMP  LocDs1                    ;
;--- Разряд 2 ------------------------------------------------------------------;
    SETIBIT PORTD, Rat2             ;
    RJMP  LocDs1                    ;
;--- Разряд 3 ------------------------------------------------------------------;
    SETIBIT PORTD, Rat3             ;
    RJMP  LocDs1                    ;
;--- Разряд 4 ------------------------------------------------------------------;
    SETIBIT PORTD, Rat4             ;
    RJMP  LocDs1                    ;
;--- Разряд 5 ------------------------------------------------------------------;
    SETIBIT PORTD, Rat5             ;
    RJMP  LocDs1                    ;
;--- Разряд 6 ------------------------------------------------------------------;
    SETIBIT PORTD, Rat6             ;
    RJMP  LocDs1                    ;
;--- Разряд 7 ------------------------------------------------------------------;
    SETIBIT PORTD, Rat7             ;
    RJMP  LocDs1                    ;
;--- Разряд 8 ------------------------------------------------------------------;
    SETIBIT PORTD, Rat8             ;
LocDs1:

  Программа может управлять индикаторами и с общим анодом и с общим катодом. Достигнуто это с помощью средств условной компиляции препроцессора ассемблера. В программе определен дефайн и два макроса, в случае компиляции для индикатора с ОА дефайн нужно раскоментировать:

;#define Common_Anode_Display    ; дисплей с ОА, инверсное управление
;
.MACRO  SETIBIT                 ; установка бита в порту
#ifdef Common_Anode_Display
    CBI   @0, @1                ; инверсная
#else
    SBI   @0, @1                ; прямая
#endif
.ENDM
;
.MACRO  CLRIBIT                 ; сброс бита в порту
#ifdef Common_Anode_Display
    SBI   @0, @1                ; инверсный
#else
    CBI   @0, @1                ; прямой
#endif
.ENDM

  Чаще всего сегменты индикатора подключают к портам контроллера руководствуясь удобством разводки печатной платы. Для отображения семисегментного разряда с десятичной точкой достаточно байта. Каждый сегмент и точка будут соответствовать своему биту.
  Для удобства подбора сегментов и соответствующих им битов в программе применен следующий способ определения кодов символов.

;==== Битовые маски для дисплея ================================================;
;
;            a
;            --
;          f|  |b
;           g--
;          e|  |c
;            -- .h
;            d
;
.EQU    Seg_A       = 0b00000001 
.EQU    Seg_B       = 0b00000010
.EQU    Seg_C       = 0b00000100 
.EQU    Seg_D       = 0b00001000 
.EQU    Seg_E       = 0b00010000
.EQU    Seg_F       = 0b00100000
.EQU    Seg_G       = 0b01000000 
.EQU    Seg_H       = 0b10000000 
;
.EQU    CH_0        = Seg_A | Seg_B | Seg_C | Seg_D | Seg_E | Seg_F
.EQU    CH_1        = Seg_B | Seg_C
.EQU    CH_2        = Seg_A | Seg_B | Seg_D | Seg_E | Seg_G
.EQU    CH_3        = Seg_A | Seg_B | Seg_C | Seg_D | Seg_G
.EQU    CH_4        = Seg_B | Seg_C | Seg_F | Seg_G
.EQU    CH_5        = Seg_A | Seg_C | Seg_D | Seg_F | Seg_G
.EQU    CH_6        = Seg_A | Seg_C | Seg_D | Seg_E | Seg_F | Seg_G
.EQU    CH_7        = Seg_A | Seg_B | Seg_C
.EQU    CH_8        = Seg_A | Seg_B | Seg_C | Seg_D | Seg_E | Seg_F | Seg_G
.EQU    CH_9        = Seg_A | Seg_B | Seg_C | Seg_D | Seg_F | Seg_G
;
.EQU    CH_v        = Seg_C | Seg_D | Seg_E
;
TBL_DIGITS:
  .DB     CH_0, CH_1, CH_2, CH_3, CH_4, CH_5, CH_6, CH_7, CH_8, CH_9  ; 0-9

Отладка в Proteus
  К студии 4.19 легко подключить в качестве отладчика Proteus. Это позволяет подавать на схему внешние сигналы и тут же считывать их программой. Или подавая программно сигналы на ножки увидеть их последовательность на цифровом графике, измерить их длительность. Пример того как симулятор показывает сигналы на разрядах дисплея при динамической индикации, эдесь Rat1..Rat8 — разряды, видно что задействовано семь разрядов, восьмой не используется:



  Сигналы Tst0, Tst1, Tst2 — это исполнение подпрограмм BINtoPackBCD, PackBCDtoUnpackBCD, UnpackBCDtoSymbolsCode соответственно. На графике они видны черточками, т.к. время их исполнения гораздо меньше цикла 1 сек. Proteus позволяет как угодно раздвинуть график и замерить время исполнения.



Левый (зеленый) курсор ставится левой кнопкой мышки, правый (красный) — той же кнопкой при нажатом CTRL. Внизу в окошке видно время исполнения подпрограммы BINtoPackBCD — 192 мкс.

Идентификатор, дата создания прошивки

Сразу после таблицы прерываний в программе расположен идентификатор.

.ORG INT_VECTORS_SIZE
;------ Идентификатор ----------------------------------------------------------;
Ident:                              ;
    .DB  0, "anakost", 0, __DATE__, 0, CH_v, CH_1 | Seg_H, CH_0, 0
.EQU  iSize = (PC - Ident)*2        ; размер идентификатора (в байтах)
.EQU  vSize = 3                     ; размер информации о версии
;

  В нем расположен копирайт, в качестве которого использован ник автора, и дата компиляции прошивки. В НЕХ файле этот идентификатор не видно, т.к. НЕХ это ASCII представление двоичного файла. Зато в бинарном файле этот идентификатор легко найти невооруженным взглядом:



  Здесь есть одна тонкость. По умолчанию AVRStudio 4.19 формирует идентификатор даты в виде строки «Jan 29 2015». Это не очень совпадает с российским представлением времени. Чтобы это исправить необходимо установить ISO формат даты в препроцессоре ассемблера. Тогда формат даты примет вид DD.MM.YYYY, где: DD — число, MM — месяц, YYYY — год.
Сделать это несложно, находим меню «Project -> Assembler Options» и открываем настройки ассемблера. В поле «Additional Parameters» вписываем ключ командной строки -FD"%%d.%%m.%%Y". Подробнее в хелпе AVRStudio.

  В конце идентификатора расположен номер версии прошивки, в данном случае «v1.0». Версия выводится первые две секунды работы программы, символы представлены в семисегментной кодировке дисплея. Две секунды потому, что в первую секунду результата измерений еще нет, показывать нечего. Интереснее со второй секундой. Дело в том, что генератор с низкочастотным «часовым» кварцем 32768 Гц довольно долго (по компьютерным меркам) выходит на стабильный рабочий режим. Из-за этого первое измерение после запуска будет неверным. Поэтому в программе оно пропускается, отображение результатов начинается со второго.

О выборе регистра флагов для флагового автомата.

  В данной программе для регистра флагов выбран регистр из старшей половины адресов. К сожалению такая возможность есть не всегда. Почему именно из старшей половины, можно ли использовать из младшей? К сожалению команды SBR, CBR работают только с регистрами из старшей половины.
  Какие еще регистры можно отдать под флаги автомата? Любые из области ввода/вывода. Главное, чтобы их адрес был не более 0х1F (ограничение команд SBI, CBI), они допускали битовую адресацию и не использовались в устройстве.
  Я практически не использую блок TWI, и его регистры (TWAR-адреса, TWBR-скорости передачи) идеально подходят для данной цели. Регистр управления TWCR лучше не использовать, т.к. его изменение может включить блок TWI, что приведет к бесконтрольному изменению содержимого других регистров TWI. К счастью регистр управления TWCR и невозможно использовать для флагового автомата, т.к. его адрес 0х36 и команды SBI, CBI с ним работать не будут. С регистром данных TWDR будут, но его лучше не использовать из других соображений, при включении питания все биты будут установлены в 1 и переустановить их можно будет только после первого прерывания. С регистром состояния TWSR тоже будут, но пять битов этого регистра отображают внутреннее состояние блока TWI, один недоступен, для програмирования доступны только два бита.
  Практическая замена пар команд SBI < — SBR и CBI < — CBR имеет особенность, команды SBR и CBR принимают в качестве аргумента не номер бита а битовую маску. Команды SBI и CBI принимают в качестве аргумента как раз номер бита, об этом надо помнить. Пары команд SBRSSBIS и SBRСSBIС все принимают в качестве аргумента номер бита, замена возможна изменением одной буквы. Если это важно, команды SBR и CBR в два раза быстрее (1 цикл) команд SBI и CBI (2 цикла).
Учтите также что переименование регистров I/O производится не через .DEF, а через .EQU или .SET, примерно так:

.EQU    BITx1       = TWAR	    	; Битовый регистр

Продолжение будет.

  Оно будет посвящено работе данной программы с различными контроллерами дисплея. Хотя данное устройство задумывалось как самодостаточное, меня заставил задуматься выхлоп компилятора:



  Обидно же, память свободна, а почти все ножки (кроме ISP) заняты. Конечно первое что приходит в голову, применить аналог ATMega8A в корпусе с большим кол-вом ножек. В данном случае прекрасно подойдет ATMega8535. Главное, наличие асинхронного таймера 2 (например в ATMega8515 он отсутствует).
  Другой вариант, вместо динамической индикации применить статическую с внешним контроллером дисплея (как в оригинале). Об этом во второй части.

В приложенном архиве FreqDyn.zip проекты AVR Studio и Proteus. Подробнее файл Readme внутри.
  • +6
  • 23 декабря 2017, 12:22
  • anakost
  • 1
Файлы в топике: FreqDyn.zip

Комментарии (179)

RSS свернуть / развернуть
Понравилась идея использовать часовой кварц с асинхронным таймером, обычно в таких вещах тыкают кварц на основную тактовую частоту… А часовой покомпактнее, меньше жрёт и вроде даже точнее.
Меня в очередной раз радует AVR, где от часового кварца работает полноценный таймер, на котором можно сделать что угодно. А не тупо RTC-календарь как во всяких пафосных стм-ках (который, кстати, только мешает, т.к. время в системе всё равно необходимо в нормальном формате скалярного счётчика секунд)…
0
>> вроде даже точнее.
У меня нет таких данных, здесь представлен не полноценный измерительный прибор, цифровая шкала — это скорее показометр, в данном случае достаточно точный…
0
Обычно часовые кварцы идут на 20ppm, высокочастотные — на 30ppm. Можно, конечно, найти более точные, но с вероятностью 99,9% в радиолюбительской конструкции будут стоять самые распространённые стандартные…
Думаю, цифровая шкала вполне может быть измерительным прибором если оценить и указать её погрешность.
0
А часовой покомпактнее, меньше жрёт и вроде даже точнее.
Это бы имело смысл, если бы индикация была не на светодиодный дисплей, а данные таймера захватывались аппаратно, а не в прерывании.
Да и дешевые часовые кварцы та еще гадость с точностью порядка 10^4.
т.к. время в системе всё равно необходимо в нормальном формате скалярного счётчика секунд
Для этого в пафосных стм-ках есть SysTick.
0
Это бы имело смысл, если бы индикация была не на светодиодный дисплей, а данные таймера захватывались аппаратно, а не в прерывании.
Мне нравится идея использовать часовой кварц для точных интервалов времени, детали реализации уже не суть. Можно и на прерывании точно считать, если оно единственное — никакого джиттера не будет.
Да и дешевые часовые кварцы та еще гадость с точностью порядка 10^4.
Дешёвые — это из китайских часов? Хотя 10^-4 — это 9 секунд в сутки, такое найти ещё надо сильно постараться… Кварцы, купленные в магазине или снятые с материнки вполне дают 20ppm.
Для этого в пафосных стм-ках есть SysTick.
Теряюсь в догадках какое он вообще отношение может иметь к подсчёту реального времени.
0
Хотя 10^-4 — это 9 секунд в сутки, такое найти ещё надо сильно постараться…
Нет, 10^-4 это 40с в сутки, погрешность самых дешманских китайских часов. Хотя, есть у меня подозрение, что это вообще в чипе косяк. Больно единодушно такие часики косят на 40с, даже в советском исполнении (были у меня и такие, да, электроника что-то-там).
На материнке кварцы явно приличней.
Мне нравится идея использовать часовой кварц для точных интервалов времени, детали реализации уже не суть.
Можно хоть атомный стандарт времени взять, если у тебя непредсказуемая задержка входа в прерывание — толку от него не будет.
Сколько помню частотомеров — часовой кварц они не используют, используют точный ВЧ кварц в термостатированной камере, либо более точный источник времени.
Теряюсь в догадках какое он вообще отношение может иметь к подсчёту реального времени.
Мы таки о RTC или о счетчике секунд с запуска?
0
Твои 40с нехарактерны. Такие часы практически невозможно использовать. Типичная ошибка — до пары секунд. В советских часах обычно есть крутилка — подстроечный конденсатор, настроенный на заводе. У таких часов ошибка порядка полсекунды.
если у тебя непредсказуемая задержка входа в прерывание
В AVR вход в прерывание — 4 такта (не 12, как во всякой хрени) плюс задержка, которую может внести атомарный блок или другое прерывание. В принципе, при необходимости влияние и того и другого можно уменьшить до нескольких тактов или вообще исключить…
Мы таки о RTC или о счетчике секунд с запуска?
Причём тут секунды с запуска? прочитай внимательно фразу, которую ты комментишь. Речь про аппаратный RTC-календарь, дурной пережиток, от которого только гемор, который нафиг не нужен. А нужен вместо нормальный монотонный счётчик секунд с начала эпохи. Впрочем, что касается SysTick, для вещей наподобие GetTickCount() он тоже мало пригоден.
0
Я прям теряюсь, всю эту хрень написал VGA в мой адрес, вы кому отвечаете?
0
Под сообщениями стрелочки, позволяющие отследить, кто кому отвечал.
0
Твои 40с нехарактерны.
Напротив, крайне характерны. Можешь сам проверить — поищи самые дешманские наручные часы с дисплеем HH:MM, двумя кнопками и без каких-либо допфункций типа будильника — только часы и календарь.
Но действительно, кроме них настолько кривые часы встречаются редко, что наводит на мысль о том, что это таки косяк чипа.
Такие часы практически невозможно использовать.
Так и есть, ага.
В советских часах обычно есть крутилка — подстроечный конденсатор, настроенный на заводе. У таких часов ошибка порядка полсекунды.
«Электроника» много часов спиздила производила. Были и без какой-либо подстройки вообще, но с адекватной точностью (содранные оттуда же, откуда содрана ностальгичная «Montana»), были и с кондером, были и с цифровой подстройкой кода. А были и с ошибкой в 40с в день.
В AVR вход в прерывание — 4 такта (не 12, как во всякой хрени) плюс задержка
Важна не абсолютная длительность (на нее можно внести поправку — как и на погрешность кварца, кстати), а ее детерминированность.
(не 12, как во всякой хрени)
Которая, впрочем, за эти 12 тактов (на втрое большей частоте, так что реального времени они заняли столько же) уже успела регистры схоронить, а не только PC передвинуть.
Причём тут секунды с запуска? прочитай внимательно фразу, которую ты комментишь.
Она в достаточной степени неоднозначна. Так что я проинтепретировал ее так, как проинтерпретировал.
А нужен вместо нормальный монотонный счётчик секунд с начала эпохи.
Это который переполняется в 2038-м и к которому нужно примерно полкило кода на декодирование его в пригодный для вывода на дисплей формат? В системе он не особо нужен даже при использовании ФС, в ФАТ32 все равно таймштамп в другом формате. А для отсчета интервалов, где и удобно иметь время в виде счетчика — во-первых, точка отсчета времени не важна, а во-вторых — желательно иметь разрешение получше, чем секунда.
Впрочем, что касается SysTick, для вещей наподобие GetTickCount() он тоже мало пригоден.
Ну GTC-то в винде на систике и сделан, на самом деле.
0
Напротив, крайне характерны. Можешь сам проверить — поищи самые дешманские наручные часы
Имеют место взаимоисключающие параграфы. «Характерны» и «поищи». У меня дома с десяток часов и я не могу найти такие, которые нужно было бы подводить чаще чем раз в несколько месяцев.
Важна не абсолютная длительность (на нее можно внести поправку — как и на погрешность кварца, кстати), а ее детерминированность.
Ну вот я и написал про детерминированность как раз после слов «плюс задержка», на которых ты отсёк цитату.
уже успела регистры схоронить, а не только PC передвинуть.
И что с того. Регистров море, ничего не мешает несколько отдать прерыванию, чтобы ничего не сохранять. Да и есть множество прерываний, состоящих из одного SBI/CBI, но которые должны выполниться как можно скорее.
Это который переполняется в 2038-м и к которому нужно примерно полкило кода на декодирование его в пригодный для вывода на дисплей формат? В системе он не особо нужен даже при использовании ФС, в ФАТ32 все равно таймштамп в другом формате. А для отсчета интервалов, где и удобно иметь время в виде счетчика — во-первых, точка отсчета времени не важна, а во-вторых — желательно иметь разрешение получше, чем секунда.
Время нужно иметь в виде счётчика, с которым можно работать. Вычислить интервал, сделать коррекцию хода, сменить таймзону, компактно сохранить и т.д. В формате RTC всё это дикий гемор. Даже атомарно загрузить/сохранить время в RTC гемор. Для отображения нужна качественная библиотека, которая переводит системный формат в любое нужное представление, а всякие аппаратные календари — перекладывание с больной головы на здоровую.
0
«Характерны» и «поищи».
Характерны для определенной модели начинки. Я не удивлен, что у тебя в доме нет ни одного образца этого дерьма, хотя и удивлен, что ты с ним не сталкивался. У меня таких с десяток побывало минимум. Могу даже прислать образец, китайцы их до сих пор делают.
Приличные часики, кстати, скорее всего калибруются на заводе, где выбирается погрешность в те самые 20ppm и остается только долговременная стабильность кварца.
Ну вот я и написал про детерминированность как раз после слов «плюс задержка», на которых ты отсёк цитату.
Там было «влияние которых можно уменьшить до нескольких тактов». Что уже недетерминированность сравнимая по порядку с нестабильностью кварца.
Регистров море, ничего не мешает несколько отдать прерыванию, чтобы ничего не сохранять.
Сильно зависит от задачи. И только при условии что оно такое одно… В ARM7 было FIQ с очень быстрым входом (и при этом без необходимости сохранять регистры) — видимо, оказалось что оно такое не нужно.
И ты уверен что имеенно с этим явлением нужно бороться хранением времени формате RTC, а не time64_t?
Зависит от задачи. Аппаратные RTC, причем далеко не только STM'овские (они, по сути, просто встроили в МК типичный аппаратный RTC — попутно забыв главную фичу, резервное питание) обычно хранят именно в таком формате — видимо, на то есть причины.
0
Там было «влияние которых можно уменьшить до нескольких тактов». Что уже недетерминированность сравнимая по порядку с нестабильностью кварца.
Чтобы несколько тактов (абсолютную ошибку) сравнивать с нестабильностью кварца (которая в долях), неплохо бы сперва привести их к общему знаменателю, а то сравнивание малость некорректное…
У anakost-а интервал секунда, тактовая частота 8 МГц, хандлеры явно уложатся тактов в 20. Это даёт ошибку в 5ppm. С нестабильностью кварца оно в принципе сравнимо, но слабым местом оказывается всё таки кварц.
Сильно зависит от задачи.
Ну вот я и хочу чтобы в зависимости от задачи можно было выбирать — тратить такты на сохранение или нет.
И только при условии что оно такое одно…
Вовсе не обязательно. Можно прерывания делать неблокирующими. Можно выделять окна времени. Иногда бывает такое что прерывание не одно, но вызваться одновременно они никак не могут.
видимо, на то есть причины
Если и есть причины хранить время в чудовищно неудобном формате в котором чудовищно геморно его хотя бы сравнить, да и вообще что-то сделать, то причины эти явно не забота о пользователе…
0
С нестабильностью кварца оно в принципе сравнимо, но слабым местом оказывается всё таки кварц
Ну, во-первых, 20-30-50ppm — это точность частоты кварца, а не стабильность. Стабильность выше, для нетермостатированного кварца она ЕМНИП приближается к 10^-6, т.е. единицы ppm.
Во-вторых, в корне я писал, что оно сравнимо с разницей между кварцами.
Ну вот я и хочу чтобы в зависимости от задачи можно было выбирать — тратить такты на сохранение или нет.
Я бы тоже предпочел иметь возможность выбора, но ее нет ни там, ни там.
Можно прерывания делать неблокирующими.
Вот как раз неблокирующие прерывания проблему «на всех регистров не напасешься» только усугубят. С блокирующими можно хотя бы временные регистры иметь общими на все прерывания.
Если и есть причины хранить время в чудовищно неудобном формате
Видимо, не для всех задач он «чудовищно неудобен». ПЛК, кстати, тоже хранят время в таком виде, насколько я помню статьи топикстартера.
0
Это который переполняется в 2038-м
И ты уверен что имеенно с этим явлением нужно бороться хранением времени формате RTC, а не time64_t (который, кстати, давным-давно уже используется)?
0
Ну GTC-то в винде на систике и сделан, на самом деле.
Потому разрешение его — убогие 15мс с копейками… Для МК это никуда не годится.
0
Потому что в винде систик настроен на такую частоту. В ARM ЕМНИП килогерц по дефолту и ЕМНИП настраивается.
Для МК это никуда не годится.
Ну, уже лучше чем 1000мс разрешения unix time.
0
Ну, уже лучше чем 1000мс разрешения unix time.
Ты опять отвечаешь не на коммент, а на то что сам хочешь. Причём тут юникстайм? GTC — это счётчик времени с запуска и комент был о том что систик для его реализации тоже годится хреново. Систик — это прерывание, которое железо дёргает через определённые интервалы времени и в котором ОС решает свои дела. А для GTC хотелось бы чего то попроще, чтобы счётчик инкрементился аппаратно…
0
А для GTC хотелось бы чего то попроще, чтобы счётчик инкрементился аппаратно…
Это, в принципе, и так в любом МК с таймером общего назначения есть. Но для них обычно находятся более важные задачи.
0
А для GTC хотелось бы чего то попроще, чтобы счётчик инкрементился аппаратно…

Справедливости ради, STM32 здесь дает вам сразу несколько вариантов.

Часовой кварц точный, но скорость маловата, соответственно, разрешение системного счетчика будет всего около 30.5 мкс. Иногда надо лучше. Не проблема! Можно сделать программный ремап входа захвата TIM14 на выход часового генератора и просто померять системную частоту относительно часового кварца. После этого можно откалибровать системное тактирование, получив высокоскоростной сигнал с точностью часового. Разумеется, калибровку можно повторять регулярно. Или только при изменении температуры (что можно отследить по встроенному термодатчику). Причем отслеживать можно даже аппаратно — АЦП (который выполняет опрос термодатчика) поддерживает автоматическое определение выхода преобразованного значения за установленные границы… Так что можно установить диапазон температур, при выходе за который прерывание будет запускать процедуру перекалибровки системного тактирования по часовому кварцу.

С другой стороны, если повышенного разрешения не требуется, можно аппаратно подключить выход MCO ко входу почти любого (кроме самых простых из имеющихся внутри) аппаратного таймера и тактировать этот таймер от него. Для MCO можно выбрать любой внутренний сигнал тактирования, и LSE (часовой кварц) в том числе. Так что аппаратный счетчик получается легко и непринужденно.

Все это вы можете почерпнуть из даташита на, скажем, STM32F030F4, который достаточно близок по характеристикам к серии ATmega.
+1
Хороший коммент. Редко от пользователей стм можно увидеть хоть что-то по делу…

Идея с калибровкой мне нравится, если конечно разрешения калибровки хватает для приличной точности…

можно аппаратно подключить выход MCO ко входу почти любого (кроме самых простых из имеющихся внутри) аппаратного таймера
Снаружи соединять ножки — можно, конечно. Но как-то это не особо изящно и непринуждённо. Что им мешало завести выход LSE на какой-нибудь таймер? Есть множество задач где это может быть полезно. Дурацкий RTC прилепили, а простых и нужных вещей не сделали, даже еепрома нет.

который достаточно близок по характеристикам к серии ATmega
Смотря по каким характеристикам… Скажем, по характеристики «количество страниц ерраты» они весьма далеки.
0
Что им мешало завести выход LSE на какой-нибудь таймер?
Судя по блок-схеме, MCO заведен на TIM14. Не уверен правда, может ли он его считать, или это только input capture.
0
Только input capture.
0
Ну, использование контроллеров от ST не всегда означает, что человек не уходит дальше STMxxCube. :D

Вообще, я по мере необходимости использую AVR, STM8, MSP430 и STM32 (на работе есть проект даже на ATXmega, и один раз запускал ARM от Atmel, после чего понял, почему их почти не используют). Чаще других применяю AVR и STM32. Реже всего — MSP430 (больно они дорогие). STM8 тоже хороши (в основном ценой, конечно), но под них нет нормального компилятора. Cosmic еще так-сяк, но там непойми что с лицензией, а SDCC — это полная печаль; за IAR хотят денег, много. К GCC соответствующий кодогенератор пока не прикрутили.

Так вот. Самый печалящий меня реальный недостаток STM8/32 — отсутствие нормального встроенного опорного источника (тот, который есть, нельзя использовать как опору АЦП — его можно только мерять!). В этом плане AVR они не конкуренты. Еще AVR безусловно выигрывают по токам GPIO — до 40 мА на ножку, до 200 мА на корпус для ATmega48, например. Так что какой-нибудь вольтметр с семисегментными индикаторами естесственно делать именно на AVR — это сразу минус два корпуса (опорный источник и драйвер дисплея).

Конкретно ATtiny13 — вообще чип-чемпион по соотношению параметров «цена-корпус-возможности». Аналогов ему у ST я не видел.

Что в STM безусловно хорошо — это таймеры. Аппаратная поддержка инкрементального энкодера (очень, очень удобно), комплементарные выходные каналы с настраиваемым защитным интервалом (deadtime), всякие возможности типа аппаратного отключения каналов по внешнему сигналу и так далее.

Очень мощная штука DMA. Самый близкий к народу пример, где DMA приносит ощутимую пользу — управление светодиодами типа WS2812. Без DMA это дико грузит процессор, с DMA и таймером процессор практически простаивает. Конкретных цифр сейчас уже не вспомню и перемерять лень, но помню, что я был потрясен разницей.

если конечно разрешения калибровки хватает для приличной точности…

32 бита же. :)

Что им мешало завести выход LSE на какой-нибудь таймер?

Вот ХЗ, на самом деле. Так правда было бы красивее, но, в принципе, дорожка на печатной плате ничего не стоит.

Дурацкий RTC

Я бы, к слову, не сказал, что он такой уж дурацкий. Если нужны именно часы — очень удобно. Кстати, модуль RTC там поддерживает пробуждение контроллера по часам и еще несколько возможностей такого сорта, ну а точки во времени удобнее указывать в человекопонятном формате, как минимум, лично мне. Ну а как сделать счетчик аптайма, мы уже разобрались.

Кстати, в состав модуля RTC входит область SRAM, питаемая от батареи, которая может быть сброшена по сигналу с определенной ножки. Удобно для хранения, скажем, ключей шифрования или еще какой-то информации, которую лучше уничтожить при обнаружении физического доступа к устройству.

И главное: модуль RTC не везде одинаковый. Например, в STM32F100 он именно что представляет собой тридцатидвухбитный счетчик, тактируемый через конфигурируемый делитель от линии RTCCLK (ее можно подключить к нескольким источникам). Это то, что вам требуется? :)

даже еепрома нет

Они фон-Неймановские, можно писать прямо во FLASH, в любое место, куда понравится. Так что EEPROM теряет смысл. Кстати вот в STM8 он есть.

по характеристики «количество страниц ерраты» они весьма далеки.

Справедливости ради, по сложности некоторых перферийных блоков — тоже. :) Сложнее железо, больше мест для ошибки.
+2
Кстати, в состав модуля RTC входит область SRAM, питаемая от батареи
О, там есть батарейный домен? В F100 это больше всего удивляло в RTC — оно есть, но его нельзя запитать от батарейки! Как так-то?!
Так что EEPROM теряет смысл.
Если не требуется часто писать.
Только input capture.
Остается только вопрос — нафига каптурить MCO…
0
Не, ну в LQFP64 у F100 есть пин VBAT.

Нене, input capture у TIM14 именно RTCCLK.
0
Не, ну в LQFP64 у F100 есть пин VBAT.
Уже вижу, видимо тогда я смотрел не в тот корпус.
Нене, input capture у TIM14 именно RTCCLK.
В STM32F030/070, по рефману, есть линия с MCO на вход TIM14 (посмотри внимательно блок-схему RCC). Куда она заведена — вопрос мутноватый, но похоже — на вход.
0
На TI1 оно заведено. Это input capture. Ну а MCO можно выбрать. Видимо, сделали потому, что могли.
0
Конкретно ATtiny13 — вообще чип-чемпион по соотношению параметров «цена-корпус-возможности». Аналогов ему у ST я не видел.
tiny13 — супер. Можно ещё глянуть tiny5/10 — похожи на tiny13, но на борту большой 16-битный таймер, с которого можно брать двухфазный сигнал, менять его частоту и коэффициент заполнения в широких пределах (причём коэффициент заполнения — это не дедтайм, его можно менять от 0 до 100/50%). Правда программирование TPI, а не SPI. Но собрать программатор не проблема, протокол описан в даташите.

Очень мощная штука DMA.
Да, STM32 несмотря на всю свою уродливость вывозят за счёт быстрых команд, тактовой частоты и DMA. Правда и здесь есть по хорошему ушату помоев. В DMA — реквесты фиксировано замаплены на несколько источников, и включив один источник остальные уже использовать нельзя. Только в F3+ исправлено. Что до быстрого ядра — впечатление портит тормозной флеш. SRAM тоже не решает проблему — её мало и к ней нужно обращаться за данными (в F3+ правда есть немножко CCM, да). Также очень большой минус — кривой и неудобный ассемблер. Без ассемблера не раскроешь мощь ядра по полной, что бы там не вопили любители ЯВУ.

Я бы, к слову, не сказал, что он такой уж дурацкий. Если нужны именно часы — очень удобно.
Я уже сделал простые часики, если буду делать ещё — будет синхронизация по NTP и ещё что-нибудь. Да и в простых была коррекция хода, прилепить её к RTC геморно. Да и вообще подход с календарным RTC — отстой. В технике время — это линейно нарастающая скалярная величина, именно так с ней работать удобно и правильно. Астрономическое время нужно только для ввода-вывода в интерфейсах, именно там оно и должно преобразоваываться в/из этот формата. ДОС хранил астрономическое время, но от того давно отказались. В линуксе используется скаляр time_t (который правда должен перепониться в 2032-м, но все давно уже перешли на time64_t). В винде время изначально 64 бита, причём разрешение аж наносекунда. Но СТ зачем-то решили выкопать труп древнего календарного RTC.

Кстати, в состав модуля RTC входит область SRAM, питаемая от батареи, которая может быть сброшена по сигналу с определенной ножки.
Насколько я помню еррату, оно не работает. По крайней мере, в тех семействах, которые я смотрел (F0, F4). То ли сбрасывается самопроизвольно, то ли не сбрасывается когда надо…

И главное: модуль RTC не везде одинаковый. Например, в STM32F100 он именно что представляет собой тридцатидвухбитный счетчик, тактируемый через конфигурируемый делитель от линии RTCCLK (ее можно подключить к нескольким источникам). Это то, что вам требуется? :)
Это безусловно плюс. Хотя именно на мелких сериях календарный RTC может быть хоть как-то оправдан (примитивные чясики без ЦКХ?), но календарный RTC стоит как раз на сериях пожирнее. Который в соответствующих жирных задачах любом случае придётся конвертировать в нормальный скаляр при чтении и в календарь при записи. И ещё ломать голову как сделать это атомарно…

Они фон-Неймановские, можно писать прямо во FLASH, в любое место, куда понравится. Так что EEPROM теряет смысл.
Флешка стирается только страничками. Что уже не очень удобно само по себе. И ресурс у флеша меньше в 10 раз, а переписывать нужно большую страницу на любое изменение.

STM8 тоже хороши (в основном ценой, конечно)
Вот именно, что лишь ценой. В остальном одно уныние. И ядро и периферия и элекртические параметры и как всё это сделано. Посмотреть хотя бы на время выполнения команд и количество регистров, какой-то простенький пик напоминает. Нет моих любимых умножений с длинным результатом. Это только один пример из большого множества.
0
причём коэффициент заполнения — это не дедтайм, его можно менять от 0 до 100/50%

Не, ну в том таймере, о котором я говорил, коэффициент заполнения, естесственно, тоже можно менять. :D Я об этом не писал как об очевидном.

Кстати защитный интервал там тоже можно настраивать по вкусу. А еще полярность выходов и много-много всего.

Правда программирование TPI, а не SPI.

Да, именно поэтому я до этих прикольных чипов пока что так и не добрался. Хотя AVRISP-mkII поддерживает его, вроде бы. А еще они больно дорогие в местных магазинах… Стоили бы они по 30 — 50 р., как серия F030, было бы интереснее.

Да, STM32 несмотря на всю свою уродливость

Здесь есть нюанс. Если вы вчитаетесь в документацию на разные серии (да хотя бы F100 и F0xx), вы сразу поймете, что это по большей части разные контроллеры. Разгадка проста — их делали разные люди. Мне рассказывали инсайдерскую информацию насчет того, что после F100 в ST просто разогнали дизайн-отдел и набрали его заново.

Так что документацию на STM32 надо читать ОЧЕНЬ внимательно. Сколько бы ни кричали представители ST и их адепты про фантастическое единство и полную взаимозаменяемость чипов всей линейки, это неправда. Серия STM32 на деле состоит из достаточно разных чипов, и это надо учитывать.

Ну и да, в некоторых местах документация не блещет прозрачностью, что есть, то есть…

Также очень большой минус — кривой и неудобный ассемблер. Без ассемблера не раскроешь мощь ядра по полной, что бы там не вопили любители ЯВУ.

Здесь, опять же, есть нюанс. Для чипов на ARM надо применять немного другую парадигму разработки. Лезть руками в тамошний ассемблер нет особого смысла уже потому, что за счет системы предвыборки команд из FLASH (ART accelerator), серьезного конвейера и out-of-order execution время выполнения команд и очередность доступа к памяти (кроме выделенных областей), вообще говоря, непредсказуемы (так что, например, писать на ассемблере Cortex-M3 протокол 1-Wire я бы не стал). Ну, если только не ставить после каждой инструкции data memory barrier.

Так что здесь действительно правильно оставить ручную генерацию ассемблерного кода и просто надеяться, что компилятор и система выполнения кода выжмут максимум. Чаще всего оно так и будет.

Сила этих чипов — мощная периферия, которая может делать многое просто сама по себе (как, например, АЦП, автоматически проверяющий преобразованное значение на попадание в диапазон). Потому все задачи, критичные к таймингам, надо перекладывать на нее. Возвращаясь к примеру с 1-Wire — на STM32 его логично реализовывать на базе UART. Их там много, потому один вполне можно отдать под 1-Wire, и это не ударит по ресурсам так, как это было бы в AVR, скажем.

То есть идеал — настроил периферию, она сама работает, а ядро вообще выключено.

Немного другой подход, немного другая парадигма. Непривычная, да.

В DMA — реквесты фиксировано замаплены на несколько источников, и включив один источник остальные уже использовать нельзя.

Нельзя на одном канале. На других можно. Недостающее можно ремапить.

прилепить её к RTC геморно

Конкретно в STM32F051 для этой цели есть RTC_SHIFTR, который позволяет аккуратненько двигать часы с субсекундным разрешением.

Да и вообще подход с календарным RTC — отстой. В технике время — это линейно нарастающая скалярная величина, именно так с ней работать удобно и правильно.

Ну, это все же субъективно. Удобно — это как когда. Чем тащить в контроллер библиотеку, которая будет корректно обрабатывать все это (сколько десятков килобайт она займет?), проще иметь аппаратный модуль RTC с календарем…

Насколько я помню еррату

На какой чип? В errata на STM32F100 про это я ничего не нашел.

Если говорить про F0, то там ошибка совершенно смешная. Кому может понадобиться программно выключать tamper detection, если уж ее включили? Обычно в таких случаях функционал конфигурируется раз и надолго.

И ещё ломать голову как сделать это атомарно…

Ломать голову не надо. Теневое копирование уже реализовано за вас, аппаратно.

И ядро и периферия и элекртические параметры и как всё это сделано.

Ага. До сих пор ржу с их регистра установки скорости UART (по-моему, дело было с STM8S103F3). У меня только один вопрос: ПОЧЕМУ ТАК? :D Зато они дешевые.
+1
Кстати защитный интервал там тоже можно настраивать по вкусу.
Не могу сказать что это хорошо, можно и самому ограничивать коэффициент заполнения. Больше сложность периферии — больше ошибок в ней, а пользы особой нет… Не люблю когда в железе лепят то что элементарно, без всяких издержек делается программно в пару операторов или строк просто чтоб оно было. Заканчивается это «много всего» потерей гибкости, более громоздким кодом и ерратой.

Стоили бы они по 30 — 50 р., как серия F030, было бы интереснее.
Много говорят про дешёвый стм, но не видел я чтобы они были намного дешевле. В компэле ATtiny10 — 33р, в чиде — 43р. Не так уж это много. Для радиолюбителя, собирающего несколько устройств в год для себя это даже копейками назвать нельзя. Нужно изготавливать много тысяч экземпляров чтобы эта несчастная разница в цене могла хоть в малейшей мере проявиться.

Мне рассказывали инсайдерскую информацию насчет того, что после F100 в ST просто разогнали дизайн-отдел и набрали его заново.
Это супер! Бывает в мире справедливость — хоть иногда, но гоняют тех кто делает такие вещи. Но по большей части МК остались теми же, в следующих сериях часть убожеств исправили, но с нуля всё не переделали… Им бы ещё тех кто документацию пишет разогнать, да переписать по человечески. Документация очепятками пестрит (типа TIMM1_BK1N вместо TIM1_BKIN), не говоря уж про остальное…

Так что здесь действительно правильно оставить ручную генерацию ассемблерного кода и просто надеяться, что компилятор и система выполнения кода выжмут максимум. Чаще всего оно так и будет.
Скорее всего не будет. Компиляторы неплохо справляются с тривиальными линейными кусками, там где ещё надо постараться сделать неоптимально. И неплохо вытягивают на учёте особенностей исполнения. Но когда структура кода становится маломальски разветвлённой, изучение выхлопа компилятора вызывает лишь уныние и желание всё переписать самому. С алокацией регистров компиляторы тоже не особо сильны. Это те вещи где нужно видеть картину в целом и кучка правил зашитых в компиляторы не вытягивают — тут нужна работа интеллекта. До полноценного ИИ компилятором далеко. Что касается avr-gcc, его выхлоп от любого кода, даже самого примитивного вызывает непрекращающийся фейспалм. Написанный вручную код как правило 4 и более раз быстрее и гораздо компактнее. Может отсюда у меня тяга к переписыванию критических участков на ассемблере. Впрочем, и на x86 это давало результат (и более акуратный код в случае с SIMD — не люблю мешанину Си с интринсиками, уж лучше кусочек написать на асме).

Сила этих чипов — мощная периферия, которая может делать многое просто сама по себе (как, например, АЦП, автоматически проверяющий преобразованное значение на попадание в диапазон).
Это мне тоже не особо нравится. Я бы результаты АЦП сперва через медианно-усредняющий фильтр пропустил. Тут также как с RTC — очень большая потеря гибкости. Хотя в отличие от RTC хотя бы маломальски оправданная если нужно делать быстро.

Ну, это все же субъективно. Удобно — это как когда. Чем тащить в контроллер библиотеку, которая будет корректно обрабатывать все это (сколько десятков килобайт она займет?), проще иметь аппаратный модуль RTC с календарем…
Удобно — практически всегда. Сравнить, отсортировать, вычислить интервал, компактно сохранить, передать, преобразовать в любой формат. Просто правильнее. Библиотеки есть. Функциональнасть аппаратного RTC не та, чтобы для его замены требовалась программная библиотека на десятки килобайт.

Если говорить про F0, то там ошибка совершенно смешная. Кому может понадобиться программно выключать tamper detection, если уж ее включили? Обычно в таких случаях функционал конфигурируется раз и надолго.
Не помню точно, вроде было аж две ошибки связанные с тампер-детектом. Что в F0, что в F4…

Ломать голову не надо. Теневое копирование уже реализовано за вас, аппаратно.
Но открываем еррату — и, сюрприз, оно не работает. О чём я и говорю. Бессмысленные навороты в аппаратных блоках «чтоб было» ведёт к еррате и раздутому коду. Причём без особой пользы для кого-либо.
0
Не могу сказать что это хорошо, можно и самому ограничивать коэффициент заполнения.

Вы немного не поняли. Таймер, о котором я говорю, немного более продвинут, чем вы себе представляете. :)

Ограничение коэффициента заполнения сработает, если используются два канала ШИМ. Тогда, действительно, можно следить, чтобы значения не перекрывались (и не оказывались слишком рядом).

В STM32 описанное реализуется одним каналом сравнения. То есть, коэффициент заполнения задается один, и из него получается два выходных сигнала: один как есть, а второй комплементарный, задержанный на указанное время (защитный интервал).

Такие дела.

Много говорят про дешёвый стм, но не видел я чтобы они были намного дешевле.

Вот пара примерных аналогов:

STM32F030F4 — 48 руб.
ATmega168PA-AU — 140 руб.

Почти в три раза…

Для радиолюбителя, собирающего несколько устройств в год для себя это даже копейками назвать нельзя.

В сущности, да. Тут уже дело в том, что я, э-э-э, не совсем любитель. Ну а для себя в таком случае проще использовать то, с чем и так работаешь.

Написанный вручную код как правило 4 и более раз быстрее и гораздо компактнее.

Здесь есть искушение попросить какой-нибудь стоящий пример. Скажем, ваши варианты FFT на Си и на ассемблере. Или сравнение реализаций чего-нибудь в духе LU-разложения, обращения матрицы, решения СЛАУ, свертки, на худой конец.

Я бы результаты АЦП сперва через медианно-усредняющий фильтр пропустил.

Зависит от применения. Когда я ловил резкие скачки значений с датчика тока для того, чтобы обрабатывать тиристорное защелкивание при испытаниях мс на воздействие тяжелых заряженных частиц, медианный фильтр был мне совсем не в тему…

Ну и никто не заставляет использовать именно эту фичу. Можно, скажем, с помощью циклического режима DMA складывать этак по десятку значений в память, а дальше по прерываниям half transfer/full transfer быстро их усреднять и уже тогда, в прерывании, делать вывод. Все равно экономия времени за счет того, что прерывание вызывается не после каждой выборки.

Просто правильнее.

Это не технический аргумент. :) Это, знаете ли, гуманитарии могут спокойно аргументировать, что, например, так играть Шопена, как они, «просто правильнее», потому что они так видят. :)

А у нас есть четкий критерий — объем кода. И аппаратный блок RTC с календарем его очень сокращает. Не нужен календарь? Возьмите чип без календаря! Нужен и календарь, и счетчик UNIX time? Возьмите чип с календарем и затактируйте один из счетчиков от LSE!

Проблемы ведь, на самом деле, просто нет. Каждому применению — свой чип.

преобразовать в любой формат

Вы же смотрели то видео, ссылку на которое я давал? :)

Не помню точно, вроде было аж две ошибки связанные с тампер-детектом.

Они связаны с настройкой и никак не влияют на собственно функционал. Написали просто для полноты картины.
0
Вы немного не поняли. Таймер, о котором я говорю, немного более продвинут, чем вы себе представляете. :)
Таймеры в хвалёном стм я более-менее себе представляю. Но продвинутым назвал бы, например, TC1 в ATtinyX61A. А таймеры у стм скорее вполне обычные…

В STM32 описанное реализуется одним каналом сравнения.
Не совсем описанное. Когда я говорил про ATtiny5/10, я имел в виду двухфазный сигнал, т.е. типа пуш-пулл — симметричный с регулировкой коэффициента заполнения. Не получишь его на одном канале в хвалёном стм…

Здесь есть искушение попросить какой-нибудь стоящий пример. Скажем, ваши варианты FFT на Си и на ассемблере.
Что касается AVR, я не вижу смысла писать FFT на Си, т.к. заранее знаю что получится тормозное кошмарище. Я сразу пишу на ассемблере. Си не способен работать с фикседпойнтом, на этом можно остановиться (а в хвалёном стм8 и асм не поможет). То о чём я говорю — статистика, набранная на различных случаях из практики. Написал критичный кусочек на Си, посмотрел на убожество полученного кода и время его выполнения (в AVRStudio это зачастую можно быстренько сделать в симуляторе, а в стм32 — видимо только бенчмарки в железе — скажем, в иаре в симуляторе я так и не нашёл счётчика тактов), понял что дофига не оптимально — переписал на асм, поразился результату. Запомнил что такое надо писать сразу на асме. Даже простое прерывание, скажем, системного счётчика микросекунд и миллисекунд при тактировании от кварца на 14.7456 на асме получается раза в 2.5 раза быстрее. Более заковыристые куски запросто дают 4 раза и более…

Когда я ловил резкие скачки значений с датчика тока
Кстати, банального компаратора в стм тоже не положили. Чем они думали? Иногда очень полезно для быстрой реакции на аварийные события. Скажем, в упомянутой мной ATtinyX61A компаратор даже можно использовать совместно с TC1 для защиты — видно, что люди думали прежде чем делать…

а дальше по прерываниям half transfer/full transfer быстро их усреднять и уже тогда, в прерывании, делать вывод
Кстати, эти half transfer/full transfer — то ещё убожество… Работа с буферами в DMA меня разочаровала, от хвалёного продвинутого МК я ожидал поддержку полноценного кольцевого буфера с гистерезисами заполнения.

Это не технический аргумент.
Правильную декомпозицию систему на уровни я ценю и для меня это вполне технический аргумент. Она сильно облегачает жизнь, позволяет строить систему легко и непринуждённо. Время в системе не скаляром — как раз архитектурно неверное решение… Многие простейшие и базовые действия превращаются в неотпимальный кошмар. При том, что особого выигрыша нет. В Си есть стандартная библиотека sys/time.h, которая тебе спокойно распакует time_t в struct tm. Говорить тут про объём кода смешно.

Вы же смотрели то видео, ссылку на которое я давал? :)
Догадываюсь о том чего там расскажут. Только едва ли аппаратный RTC в стм умён как библиотека на десятки килобайт.
-1
Когда я говорил про ATtiny5/10, я имел в виду двухфазный сигнал, т.е. типа пуш-пулл — симметричный с регулировкой коэффициента заполнения. Не получишь его на одном канале в хвалёном стм…

Прямо сейчас на столе у меня лежит устройство, которое генерирует именно такой сигнал на одном канале таймера 1 в STM32F030F4P6. Код инициализации (взят прямо из проекта):


    /** TIM1 setup for complementary PWM on pin PA10/TIM1_CH3 & PB1/TIM1_CH3N */
    TIM1->CCMR2=TIM_CCMR2_OC3PE | TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3M_1; //Fast PWM on TIM1_CH3
    TIM1->BDTR=TIM_BDTR_MOE | DEADTIME_CONF;
    TIM1->PSC=PWM_PRESCALER;
    TIM1->ARR=PWM_ARR;
    TIM1->CCR3=0;
    TIM1->CNT=0;
    TIM1->CCER=TIM_CCER_CC3E | TIM_CCER_CC3NE; //Enable complementary output
    TIM1->CR1=TIM_CR1_ARPE | TIM_CR1_CEN;
    TIM1->EGR=TIM_EGR_UG;


На выходе (PA10/PB1) именно push-pull сигнал. Коэффициент заполнения управляется значением TIM1_CCR3 (да, сразу для двух каналов с учетом защитного интервала).

Кстати, банального компаратора в стм тоже не положили.

Вы постоянно обобщаете применительно к каким-то абстрактным STM32, в то время как я уже не раз указывал, что это семейство негомогенно, вопреки заверениям маркетологов ST. Пожалуйста, уточняйте, о каком кристалле идет речь…

Скажем, в упомянутой мной ATtinyX61A компаратор даже можно использовать совместно с TC1 для защиты — видно, что люди думали прежде чем делать…

В STM32L152, например, компараторов целых два, и, не поверите, их тоже можно использовать для защиты:

The COMP2 output can be redirected to TIM2/TIM3/TIM4’s input capture 4 (IC4) or OCREF_CLR inputs, or to the TIM10s input capture 1 (IC1). — см. даташит.

Более того, из этих двух компараторов можно собрать оконную схему, выбрать из нескольких предустановленных уровней опорного напряжения или вообще использовать ЦАП для опоры, и т.д., и т.п.

На компараторах и таймерах рассматриваемого чипа вполне можно собрать аналог MC33063, который будет работать сам по себе, автономно от основной системы.

В Си есть стандартная библиотека sys/time.h, которая тебе спокойно распакует time_t в struct tm.

И сколько оно занимает в памяти МК?
+1
Прямо сейчас на столе у меня лежит устройство, которое генерирует именно такой сигнал на одном канале таймера 1 в STM32F030F4P6.
Увы, не понимаю за счёт чего в этом примере формируется двухфазный сигнал. На всякий случай уточню что я под ним понимаю…



Пожалуйста, уточняйте, о каком кристалле идет речь…
F1. Да, в данном случае уже исправили. Молодцы.

И сколько оно занимает в памяти МК?
Скомпилировал такую заглушку под STM32F1.
int main() {
    time_t test = 1515251301;
    gmtime(&test);
    return 0;
}
Объём test.bin вырос на 1800 байт где-то. А какая нафиг разница? Если проект — простые часики, то флеша с избытком и из-за этих несчастных полутора килобайт за лимит никак не вылезешь. Если задача хоть на миллиграмм сложнее, от конвертаций всё равно ни за что не отделаться. Любая работа со временем (кроме вывода на семисегментники в часиках) не в скаляре крайне неоптимальна. Потому и используется time_t в линупсе, FILETIME в винде…
0
Да-да, сигнал именно такой, как на картинке. Генерация такого сигнала — стандартная возможность некоторых каналов в таймерах STM32.

В том отрывке кода, который я привел, я включаю комплементарный канал (бит TIM_CCER_CC3NE), после чего просто задаю ненулевой защитный интервал (DEADTIME_CONF — дефайн, объявленный выше по тексту программы).

Ну и как бы все. Остальное стандартная настройка режима ШИМ, включение генерации сигнала (TIM1_BDTR_MOE) и так далее. Естесственно, в полной программе есть еще настройка GPIO и тактирования.

Да, действительно все так просто. И не нужны никакие STM32Cube и прочие HAL с StdPeriphLib. Просто надо внимательно читать даташит.
+1
Хм, допустим… А если я хочу простой ШИМ, с комплементарным выходом и тоже с дедтаймом, как изменится инициализация? Что определяет режим?

0
Тогда надо еще раз инвертировать комплементарный канал. Или инвертировать основной канал. Это, если мне не изменяет память, бит CCxP в регистре CCER.
0
Всё это не очень понятно… Включаю Fast PWM, комплиментарный выход, настраиваю дедтайм и оно сразу выдаёт двухфазный сигнал? Т.е. нормальный пуш-пулл как TL494? А какую роль играет дедтайм?



А потом включаю инверсию одного канала и оно превращается в простой однофазный ШИМ (как и ожидается при настройке в режиме простого ШИМ)?



Странное поведение. В даташите я даже намёка на подобное не вижу…
0
Вы предлагаете мне за вас углубиться в даташит и поэкспериментировать, чтобы развеять ваши сомнения? :) Честно, мне лень. Сейчас мне не нужен этот режим. Когда понадобится — освою. Вполне вероятно, что я неправильно вспомнил бит, который нужно выставить. Я использовал только комплементарный режим.

Там есть еще куча бит, которые отвечают за огромное количество возможностей. Всех их наизусть я, понятное дело, не помню.

Но в любом случае, таймер 1, например, имеет четыре ШИМ-канала с управляемой инверсией, которые, естесственно, можно задействовать одновременно в любой комбинации. Так что если и не с одного канала, то с двух можно получить любой сигнал. Это то, что я могу сказать навскидку.
+1
Ух-ух, какие мы важные — даже стм32 изучили па даташиту и поучаем ДАЖЕ самого Лайфловера. Я не знаю, кто щас эти люди, но лет 5-6 назад, как я помню — Лайфловер был студент 1-2 курса, а этот ДЯДЯ был окончивший юниверситет молодой препод, но тупой, как пробка тогда в електронике. Хе-хе, или я в чем-то ошибаюсь?
-2
Действительно, ошибаетесь. Лет пять-шесть назад я тоже был студентом, второго-третьего курса. Преподом я был года три-четыре назад. :) Наверное, вы все же меня с кем-то путаете. :D
+1
Ну значит я ошибся, но почему-то все_равно_щитал, что ты електронный унтерменш. Ну тогда есно,
Лет пять-шесть назад
, ха-ха-ха!
0
Хе-хе, или я в чем-то ошибаюсь?
Да. В «тупой как пробка в электронике».
поучаем ДАЖЕ самого Лайфловера
Завидуй молча.
0
VGA, брателло, ты-то чо влез опять? Кому завидовать?
0
VGA, брателло, ты-то чо влез опять?
Тот же самый вопрос можно задать тебе.
Кому завидовать?
Судя по твоим комментариям — всем, кто что-то знает.
0
всем, кто что-то знает
Да, я завидую тебе… и моему дорогому другу evsi, хнык-хнык-хнык…
0
4 года уж прошло, а ты все завидуешь…
0
4 года уж прошло
Ух ты, с большим удовольствием перечитал… Да-а, а Я явно раньше был ГОЛОВА — точно умнее и пассионарнее, чем щас.

VGA, ты зачем это запостил? Гы-ы, ты мазохист, или же просто хотел пропиарить МЕНЯ! :DDD
0
и поучаем ДАЖЕ самого Лайфловера
Его поучать — только хуй тупить. Не в коня корм. Он, поди, до сих пор верит, что AVR быстрее на прерывание реагирует, чем кортексы.
0
Там есть еще куча бит, которые отвечают за огромное количество возможностей.
Моё впечатление — стм создавали методом копипасты. Т.е. дезигнерам была поставлена задача «сделайте всё, что есть у других». Совсем не удивлюсь, услышав такую «инсайдерскую инфу». Мне больше по душе МК, которые создавались осмысленно.
Так что если и не с одного канала, то с двух можно получить любой сигнал.
Да ладно. По фазе оно сколочно гвоздями краями или серединками и ничего с этим не сделаешь. Так что далеко не любой. Хотя при такой сложности могли бы и погибче сделать, чтобы фазу можно было двигать произвольно…
А, до меня дошло, чего вы хотите. Картинки не совсем удачные. Я наконец-то прочел подписи на них. :D
Не знаю как можно удачней нарисовать. И я же сразу сказал — пуш-пулл. Разница между ним и однотактником вроде должна быть очевидна.
-1
Не знаю как можно удачней нарисовать.

Не так симметрично. Первая картинка напоминает комплементарный сигнал с большим защитным интервалом.

Ох. Я, наверное, по свободе расчехлю и попробую сам проверить, кто из нас неправ. :)

Может быть даже статьей отпишусь.
0
Расчехлю отладку, то есть.
0
Моё впечатление — стм создавали методом копипасты. Т.е. дезигнерам была поставлена задача «сделайте всё, что есть у других»

> «Брат, прими хазават — Бог велит!» Л, Н.Толстой «Хаджи-Мурат».

Решил все же взяться за старый-добрый олдскульный RISC (ARM), маладэсъ! Кстати, в ARM Cortex-M3 в своё время (2009) инженеры-энтузиасты из Luminary решили использовать самое старое и якобы нахуй никому не нужное ядро ARM 1-й или 2-й версии ещё 80-х годов из 40-50 тыс транзисторов.

И сиим дополнительно подняли бабло этой британской конторе буквально из говна нуля. Не говоря уже о последовавшей далее тотальной экспансии на мировой рынок 8/16-bit MCU.

В любом случае, щас эта паразитическая британская говно-контора (ARM) уже нравится не всем из-за своей жадности и частичной проприетарстности. И многие уже посматривают на RISC V. Мир уже готов к эпохе возвращения старых примитивных и быстрых ядер.
0
инженеры-энтузиасты из Luminary решили использовать самое старое и якобы нахуй никому не нужное ядро ARM 1-й или 2-й версии ещё 80-х годов из 40-50 тыс транзисторов.
ARMv2 не запатентован, так что если тебя не смущает необходимость писать микроархитектуру с нуля — можешь пилить его не плятя ARM Holdings ни копейки. Собственно, даже писать необязательно — есть Amber.
И сиим дополнительно подняли бабло этой британской конторе буквально из говна нуля.
Поясни-ка этот пункт. В 2009 ARM уже активно завоевывал рынок микроконтроллеров, а взлетели они за счет сотовых телефонов.
0
Телефоны и ARM7tdmi — это другая тема. Мы говорим здесь о MCU на кортексах, это ещё более примитивное и дешевое ядро, чем арм7tdmi. Ты видимо вспомнил NXP LPC24xx и т.п. на ARM7tdmi — это тупиковая попытка арма, настоящий успех пришёл к люминари с кортексом м3.
0
настоящий успех пришёл к люминари с кортексом м3.
Пруф? Как по мне, успех пришел с дешевыми МК от ST, хотя уже LPC2xxx были по цене сравнимы с восьмибитками, а то и дешевле.
0
Luminary в 2009-м была куплена TI (mcu Stellaris). Я ошибся — уже в 2006/7-м году они пиарили свой 32-бит mcu LM3 на ядре кортекс м3 по цене $1 (цена, есно, оптовая — для производителей). Это была первая фирма, которая создала и продвигала 32 бит mcu на кортексе. Все остальное быдло — nxp, stm и ti — прочухли тему уже позже и срочно подтянулись.
0
Пруфы-то будут, что именно люминари первая предложила МК на Cortex-M и что у нее у первой продажи пошли так что ухх, не то что LPC2xxx/AT91SAM7/etc?
Алсо, согласно вики, на 2009 год ARM7TDMI было самым распространенным из всех армовских ядер, несмотря на то, что уже 5 лет как существовал CM3.
0
Где существовал см3 5 лет? — не надо бредить. возможно, что в 2004/5 они создали технологию в лаборатории, «на коленке» — это ещё не значит, что это все было готово к пром.выпуску, а маркетинг (этот факин капитализм), а ПО, что ещё более важно для этой отрасли.
0
Посмотри на даты выхода ядер в вики. ARM7TDMI еще c 2001-го NRND.
0
Я пользуясь вики, но не верю ей, если у меня есть другие сведения/источники. основа ядра см3 более древнее и более примитивное, чем ARM7TDMI, т.е. более эффективное по кол-ву вентилей и более дешевое, иначе — в mcu так и продвигался бы и далее ARM7TDMI.
0
В таком случае приведи более существенные пруфы, чем вики.
0
Ты думаешь, что я пишу здеся для тебя — зануды? Нет, для ЛЮДЕЙ — они сами разберутся и поймут, если надо (по крайней мере, гугель у каждого под рукой — luminary $1 microcontroller).
-3
Другими словами пруфов у тебя нет, чего и следовало ожидать.
0
Да, по-видимому, в железе CM3 вышел только в конце 2006 в виде LM3S101, спустя год после появления не-конфиденциальной документации на ядро. В середине 2007 подтянулась и STM.
Тем не менее, судя по этой статье, даже спустя два-три года выбор CM3 vs ARM7 был совсем не очевиден.
0
И ваще, разработка этого проца и сам принцип RISC и присущая ему сист.команд были сделаны в САСШ, по-моему в Беркли, профессорами энтузиастами и их студентами. Было много шума в 80-х, но индустрия САСШ — положила на них хуй, т.к. тогда на мази в области CPU были 2 конкурирующие CISC архитектуры — это православная Моторола 68000 и задроченное, говенное, но пропиаренное гумно в виде Интел 8086.

Потом, это тупое и ничего не знающее чмо из Acorn кампутер, финансируемое из британской гос.казны (как и BBC) — говно-кампутер (аналог Эппл-I) на знаменитом нищебродском 8-бит проце MOS Tec 6502, заявило свою тупую, дебильную, британскую харю в Беркли (эти мартышки до этого продавали аудио-усилители в наборах, и «проектировали» знаменитый говнокомп «Лорда» Синклера). В поисках КРУТОГО и дешевого проца, есно. Ну а те лошары пиндосские (чо паделать — ботаны/совки) слили этим британским тупицам технологию за сущие копейки.

Вот така и паявилася эта фирмА под названием ARM. Мартышки долго бедствовали и не знали, как свести концы с концами, но примитивная, есно, нисколько не развиваемая этими британскими полудебилами технология, ВДРУГ, дожила до тех времен, когда был нужен малопотребляющий проц в портативных девайсах.
-2
Не хочу вмешиваться в ту высокоумною беседу, что вы тут развели, но.
Мужчины, праздники заканчиваются, пора завязывать…
+1
Чо, круто написано! :DDD Да-а, такого не достигнуть без допинга… Самое интересное, что все это — правда, но конечно же в специфической упаковке…
0
Чо, круто написано! :DDD
Нет. Попытки подражать лурке заметны, но если у лурки слегка ядовитый юмор, то у тебя — только яд.
0
Чо, круто написано!
Вы слишком высокого мнения о своей писанине.
Да-а, такого не достигнуть без допинга…
Судя по стилистике вашего бреда, допингом вам служил низкокачественный алкоголь в больших количествах.
Самое интересное, что все это — правда, но конечно же в специфической упаковке…
Все написаное вами не более чем ваше предвзятое мнение.
0
Я так погляжу, ARM'у ты тоже завидуешь.
0
Моё впечатление — стм создавали методом копипасты.
У вас много странных впечатлений.
Мне больше по душе МК, которые создавались осмысленно.
Странно, что при этом вы ненавидите STM32, но в восторге от корявого на всю голову AVR, где даже в пределах одного семейства «хто в лес, кто по дрова».
0
По фазе оно сколочно гвоздями краями или серединками и ничего с этим не сделаешь.
Точно. Причем это справедливо для любой реализации PWM на счетчиках.
Хотя при такой сложности могли бы и погибче сделать, чтобы фазу можно было двигать произвольно…
Даже интересно, как вы себе это представляете? Особенно учитывая, что фаза как таковая появляется только в тот момент, когда принимается решение от чего эту самую фазу отсчитывать.

P.S. учитывая, что таймеры можно грузить через DMA, то сгенерировать можно практически что угодно. внимательное изучение, General Purpose Cookbook вполне может помочь тому, кто в этом хочет разобраться. Правда, насчет вас я сильно сомневаюсь, что вы хотите в этом разбираться, а не просто традиционно поливать дерьмом.
0
А, до меня дошло, чего вы хотите. Картинки не совсем удачные. Я наконец-то прочел подписи на них. :D

Да, навскидку я бы делал такое с помощью двух каналов.
0
0
вы сильно недооцениваете оптимизаторы. Даже SDCC не требует написания на ассемблере, несмотря на то, что оптимизатор в нём описываемого вами уровня (практически никакой). Добавить пару ключевых слов, немного изменить структуру Си кода и мы получаем генерацию оптимального АСМ кода при сохранении читаемости программы.
асм имеет смыл в очень узкой нише, где такая жёсткая оптимизация оправдана (как та же программная реализация юсб), но это признак ошибки выбора целевой платформы.
0
Если вдруг Вам понадобиться преобразовать счетчик во время и дату и обратно, предлагаю вот это решение
0
Интересно, спасибо.
0
которая будет корректно обрабатывать все это
О, да. Никогда не связывайтесь в временными зонами.
0
Хочу напомнить, что вы не пуп земли, и STM не крутится вокруг вас и ваших хотелок. И если что-то не нравится конкретно вам, то это не значит, что это что-то говно в первой инстанции. Из ошибок отмечу ключевые слова ассемблер, умножение, страница. На лицо явное не знание принципов создания архитектуры, отсюда и желание впихнуть куда попало.
0
Кстати, вот еще пруф.

Так называемый RTC в STM32F100 — ничто иное, как тридцатидвухбитный счетчик со своим тактированием и предделителем, работающий независимо от самого контроллера.
+1
а данные таймера захватывались аппаратно, а не в прерывании.
Кстати, это пожалуй им на AVR можно сделать аппаратно, а не только на всякой хрени (с). Через Input Capture. Не уверен, можно ли это сделать внутри кристалла, но можно завести на вход ICP выход с OC.
0
Простите. Вы же наверника пишите с самодельного ПК на святом АВР, и не имеете возможность погуглить характеристики благословляемого и обсираемогово вами устройства. Скажу лишь, что вы зря боготворите часовые кварцы. Имеющиеся в шаговой доступности обладают гораздо худшими харрактеристиками, чем высокочастотные. А конденсаторик покрутить, можно и на высокочастотном. Понимаю, такие детали вам неизвестны, вы же на ПК с авр сидите.
0
Насколько я помню, _YS_ не так давно делился опытом о том, что часики на МК у него на часовом кварце куда точнее ходят, чем на ВЧ. С чем связано не знаешь?
0
С тем, что кварцы на одну и туже частоту бывают разной точности. Читай внимательно мой пост, я тоже делюсь опытом. У палки два конца.
0
Там ты говорил о том, что кварцы «в шаговой доступности» имеют куда худшую точность, чем ВЧ. А _YS_, что характерно, берет детали именно в шаговой доступности.
0
Я понимаю, что _YS_ эталон, божество и прочее, а остальных радиолюбителей для вас не существует. Вы вообще в курсе, что страна у нас большая?
0
Это попахивает аргументом ad hominem. А меня интересует ad rem.
Я в курсе, что это одиночный случай и с удовольствием почитаю внятный труд, сравнивающий параметры высокочастотных и часовых кварцев, если он у тебя есть.
0
Гуглить влом.
0
Воу, воу! Здравствуйте! :D

Именно после этого комментария я тоже решил вступить в холивар. :D Эталон прибыл для прояснения ситуации! :D

Да будет вам известно, что «часовые» и «обычные» кварцы сильно различаются.

Вкратце, у «часового» и «обычного» кварца разная физическая конструкция и разный тип среза кристалла (для первого — XY и камертон, для второго — AT и, чаще всего, пластина/диск).

Часовые кварцы безусловно стабильнее при условии верного включения. А они имеют особенности включения; прежде всего, надо иметь в виду, что предельная рассеиваемая мощность для них гораздо ниже, чем для «обычных», и потому про нее надо думать и, при необходимости, ограничивать.

Такие дела.
0
_YS_ угу, и это называется практика. А Lifelover и Vga занимаются теоретикой.
0
И в чем, интересно, в этом комментарии заключается практика?
0
Погугли про правильное включение.
0
Правильное включение чего?
0
Очевидно, ответы на все эти вопросы вас не интересует, а мне они и без того известны. Давайте не будет тратить больше времени и писать бессмысленные комментарии.
Вы крут, и всё такое, поздравляю вас и всё такое. На этом и закончим.
-1
Ответ на вопрос меня интересует, но пока что я вижу только отписки «погугли». Причем без уточнения, что именно гуглить.
Не говоря уже о том, что это как раз тот ресурс, куда гугль должен привести за ответом.
+1
Не знаю насчет наших уважаемых коллег, но, действительно, я познал различие между скоростными и «часовыми» кварцами трудным путем, ценой одного проекта-позора. :D
0
Возможно, согласование лучше. Да и по заявленным характеристикам часовые точнее — 20ppm, а ВЧ — 30ppm. Это если говорить о стандартных кварцах, которые и продаются в шаговой доступности. Мои чясики врут на 1.1сек в сутки (такое значение подобрал для коррекции хода), что всего около 12ppm.
0
Ты не в тот блог публикуешь. Этот — для всякой теории вида «как посчитать резистор». Твое… Гм. По большей части текст представляет просто комментарий к программе, был бы блог «ассемблер» — туда ему и было бы самое место. А так — вероятно, в «AVR».

Да и применение ATmega328 мне показалось неоправданно жирным.
Полагаю, этот скетч прекрасно влезет в ATMEGA8A, надо только выбрать другой таргет в IDE. Конечно, переделать его под, скажем, другой индикатор без знания С (и немного С++) не выйдет.
не более 64536 импульсов
У тебя опечатка. Алсо, 65535. На 65536-м произойдет переполнение. Аналогично с 16-разрядным, на 16777216-м импульсе счетчик насчитает ноль.
;------ Конфигурация портов ----------------------------------------------------;
    OUTI  DDRB, (1<<SegG)|(1<<SegH)|(1<<Rat1)
При изменении раскладки пинов эту часть придется искать в коде и изменять. И еще процедуры вывода на дисплей перелопачивать, внимательно правя там порты.
Вместо этого можно было завести табличку, хранящую для каждого разряда/сегмента адрес регистра и номер бита, и проходить ее в цикле. Редактирование распиновки сразу резко облегчается и резко снижается вероятность ошибки, а код избавляется от копипасты.
AVR не имеет приоритетности прерываний
Насколько я помню, таки имеет. Просто приоритеты нельзя изменить, а так — прерывание OVF1 приоритетнее, чем OVF0, OVF2 — вообще самое приоритетное из использованных.
Алсо, судя по карте регистров — свободных регистров предостаточно, вместо сохранения регистра TEMP можно было просто выделить прерыванию OVF2 свой личный рабочий регистр.
Из-за особеностей флагового автомата подпрограммы не оформлены как подпрограммы, нет обращения по метке, нет вызовов RCALL и RET в конце
На самом деле, никто не мешает для улучшения читаемости выделить подпрограммы в полноценные подпрограммы. Тогда в цикле останется только
loop:
  SBRC FLAGS, Flg1
  RCALL Proc1
  SBRC FLAGS, Flg2
  RCALL Proc2
  SBRC FLAGS, Flg3
  RCALL Proc3
  RCALL RefrDisp
  RJMP loop
0
  • avatar
  • Vga
  • 23 декабря 2017, 20:39
Хм, написано много, большинство спорно. Скажу только, что улучшение читаемости от сишника в ассемблерном коде не главное, хотя приветствуется.
0
Для ассемблера читабельность еще важнее, чем для С.
0
Такой вот результат компиляции это не попытка как то снизить операционные затраты, это образ жизни:
0
Если ты про размер, то умелое применение подхода таблицы данных + циклы способно его уменьшить, и порой весьма заметно.
0
Думаю, речь о чистоте (отсутствии лишних телодвижений, мусорного кода, дурного выхлопа компилятора).
0
Я не назову этот код чистым.
0
Можете смело называть его грязным, мне это будет не очень приятно, я старался сделать его наиболее прозрачным, значит не удалось, это ассемблер…
0
Не мешай сраться с Lifelover'ом о идеалах :D
0
Мальчики, вы нашли не очень удачное место для сранья. ИМХО.
0
Техническое изделие может иметь множество уровней представления. Верхний — алгоритм работы устройства, который видит пользователь, затем код, который реализует этот алгоритм, затем командочки которые выполняются на процессоре, в которые выливается этот код и т.д. Тебя волнует только код, а большинству вообще кроме верха нафиг ничего не надо. Инженер же смотрит на весь пирог в целом, чтобы все уровни были без дурных излишеств, а не только избранные. Сущности каждого уровня должны логично и просто, без лишних фендибоберов переходить в сущности нижележащего… Потому что для чистоты реализации она должна быть во всех уровнях, иначе её не будет ни в одном. Именно на этом и построен Си, кстати… Любители же чистого кода никогда никакого чистого кода не получат. Потому что заметание мусора под ковёр не равно чистоте.
0
Потому что для чистоты реализации она должна быть во всех уровнях, иначе её не будет ни в одном.
Тобишь чистоты ты не получишь вообще, потому что на гейт-левеле микроконтроллер сгенерен HDL-синтезатором. Который дает знатную кашку.

Кроме того, если ты не заметил — я не предлагаю сменить уровень абстракции. Я комментирую сделанное на текущем.

Потому что заметание мусора под ковёр не равно чистоте.
Если ты говоришь о компиляторах и ЯВУ, то это не заметание мусора под ковер, а перекладывание работы на кремниевые мозги. Они быстрее и не ошибаются. Хотя и не всегда делают лучшим образом, но мы работаем над этим (с). Да и в большинстве случаев качество их работы достаточно.
0
Если ты говоришь о компиляторах и ЯВУ
Даже не столько о них, сколько о дури, которую привносят сами разработчики. Типа хранения времени в формате RTC. Геморно на аппаратном уровне (дикой сложности блок, который реализует календарь), дико геморно в программе (сравнить два времени). И всё потому что какой-то идиот решил что раз время в формате RTC удобно выводить, значит в нём и хранить будем. Типичный пример перекладывания «лишней работы» на железо, в итоге всегда мусор на одном уровне зеркально переходит в мусор на остальных…
0
(дикой сложности блок, который реализует календарь
Не такой уж и дикой, просто несколько счетчиков малой разрядности плюс логика задания числа дней в месяце, достаточно простая.
дико геморно в программе (сравнить два времени)
Сравнение — не единственная, и в эмбеде вероятно даже не основная операция с временем. К тому же, сравнить структурки со временем проще, чем декодировать юникс-тайм в нечто пригодное для вывода на индикатор.
0
>> умелое применение подхода таблицы данных +
Я не против, наверное просто не умею. Да с заменой пинов есть проблемма, но дело не в моих руках, дело в препроцессоре ассемблера. Не забывайте, вы не в С.
0
Ассемблер не лучший инструмент, да. Как и С, впрочем. Но и в нем можно сделать лучше.
0
Вот с этим не поспорить. Не ругайте пианиста, он играет как умеет…
А как лучше покажите чем что заменить. Иначе это просто болтовня.
0
Не ругайте пианиста, он играет как умеет…
Э нет. Так не пойдет. Если пианиста не ругать, он так и будет играть как умеет, а не улучшаться.
Иначе это просто болтовня.
Это не болтовня. Это критика. И я подсказываю, в каком направлении копать. Снова писать за тебя процедуру перемешивания бит в 30 байтах флеша мне на данный момент лень.
0
>> Снова писать за тебя…
Тупой я, забыл что бы кто то за меня чтото написал. Не напомните?
0
0
Я то понадеялся, что второй раз вы не лоханетесь. Приведу заключительный текст из той статьи:
Я сразу, как поглядел на адресацию индикаторов с ОА, понял что это гиблое дело, даже проверочный код не стал писАть. Некоторым это показалось неочевидно. Развязался флейм, для проверки своего предположения я попросил VGA предоставить проверочный код. Код на ассемблере MCS-51 был предоставлен. Приведу результаты тестирования, вырезка, орфография сохранена:
… Тут предоставленный код на MCS-51…
Итого, 1169 — 31 = 1138 команд потрачено на дебильную адресацию…
Это называется написать за меня код?
0
Команд потрачено 21 штука. Времени исполнения — да, 1к тактов, но даже если ты именно это и подразумевал изначально — из текста оно ни разу не очевидно. Однако, если глянуть на первый же твой коммент, то станет ясно, что место таки идет о занимаемом во флеше месте. Иначе ты бы сразу сказал, что да — код будет коротким, но займет много времени.
К тому же, если команд доступно всего 4096 на каком-нить ATMEGA8, то тактов там доступно 16000000 каждую секунду, что делает цену в 1к тактов несущественной для реальных условий.
0
Детский лепет, спать пора…
0
Слив засчитан.
0
1000 тактов это немало. Всё, что блокирует главный цикл — уменьшает время отклика на время своего выполнения. Даже если в среднем оно 0.0001% тактов занимает.
0
Если оно висит в главном цикле — значит оно уже не требует такой точности времени отклика.
Кстати, если говорить конкретно об этом примере, то он потом сформированный массмв данных через Soft SPI выводит, тратя на это еще несколько килотактов. Ну и какое значение после этого имеет перекодировка?
0
Если оно висит в главном цикле — значит оно уже не требует такой точности времени отклика.
Я говорил не о точности, а о скорости. Для точности есть прерывания, но всегда полезно чтобы главный цикл имел хорошее время отклика, пусть оно и далеко не будет детерменированным.
0
Я это и имел в виду. Главный цикл на то и главный цикл, чтобы в нем крутились задачи, которые не требуется уминать в микросекунды.
Хотя на AT89C2051 действительно можно впороться в ситуацию, когда вывод в такой дисплей будет жрать 10-20% процессорного времени, и процентов 20-50 из них будет уходить на перемешивание битиков, но надо еще найти задачу, где это вообще будет проблемой.
0
Сцепились, пошел спать…
0
Сцепились
В кои веки здеся опять наконец появился Лайфловер, но этот бездарный киндервуд, зануда и буквоед опять вцепился в него. Что он хочет доказать этим? То что он вумнее или лучше? И так ясно, что, как разработчик и пейсатель интересных технических статей — он полный нуль. А потом опять начнет ныть: Вот де, люди ничего уже здеся не пишут и даже не ходютъ сюды!
0
Для маньяков точности часовой кварц с таймером можно заменить выводом 1 Гц с DS3231 (термокомпенсированный кварц с возможностью подстройки, ± 2 ppm) или 1 pps с GPS модуля, говорят до 11-12 знака можно точность получить на суточных интервалах.
0
У GPS хорошая точность синхронизации времени, точность собственного генератора в модуле ниже. А для секундного интервала нужен именно последний.
0
В дешевых OEM GPS модулях обычно ставят TCXO — термокомпенсированный генератор. Его точность и стабильность выше на порядок «обычного» кварцевого генератора (ну, генератора на инверторе и кварце из магазина за 50 р, к примеру) и составляет порядка 1e-6. Но если навигационное решение приемником найдено, то точность установки фронта секундной метки PPS составит уже 40 — 100 нС, то есть еще повысится на один-два порядка. Если же хочется иметь такую точность и стабильность генератора сразу из коробки — то пригодны только OCXO (oven-controlled, термостатированные) генераторы, но стоят эти фиговины уже откровенно дорого, большие по габаритам ну и продаются не на каждом углу.
0
В дешевых OEM GPS модулях обычно ставят TCXO — термокомпенсированный генератор. Его точность и стабильность выше на порядок «обычного» кварцевого генератора
Логично, в принципе, но дешевле его и поставить, если сам GPS не нужен)
то точность установки фронта секундной метки PPS составит уже 40 — 100 нС, то есть еще повысится на один-два порядка
Как я понимаю, это абсолютная точность клока относительно часов на спутниках. Но период сигнала по прежнему будет определяться TCXO с погрешностью порядка 10^-6, не? Вот на большом отрезке времени, за счет регулярных синхронизаций со спутником можно получить точность порядка точности часов на спутнике.
то пригодны только OCXO (oven-controlled, термостатированные) генераторы
Бывают же еще атомные, где-то на кикстартере был проект карманных (или даже наручных ли) часов на таком :)
0
Вот цитата из даташита на приемник GEOS-3: «Секундная метка времени формируется с временным дискретом 61нс (определяется
частотой опорного генератора 16,369МГц)». То есть, дискретность привязки времни зависит от внутреннего генератора, но точность зависит только от наличия навигационного решения, большой интервал времени для получения этой точности не нужен. Появилась буква «A» в NMEA — все, PPS приколочен к UTC с точностью +-61 нс.

Вот еще цитата из даташита на EB-600: «The 1PPS signal accuracy directly relates to the position accuracy. The GPS /
Glonass signals travel at the speed of light, therefore a position inaccuracy directly
translates into 1PPS inaccuracies.
10 m position deviation ≈ 33 ns 1PPS deviation (typically)
100 m position deviation ≈ 333 ns 1PPS deviation (typically)»

Ну, атомные часы бывают, да. Но это уже фантастиш, я про такое и писать не стал :) Они-то и стоят на спутниках, собственно. Бывают атомные часы и в наземных приемниках — с такими топографы бегают иногда. С наземной базой точность определения координат в радиусе действия базы (5 — 10 км) — единицы миллиметров.
0
Появилась буква «A» в NMEA — все, PPS приколочен к UTC с точностью +-61 нс.
Вот именно, приколочен к UTC. Если только оно не приколачивает каждую метку к сигналу со спутника — то период меток на малом отрезке времени (между моментами синхронизации) будет определяться стабильностью тактового генератора модуля. А для частотомера он и нужен.
Но это уже фантастиш, я про такое и писать не стал :)
Ну, как я понял из прооекта тех часов — если тебя не смущает заплатить несколько сотен (или тысяч ли) баксов, то атомный клок вполне доступен и даже имеет разумные размеры (со спичечный коробок).
0
Если только оно не приколачивает каждую метку к сигналу со спутника — то период меток на малом отрезке времени (между моментами синхронизации) будет определяться стабильностью тактового генератора модуля.
Да, все так. Нет навигационного решения — точность частоты секундной метки такая, какую может обеспечить TCXO сам по себе. Разве это не очевидно?
если тебя не смущает заплатить несколько сотен (или тысяч ли) баксов, то атомный клок вполне доступен и даже имеет разумные размеры (со спичечный коробок)
Поинтересовался сейчас и обнаружил, что китайцы пихают Б/У за относительно приемлемые деньги :) goo.gl/UYpwgH
0
Нет навигационного решения — точность частоты секундной метки такая, какую может обеспечить TCXO сам по себе.
Вот и возникает вопрос — как часто оно синхронизирует эту метку? Если синхронизируется каждая метка, то да — точность там будет порядка 61нс плюс точность часов на спутнике.
Поинтересовался сейчас и обнаружил, что китайцы пихают Б/У за относительно приемлемые деньги :) goo.gl/UYpwgH
Во, с таким можно клепать частотомер на 10 знаков!)
0
Вот и возникает вопрос — как часто оно синхронизирует эту метку? Если синхронизируется каждая метка, то да — точность там будет порядка 61нс плюс точность часов на спутнике.
Да, синхронизируется каждая PPS. Логика тут простая: есть решение — метка синхронизирована. Предвосхищая следующий вопрос, как оно там это происходит в подробностях, я отвечу: я не знаю :) Про то в даташитах не пишут. Разработкой самих навигационных приемников я никогда не занимался.
0
Логика тут простая: есть решение — метка синхронизирована.
Это логично, потому что решение выдает 4 переменные — текущее время и координаты. Но для синхронизации каждого PPS нужно считать решение каждую же секунду, а какой там update rate у приемников — я не знаю, не знаю и какой он вообще максимально возможен в теории.
0
а какой там update rate у приемников
У большинства приемников — 1 раз в секунду, но у каких-то можно нарулить и более частый апдейт.

Жаль, что приемники тактируются генераторами на всякие хитрые частоты. Вот, скажем, OCXO на 10 МГц почему-то является самым распространенным, часто продается относительно недорого на всяких аукционах и т.д. Мне всегда было интересно, что произойдет, если дешевой джипиэсине заменить генератор на вариант покруче. Что произойдет с точностью определения координат, если стабильность и точность генератора увеличить на два порядка? :)
0
Решение навигационным приемником вычисляется непрерывно, а выдается дискретно. Каждая метка времени (1 PPS) синхронизирована либо с системным временем навигационной системы, либо со шкалами UTC(SU), UTC(US) в зависимости от настроек приемника. Погрешность синхронизации со шкалами времени может быть различна и доходить до сотен наносекунд, а при использовании длинных антенных кабелей и до единиц микросекунд, однако изменение этой погрешности синхронизации во много раз меньше и составляет несколько (десятков) наносекунд. Таким образом периоды сигнала 1 PPS могут отличаться друг от друга на эти несколько (десятков) наносекунд и это различие будет не систематической, а случайной величиной, с которой, до некоторой степени, можно бороться усреднениями.
10 МГц является стандартом опорной частоты в измерительной технике. В СССР также использовались частоты 5 МГц и совсем редко 1 МГц. Чем выше частота, тем меньше времени необходимо для её измерения с той же точностью.
Но вряд ли радиолюбительские конструкции на микроконтроллерах по точности упрутся в точность 1 PPS GPS/GLONAS. Ведь основной проблемой является подсчет периодов измеряемой частоты, а ведь их никогда не бывает целого количества.
0
>> Ведь основной проблемой является подсчет периодов измеряемой частоты, а ведь их никогда не бывает целого количества.
Это проблема давно решена.
В частотомерах комбинированного счета (извините, не знаю как правильнее назвать).
Это когда одновременно подсчитываются как измеряемая частота так и опорная.
При этом отсчет начинается от синхронизатора измеряемой.
Такой метод гораздо точнее, тем более если опорная выше или соизмерима с измеряемой.
0
Поясните, пожалуйста, как этот помогает посчитать части периодов? Ведь фронты измеряемой и опорной частоты несинхронны и всегда возникает дополнительная погрешность из-за этой несинхронности. А методов есть много и разных, только речь шла от том, что на микроконтроллерах в точность опорной частоты GPS/GLONASS приемника не упереться.
0
Синхронизатор измеряемой подгоняет измерительный цикл под периоды измеряемой частоты. На переднем и заднем фронтах обычно запускается интерполятор, растягивающий в N-раз разрешающую способность. Желательно, чтобы опорная частота превышала измеряемую.
0
Предположу, что вы хотите рассказать про ФАПЧ (PLL). В таком случае, расскажите: во сколько раз могут отличаться опорная и измеряемая частоты между собой, и как при этом регистрируется тот самый «недобор»?

Под «недобором» я имею в виду вот это: «как этот помогает посчитать части периодов?»
0
Вопрос можно задать в двух словах, чтобы ответить потребуется статья.
В данной цифровой шкале подобные ухищрения просто излишни.
Если вам интересно, посмотрите как сделано у Ридико.
0
Тот частотомер — просто детская игрушка, не более, и про PLL там ни слова. Годен только в качестве «быстрого старта», как собственно и ваша конструкция. Измерение частоты с помощью «временных ворот» — это самый начальный этап. Далее за ним я бы поставил преобразование Фурье с дополнением нулями, ФАПЧ и преобразование Уолша.
Ну, а для всего этого прежде всего нужен эталон частоты — вот и зашла о нем речь, а GPS — просто самый дешевый способ такой эталон получить в бытовых условиях вот и зашла о нем речь. Мы все здесь учимся друг у друга ну и выпендриваемся помаленьку :) Разве это плохо?
А к вопросу, зачем мерять частоту точнее пары герц я отвечу — каждую физическую величину можно так или иначе измерить с помощью другой физической величины или величин. Например, представьте себе, что вы задумали сделать точный широкодиапазонный L/C измеритель на основе измерения частоты.
+1
Я пошутил. Никакой контроллер такую тему не вывезет. Учите логику и верилог, вам очень понравится, если вам понравился сраный ассемблер.
0
Да даже со счетным методом и даже с таким частотомером, как в статье, упереться в точность кварца не проблема.
Однако, не подскажешь, где можно почитать основы про методы измерения частоты с лучшим разрешением?
0
Да в общем-то любую книжку по спектральному анализу. С Фурье и нулями там основная идея в том, что нужно определить частоту, соответствующую максимуму в спектре. Для этого нужно сначала это преобразование сделать. Обычно для этого пользуются алгоритмом БПФ, но, хотя он и быстро работает, у него есть недостаток — шаг по частоте в вычисленном спектре равен Fs/2^n, то есть, если частота дискретизации высокая или элементов в массиве мало — шаг получится большим и нужной точности можно и не достичь. А искусственно дополнив массив нулями это ограничение можно преодолеть. При этом спектр станет более «размазанным», но максимум у него останется на своем месте.
Еще Фурье можно считать не по БПФ, а так, как оно дается по-определению. Тут спектр вычисляется в любой произвольной точке, но появляется проблема поиска максимума в широкой области. Этот метод годен для подсечения небольших отклонений частоты — работает быстрее и точнее БПФ. Для поиска максимума с заданной точностью его можно совместить с методом деления отрезка пополам или подобным.
+1
Вы уж меня простите, по приведенному в статье коду можете что то сказать?
А все эти высокоумные размышления размещайте пожалуйста в своем топике.
Может их даже кто то прочитает…
0
0
Я когда то исследовал вопрос с точностью PPS сигнала у разных приемников штудированием даташитов. Нашел что точность фронта определяется в основном частотой опорного генератора. Однако было насколько приемников, которые в качестве решения выдавали также точное временное значение PPS пульса. То есть узнать время фронта PPS можно с более высокой точностью, чем позволяет тактовый генератор, однако само время фронта так же определяется частотой опоры.
Читал что разрешение по пространству может быть достигнуто порядка 3см. Можно соответствующее разрешение ожидать и по временным меткам, то есть разницу между 2 импульсами PPS знать с соотв точностью.
0
В данной статье нет ни слова о GPS, PPS и т.д.
К чему эти высокоумные размышления, попиариться?
0
В свое время я потратил достаточно много времени на изучение интернетов по поводу образцового интервала времени. Конструкцию я не закончил, понял что на чатотомер у меня не хватит свободного времени. Постарался что бы мои изыскания не пропали совсем уж впустую.
0
Если вы не являетесь сотрудником службы метрологии, ну какая разница на 1 Гц или на 2 Гц ошибется ваш измеритель? Вы даже не можете его метрологически проверить.
Это все равно что рассуждать на какую долу миллиметра ошибается ваша линейка. Да какая практическая разница?
0
Это все равно что рассуждать на какую долу миллиметра ошибается ваша линейка. Да какая практическая разница?
А разница простая, залезет вот эта штука в ту дырку или нет? А если залезет, то с натягом или зазором?
Смотря что и как использовать. Если генератором в качестве подмотки спидометра пользоваться, то шкала вообще не нужна
0
Ну так, типичный случай — комментарии интереснее и полезнее статьи.
0
Комментариев более сотни, но нет ни одного, после которого захотелось бы внести изменения, дополнения в статью. Печалька.
0
Да ты даже указания на явные опечатки и на off-by-one ошибки игнорируешь.
0
off-by-one ошибки это по мнению сишника. Для ассемблерщика это просто незнание критикующим особенностей препроцессора ассемблера.
Про явные опечатки ничего не скажу, не помню чтобы мне на это указывали.
0
Значение 2 в 16-й степени от языка не зависит. И максимальное значение 24-битной переменной — тоже.
Ну и да, кончай ссылаться на сишник/ассемблер. Я, кажется, уже демонстрировал, что ассемблер знаю.
0
Вспомнил, на мои слова «Т.к. шестнадцатиразрядный счетчик может вместить не более 64536 импульсов» вы оветили «У тебя опечатка. Алсо, 65535. На 65536-м произойдет переполнение. Аналогично с 16-разрядным, на 16777216-м импульсе счетчик насчитает ноль.»
Вы знаете математика с вами не согласна, значение 2 в 16-й степени будет 65536, можете проверить на калькуляторе. Вашу ошибку я заметил, поправлять не стал, зачем холивар начинать?
А знать табличку кодов ассемблера, и уметь грамотно применять язык ассемблер это совсем разные вещи.
0
Вы знаете математика с вами не согласна, значение 2 в 16-й степени будет 65536, можете проверить на калькуляторе.
Разумеется 65536. Вот только в 16-битный регистр влезет 2^16-1, то есть 65535.
А у тебя, напомню, вообще написано 64536.
А знать табличку кодов ассемблера, и уметь грамотно применять язык ассемблер это совсем разные вещи.
Почему-то люди, знающие только ассемблер, уверены, что это сакральное знание, которое другим не под силу. При этом сами ничего из разряда «вау, я бы не додумался!» не демонстрируют, совершенно типичный и прямолинейный код на ассемблере.
В отличие от сишников вроде Кармака, чей «быстрый обратный корень» до сих пор вызывает отвал челюсти у всех, кто его видит впервые.
0
Первая описка, не ошибка, мог бы сразу сказать, бывает, поправил.
А вот 2^16 даже в двоичном счетчике 65535, не надо забывать что ноль хотя и фантомное число, на емкость счетчика влияет непосредственно.
Ассемблер для меня отдушина, на работе я иногда программирую PLC Siemens, иногда пишу на Delphi под Windows конфигурационные программы под конкретное оборудование. Очень редко конфигурирую программы Brewmaxx(ProLiiT). Ассемблер для меня отдушина от всего этого безобразия.
0
Первая описка, не ошибка, мог бы сразу сказать, бывает, поправил.
Я об этом написал в первом же своем комментарии.
не надо забывать что ноль хотя и фантомное число, на емкость счетчика влияет непосредственно
Вот именно об этом я и не забываю, в отличие от тебя. В счетчике есть место для нуля импульсов, поэтому 65536 импульсов туда не влезет.
Ассемблер для меня отдушина
Рад за тебя. Но это ни разу не означает, что ассемблер я знаю хуже тебя.
0
В начальном состоянии 16-разрядного счетчика 0х0000. После 65535 импульсов будет 0хFFFF. еще один импульс вернет счетчик в начальное состояние. Чего вы его теряете?
0
Это состояние уже нельзя отличить от состояния «не пришло ни одного импульса». Таким макаром 16-битный счетчик хоть гуглплекс импульсов вместит.
0
Да не так же, 16-разрядный счетчик расширен до 24-разрядного. Каждая единичка в регистре расширения это вес именно 65536 импульсов.
Это математика.
0
Мы говорим о вместимости 16-битного счетчика, а не 24-битного. Таким макаром, опять же, можно и до гуглплекса досчитаться, было бы достаточно оперативки.
Счетчик разрядностью N вместит только 2^N-1 импульсов. Но никак не 2^N.
0
Упорным быть хорошо, а вот упертым?
По вашему в одном 16-битном счетчике 65535 состояний. А если сложить через переполнение будет 131070. А если еще раз (или не раз) сложить?
0
В 16-битном счетчике 65536 состояний. От 0 до 65535. Состояния 65536 в нем нет.
0
Ваши же слова выше:
>> Вот только в 16-битный регистр влезет 2^16-1, то есть 65535.
0
Где ты тут увидел противоречие?
0
А где вы увидели ошибку в моих словах «шестнадцатиразрядный счетчик может вместить не более 64536 импульсов»?
0
65536, это описка, я уже поправил.
0
От 65536 импульсов счетчик переполнится. С 16-битным счетчиком можно насчитать не более 65535 импульсов.
0
Упертый, прекращаем…
-1
Да, действительно, ты упертый.
-1
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.