суббота, 2 декабря 2023 г.

Аналого-цифровое преобразование при программировании Attiny13 в среде Microchip Studio на языке С

    

        В данной статье будет рассмотрен принцип использования встроенного аналого-цифрового преобразователя (АЦП или по англ. ADC - Analog-to-digital converter) в микроконтроллерах AVR на примере attiny13

        АЦП в Attiny13 может использовать 3 порта ADC1-ADC2. Также при соответствующих настройках фьюзов в качестве четвертого порта ADC0 можно задействовать порт RESET.

 



        Для управления АЦП используются регистры ADMUX и ADCSRA, а для хранения результата – 16-битный регистр ADC, состоящий из двух восьмибитных ADCH и ADCL. Теперь об этом немного попрдробней:

ADMUX – ADC Multiplexer Selection Register 

Где:

REFS0 – Выбор источника опорного напряжения. 0 – используется напряжение питания Vcc, 1 - внутреннее опорное напряжение величиной 1.1В

 

ADLAR – задает способ вывода результата в регистр ADC (ADCH и ADCL)

        Если ADLAR = 0, то в регистре число в формате, удобном для 10-битной работы АЦП (в ADC – 16-битное значения сигнала на входе (0-1023)):

 

        а если ADLAR = 1, то число в удобном формате для 8-ми битной работы АЦП (В регистре ADCH – 8-битное значения сигнала на входе (0-255)):

 

MUX1:0 – задает порт ADC0-ADC3, с которым будет работать АЦП:


ADCSRA – ADC Control and Status Register A

 Где:

ADEN: ADC Enable – запись в него 1 разрешает работу АЦП

ADSC: ADC Start Conversion – запись в него 1 запускает старт преобразования

ADIF: ADC Interrupt Flag – бит, который используется для контроля процесса преобразования. По окончанию преобразования данный бит устанавливается в 1.

Bits 2:0 – ADPS[2:0]: Выбор предделителя для подачи тактирования. Диапазон тактирования от 50 до 200кГц. Для получения частоты тактирования в 75кГц нужно выбрать коэффициент 128 (9 600 000/128 = 75 000)

 

Пример использования:

Инициализация АЦП. 

// Выбираем внутренний источник опорного напряжения и ADC3
ADMUX|=(1<< REFS0) | (1<<MUX0) | (1<<MUX1); 

//Разрешаем работу АЦП и устанавливаем делитель на 128
ADCSRA|=(1<< ADEN) | (1<<ADPS0) |(1<<ADPS1) | (ADPS2);


Чтение результата АЦП:

ADCSRA|=(1<<ADSC); //Запуск преобразования
While((ADCSRA&(1<<ADSC))); //Ожидаем окончания преобразования

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


        Пример программы с 10 битным преобразованием (результат 0-1023):

/*
 * ADC-tm1637.c
 *
 *	============================================
 *	H A R D W A R E   I N F O R M A T I O N S
 *	============================================
 *	Attiny13a   ___________
 *		1 /____        |8
 *	      o--|RESET     Vcc|--o +5V
 *	  ADC o--|PB3/ADC3  PB2|--o 
 *	      o--|PB4/ADC2  PB1|--o  CLK_PIN
 *	  GND o--|GND       PB0|--o  DIO_PIN
 *		 |_____________|
 *
 * Author : Vyacheslav
 */

#define F_CPU 1200000L
#include <avr/io.h>
#include <util/delay.h>
#include "tm1637.h"

unsigned short
		numb_b,
		digit_tmp;

int main(void)
{
        //Инициализация АЦП
		ADMUX|= (1<<MUX0) | (1<<MUX1); //Выбираем для работы АЦП порт ADC3
		ADCSRA|= (1<<ADEN) | (1<<ADPS2); // Разрешаем работу АЦП и устанавливаем делитель на 16 (1.2MHz/16=75kHz)
		
		TM1637_init(1, 6);
        TM1637_clear();
		TM1637_display_colon(0);
           
	while (1) 
    {
		ADCSRA|=(1<<ADSC); //Запуск преобразования
		while(!(ADCSRA&(1<<ADIF))); //Ожидание окончания преобразования
		numb_b =  ADC;
						
		for (unsigned char i = 1; i <= 4; ++i) {
					digit_tmp = numb_b % 10;	
			TM1637_display_digit(4-i,digit_tmp);
			numb_b = numb_b / 10;
		}
		
			_delay_ms(1000);
    }
}



        Чтоб преобразование было не 10 битным, а 8 (результат 0-255) нужно бит ADLAR регистра ADMUX установить в 1 и результат взять не с ADC, а с ADCH:

/*
 * ADC-TM1637-8bit.c
 
 *	============================================
 *	H A R D W A R E   I N F O R M A T I O N S
 *	============================================
 *	Attiny13a   ___________
 *		1 /____        |8
 *	      o--|RESET     Vcc|--o +5V
 *	  ADC o--|PB3/ADC3  PB2|--o 
 *	      o--|PB4/ADC2  PB1|--o  CLK_PIN
 *	  GND o--|GND       PB0|--o  DIO_PIN
 *		 |_____________|
 *
 * Author : Vyacheslav
 */

#define F_CPU 1000000L
#include <avr/io.h>
#include <util/delay.h>
#include "tm1637.h"

unsigned short
		numb_b,
		digit_tmp;

int main(void)
{
        //Инициализация АЦП
		ADMUX|= (1<<ADLAR) | (1<<MUX0) | (1<<MUX1); //Выбираем для работы АЦП порт ADC3
		ADCSRA|= (1<<ADEN) | (1<<ADPS2); // Разрешаем работу АЦП и устанавливаем делитель на 16 (1.2MHz/16=75kHz)
		
		TM1637_init(1, 6);
        TM1637_clear();
		TM1637_display_colon(0);
           
	while (1) 
    {
		ADCSRA|=(1<<ADSC); //Запуск преобразования
		while(!(ADCSRA&(1<<ADIF))); //Ожидание окончания преобразования
		numb_b =  ADCH;
						
		for (unsigned char i = 1; i <= 4; ++i) {
					digit_tmp = numb_b % 10;	
			TM1637_display_digit(4-i,digit_tmp);
			numb_b = numb_b / 10;
		}
		
			_delay_ms(1000);
    }
}



        И еще вариант программы, где АЦП реализовано в виде функции ADC_read с возможностью задания порта при вызове функции. Т.к. в качестве примера выбран порт 2 (ADC2), а к нему подключена кнопка, то на индикаторе отображается напряжение на кнопке (5В = 1023 единицы):

/*
 * ADC-tm1637-func.c
 *
 *	============================================
 *	H A R D W A R E   I N F O R M A T I O N S
 *	============================================
 *	Attiny13a   ___________
 *		1 /____        |8
 *	      o--|RESET     Vcc|--o +5V
 *	  ADC o--|PB3/ADC3  PB2|--o
 *	      o--|PB4/ADC2  PB1|--o  CLK_PIN
 *	  GND o--|GND       PB0|--o  DIO_PIN
 *		 |_____________|
 *
 * Author : Vyacheslav
 */

#define F_CPU 1200000L
#include <avr/io.h>
#include "tm1637.h"
#include <util/delay.h>

unsigned int ADC_read (unsigned char ch_adc)
{
    if (ch_adc & (1<<0))	{ADMUX|= (1<<0);}; //Т.к. значение MUX1:0 соответствует номеру порта ADCx
	if (ch_adc & (1<<1))	{ADMUX|= (1<<1);}; // то записываем в них номер порта
			
	ADCSRA|=(1<<ADSC); //Запуск преобразования
	while(!(ADCSRA&(1<<ADIF))); //Ожидание окончания преобразования		
	
	return (ADC); //Возврат в основною программу со значением 8-битного АЦП
}

int main(void)
{
    ADCSRA|= (1<<ADEN) | (1<<ADPS2); // Разрешаем работу АЦП и устанавливаем делитель на 16 (1.2MHz/16=75kHz)
	TM1637_init(1, 6);
	TM1637_display_colon(0);
	
	int numb_b, digit_tmp; 
    while (1) 
    {
		numb_b = ADC_read(1); //Считываем в переменную значение ADC
		for (unsigned char i = 1; i <= 4; ++i) { // выводим значение ADC на дисплей
			digit_tmp = numb_b % 10;
			TM1637_display_digit(4-i,digit_tmp);
			numb_b = numb_b / 10;
			}
	 _delay_ms(500);
	}
}



Комментариев нет:

Отправить комментарий