Администрация форума не несёт ответственности за достоверность информации и оставляет за собой право редактировать или в особых случаях даже удалять посты без предупреждения. Спасибо за понимание.

Программирование ATMEL в BASCOM.

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » Программирование ATMEL в BASCOM. » Вопросы - ответы » АЦП. Как получить высокую производительность.


АЦП. Как получить высокую производительность.

Сообщений 1 страница 17 из 17

1

Даташит обещает до 15000 выборок за секунду. (Атмега8)
Из написанного более мелкими буквами следует, что это достижимо только при использовании одного канала и хорошо подобранного кварца.

Вот, написал тестовый пример в котором должен выбираться нужный канал, выставляться бит запуска преобразования и срабатывать прерывание по готовности.
Но что-то он не работает. Не укажете на ошибки?

(фрагменты)

Код:
Config Adc = Single , Prescaler = 32 , Reference = Internal
Start Adc                                                   ' 0-Is 1-Ib 2-Uref(1.26) 5-U
On Adc Conv
Enable Adc
Enable Interrupts
Admux.3 = 0 : Admux.2 = 0 : Admux.1 = 0 : Admux.0 = 0       'input select
Adcsra.6 = 1                                                ' start conversion
...

Conv:
  I(n) = Adc
  Incr N
  Adcsra.6 = 1
Return

0

2

Admux.3 = 0 : Admux.2 = 0 : Admux.1 = 0 : Admux.0 = 0       'input select
Adcsra.6 = 1 
- насчет подобной установки битов, баском подддерживает ?

0

3

Это лишь имена регистров. Баском не возражает, если обращаться к ним напрямую.
Но, видимо, я где-то напутал, раз оно не работает.

0

4

Формально ошибок нет. Но оставляешь слишком много свободы компилятору.
Задавать сразу напрямую:

    Adcsra = &B10001110                  'разрешить АЦП с частотой тактирования F / 64
    Admux = &B00001001                  'внешняя опора, измерение по входу PA0-PA1 (дифференциально x10)

Проверь: происходят ли прерывания вообще? или неверно считываются данные?

1 Конструкция I(n) = Adc плохая. Что там компилит Bascom я бы и не стал проверять!
А написал через временную переменную Tmpw = Adc :  I(n) = Tmpw. А то  и совсем надежно
на ассемблере:

Код:
'----------------------------------------------------------------------------------
'обработка прерывания от внутреннего АЦП
Adc_int:
$asm
    Push R31                            'сохраним регистры
    In R31 , Sreg
    Push R31
    Push R30
    Push R29
    Push R28
'-----
    lds R28 , {n}                       '
    Inc R28
    Sts {n} , R28
    Ldi R29 , 0
$end Asm
    Loadadr I(1) , Z                    'указатель на буфер в Z
$asm
    Add R30 , R28                       'вычислить адрес ячейки массива, в которую
    Adc R31 , R29                       'запишем результат
    Add R30 , R28                       'удвоить, т.к. число двухбайтное
    Adc R31 , R29
'-----
'считать данные внутреннего АЦП
    In R28 , Adcl                       'считать показания (M32)
'    Lds R31 , Adcl                        'считать показания (M644)
    In R29 , Adch                       ' (M32)
'    LDS R31 , Adch                      ' (M644)
    sbrc R29 , 1                        'знак результата (важно для дифференциального режима)?
    ori R29 , &B11111100                'отрицательный - сделать число отрицательным
'-----
    St Z+ , R28                         'сохранить младшие
    St Z , R29                          'сохранить старшие
'-----
Adcinte:
    Pop R28                             'восстановим регистры
    Pop R29
    Pop R30
    Pop R31
    Out Sreg , R31
    Pop R31
    Reti
$end Asm
Return
'----------------------------------------------

Наконец третье, АЦП лучше запускать в автоматическом режиме. Так как, при одиночном запуске он работает вдвое медленней (F/64 и еще /25). И похоже, что ты запускаешь его раньше, чем он будет готов к этому (ведь его тактовая F/64, а это 64 команды). Посмотри внимательно даташит.
И для точности (если нужно более 8 разрядов) нужно бы еще останавливать процессор на время измерений и усреднять несколько отсчетов.

+1

5

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

Хочу вернуться к начальной задаче: она состоит в том, чтобы считать максимальное количество раз (но известное, т.е. заранее заданное) из двух каналов АЦП.
Алгоритм предполагался такой: по таймеру с частотой близкой к 15кГц устатавливается бит  "начать преобразование" и номер канала, затем в момент готовности АЦП вызывает свое прерывание и в нем элементу массива присваивается значение.
По времени: я так понял они различают "первое" преобразование и остальные, причем независимо от типа Single или Free. Р 22-3, 22-4, 22-5.
А как работает автоматический режим? Раз он не знает какой канал нужно читать, то наверное читает их все по очереди, а значит увеличивает время обновления в 8 раз?

P.S. Проверил - в прерывание не попадает.

Отредактировано Civil (2011-04-27 01:11:07)

0

6

Автоматический режим - это с самозапуском, но для поочередного измерения двух каналов он не годится.  Автоматическое сканирование каналов это - MSP430. АЦП у AVR довольно убогий. Максимум, что можно достичь - 10000 парных выборов с секунду, если при кварце F=16-20 МГц применить Fацп = F/32 (500 кГц). При хорошем монтаже корпуса TQFP деградации точности почти не происходит. Ну и опору повыше - с 1.22 В потеряешь один разряд.

0

7

dmm, Mrshilov, спасибо.

Сейчас доделаю опторазвязку и смогу посмотреть как меняется качество данных на разных частотах.

0

8

Ого. Я остановился на 500кГц и 10 битах. Но было заметно некоторое ухудшение качества.

0

9

Mrshilov написал(а):

Функция Getadc в васике всегда инициализирует "первый" замер. У него 13,5 тактов Sample&Hold и в сумме 25 тактов на весь цикл. Для увеличения скорости (и качества измерения) лучше пользоваться режимом ADC Noise Canceler. У него 1,5 такта Sample&Hold и в сумме 13,5 тактов на измерение. Вызывается просто:
'---------------------------------------------------------------------------------
Admux = 195                                                 'выбор ADC3 (192+номер ADC)
Power Adcnoise                                              'замер
'---------------------------------------------------------------------------------
Результат получаем из прерывания ADC:
'---------------------------------------------------------------------------------
Adc_int:
L = Adcl                                                    'младший байт результата - считывать обязательно первым
H = Adch                                                  'старший байт результата
Shift H , Left , 8
Result = L + H                                               'результат
Return
'---------------------------------------------------------------------------------
Перед началом измерения необходимо сделать "пустой" замер для инициализации режима.
У всех исследованных чипов AtMega8 ADC стабильно работали на частоте выше штатной (200кГц) - до 500кГц и даже выше. Я делал до 35000 замеров в секунду.
'----------------------------------------------------------------------------------
$regfile = "m8def.dat"
$crystal = 8000000
Config Adc = Single , Prescaler = 16 , Reference = Internal
On Adc Adc_int

Отредактировано Mrshilov (2011-04-28 03:13:05)


А как опросить не один канал ADC3, а пять каналов АЦП, чтобы результаты опросов были в переменных ADC1,ADC2....ADC5

0

10

Cyber Troy написал(а):

А как опросить не один канал ADC3, а пять каналов АЦП, чтобы результаты опросов были в переменных ADC1,ADC2....ADC5

Посмотрите здесь

0

11

Подскажите а как организовать считывание без использования timer0, т.к timer0 и timer1 будет использоваться в других целях?

Sub UImetr()
'
Local A As Byte
Local ADCmean  As Word                                   'локальная переменная для среднего значения
'Stop Timer0
ADCmean = 0
For A = 0 To 10
Readadc1 = Getadc(0)                                     'опрос АЦП 0 канал напряжение 0...10В
ADCmean = ADCmean + Readadc1
Next A
Readadc1 = ADCmean / A
'
ADCmean = 0
For A = 0 To 10
Readadc2 = Getadc(1)                                     'опрос АЦП 1 канал напряжение 10...50В
ADCmean = ADCmean + Readadc2
Next A
Readadc2 = ADCmean / A
'
ADCmean = 0
For A = 0 To 10
Readadc3 = Getadc(2)                                      'опрос АЦП 2 канал ток 0...10А
ADCmean = ADCmean + Readadc3
Next A
Readadc3 = ADCmean / A
'
ADCmean = 0
For A = 0 To 10
Readadc5 = Getadc(4)                                     'опрос АЦП 4 канал предел защиты по напряжению
ADCmean = ADCmean + Readadc5
Next A
Readadc5 = ADCmean / A
'
ADCmean = 0
For A = 0 To 10
Readadc6 = Getadc(5)                                     'опрос АЦП 5 канал предел защиты по току
ADCmean = ADCmean + Readadc6
Next A
Readadc6 = ADCmean / A
'
End Sub

Вот как у меня происходит считывание данных АЦП, хотелось бы оптимизировать код и избавиться от функции getadc, знать бы толко как (

Отредактировано Cyber Troy (2016-02-11 18:53:34)

0

12

Например так

$map       ' Получаем карту распределения памяти в отчёте компиляции
  $regfile = "m328pdef.dat"       ' Используемый контроллер
  $crystal = 1000000       ' Частота работы контроллера
  $framesize = 32       ' Размер области используемой для преобразований
  $hwstack = 40       ' Размер аппаратного стека
  $swstack = 16       ' Размер программного стека

'-------------  Настройка USART  -----------------------------------------------
  $baud = 4800       ' 38400
  Ucsr0a.1 = 1       ' Baud = Baud * 2
  Config Com1 = Baud , Synchrone = 0 , Parity = None , Stopbits = 1 , Databits = 8 , Clockpol = 0

'-------------  Настройка АЦП  -------------------------------------------------
'Полная документация Atmel - http://www.atmel.com/images/Atmel-8271- … mplete.pdf
'Работа с АЦП стр.242

'                 +----- 3 - MUX3 - выбор канала АЦП, Table 24-4, стр.255
'                 |+---- 2 - MUX2 - выбор канала АЦП, Table 24-4, стр.255
'                 ||+--- 1 - MUX1 - выбор канала АЦП, Table 24-4, стр.255
'                 |||+-- 0 - MUX3 - выбор канала АЦП, Table 24-4, стр.255
'                 ||||
' ADMUX = &B_0000_0000
'            ||||
'            |||+------- 4 - зарезервировано производителем
'            ||+-------- 5 - ADLAR - 8-ми битный результат, считывать регистр ADCH
'            |+--------- 6 - REFS0 - выбор источника опорного напряжения, Table 24-3, стр.254
'            +---------- 7 - REFS1 - выбор источника опорного напряжения, Table 24-3, стр.254
'

'Режимы считывания:
'  Single - единичное считывание, результаты заносятся в ADCL, ADCH
'  Free – постоянное считывание, результаты заносятся в ADCL, ADCH, но происходит всё автономно

'Prescaler:
'  Может быть 2|4|8|16|32|64|128 или Auto, в случае Auto компилятор выбирает подходящую частоту работы АЦП

'Reference: Источник опорного напряжения Reference может быть:
'  Internal – внутренний
'  Aref – внешний источник
'  Avcc – напряжение питания схемы

'  Config Adc = Single , Prescaler = Auto , Reference = Avcc
  Config Adc = Free , Prescaler = 128 , Reference = Avcc       ' Источник опорного напряжения Reference может быть: Aref – внешний источник, Avcc – напряжение питания схемы и Internal – внутренний
  On Adc Обработка_прерывания_ацп_asm Nosave       ' Обработчик прерываний АЦП

'--------------  Используемые переменные для работы с АЦП  ---------------------
  Dim Флаг_данные_ацп As Byte
  Dim Номер_канала_adc As Byte
  Dim Готовые_данные_ацп_word(6) As Word

'\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\/\
'--------------  Разрешаем работу необходимых прерываний  ----------------------
  Enable Adc : Start Adc
'------------  Разрешаем работу всех включенных прерываний  --------------------
  Enable Interrupts
'-------------------------------------------------------------------------------
'Ждём первой выборки замеров АЦП
Ждем_первый_замер_ацп:
  Idle
  If Флаг_данные_ацп = 0 Then
      Goto Ждем_первый_замер_ацп
  End If
  Флаг_данные_ацп = 0

'===============================================================================
'                   Г Л А В Н Ы Й         Ц И К Л
  Главный_цикл:
'===============================================================================

     If Флаг_данные_ацп <> 0 Then
         Print "ADC0 = " ; Готовые_данные_ацп_word(1)
         Print "ADC1 = " ; Готовые_данные_ацп_word(2)
         Print "ADC2 = " ; Готовые_данные_ацп_word(3)
         Print "ADC3 = " ; Готовые_данные_ацп_word(4)
         Print "ADC4 = " ; Готовые_данные_ацп_word(5)
         Print "ADC5 = " ; Готовые_данные_ацп_word(6)
         Print
         Флаг_данные_ацп = 0
         Goto Главный_цикл
     End If

   Idle       ' Отправляем МК на тихий час
'===============================================================================
  Goto Главный_цикл
End
'===============================================================================
'       П О Д К Л Ю Ч Е Н И Е     Р А Б О Ч И Х     М О Д У Л Е Й
'===============================================================================

'===============================================================================
  Обработка_прерывания_ацп_asm:
'-------------------------------------------------------------------------------
   'Сохраняем регистры, которые будем использовать и SREG
    !push R16
    !in R16 , sreg
    !push R16
    !push R10       ' Используется здесь - Loadadr Готовые_данные_ацп_word(номер_канала_adc) , Z
    !push R11       ' Используется здесь - Loadadr Готовые_данные_ацп_word(номер_канала_adc) , Z
    !push R30
    !push R31

    Loadadr Готовые_данные_ацп_word(номер_канала_adc) , Z

    !in R16 , adcl
    !st Z+ , R16
    !in R16 , Adch
    !st Z , R16

    !lds R16 , {Номер_канала_adc}
    !inc R16
    !cpi R16 , 7
    !BRLO Переключаем_канал_ацп       ' Перейти если меньше
    !ldi R16 , 0

   Переключаем_канал_ацп:
    !in R30 , Admux
    !andi R30 , &B_1111_0000
    !OR R30 , R16
    !out admux , R30
    !sts {Номер_канала_adc} , R16

   'Поднимаем флаг сигнализирующий о готовности результата измерения АЦП
    !ldi R16 , 1
    !sts {Флаг_данные_ацп} , R16

   'Восстанавливаем регистры и SREG

    !pop R31
    !pop R30
    !pop R11
    !pop R10
    !pop R16
    !out sreg , R16
    !pop R16
'-------------------------------------------------------------------------------
  Return
'===============================================================================

0

13

если нужно Config Adc = Single добавлять что-нибудь надо?

0

14

Cyber Troy написал(а):

добавлять что-нибудь надо?

Нужно каждый раз перезапускать измерения.



А чем Вас не устраивает:

for цикл_ацп =0 to 7
     переменная_word(цикл_ацп+1) = getadc(цикл_ацп)
next цикл_ацп

???

0

15

Вы имеете в виду как у меня и есть:

For A = 0 To 10
Readadc1 = Getadc(0)                                     
ADCmean = ADCmean + Readadc1
Next A
Readadc1 = ADCmean / A

просто, я извиняюсь, мне сложновато ориентироваться в смеси баскомовских команд и "переменных на русском"? путаюсь немного.
да форумов начитался(в том числе и этого), не советуют так делать, шучу)
А если серьёзно хочется код покомпактнее. Заодно bascom осваиваю понемногу я ещё дремучий человек в программировании.
Ещё у меня почему-то немного пляшут данные АЦП(последний разряд в формате ##,#). замечу, что земли разделены как положено, опора внешняя REF193. Вот и играю с кодом.

0

16

Cyber Troy написал(а):

не советуют так делать


Одно из огромных достоинств Bascom, толлерантность к кириллице!

Cyber Troy написал(а):

Вы имеете в виду как у меня и есть:


Если работает, чего Вы мучаетесь.

Cyber Troy написал(а):

Ещё у меня почему-то немного пляшут данные АЦП


В качестве опорного, у Вас что?

0

17

не советуют так делать

Это я о команде Readadc1 = Getadc(0)

В качестве опорного, у Вас что?

внешний ИОН - ref193

0


Вы здесь » Программирование ATMEL в BASCOM. » Вопросы - ответы » АЦП. Как получить высокую производительность.