Перейти к содержанию

Вопросы от начинающих по МК


Рекомендуемые сообщения

Реклама: ООО ТД Промэлектроника, ИНН: 6659197470, Тел: 8 (800) 1000-321

  • Ответов
  • Создана
  • Последний ответ

Топ авторов темы

вряд ли дело в запятой)

'0' и 0 не одно и то же

'0' - это символ для вывода на экран, байт будет не 0 а 48, то есть запись { '0' } это то же самое что { 48 }
0 - а это именно байт равен нулю, нужно для определения конца строки.

 

8 часов назад, Дмитрий Мамедиев сказал:

for(n=0;str1[n]!='\0';n++)

когда у вас этот цикл закончится? когда вы встретите не '0' а '\0'

а вот как раз '\0' == 0 != '0' :) как то так)

именно по этому я говорил учить программирование) а дать кусок кода аля

7 часов назад, IMXO сказал:

while(*s) lcd_write(*s++);

без пояснений... видя что 

7 часов назад, IMXO сказал:

вы  копировать копировать текст не умеете?

что человек не знает для чего там 0 и разницу между '0' и 0, это подводный камень) пока человек не выкинет этот 0 опять)

 

 

в вашем случае вы можете обойтись без  0, передавая длину массива в цикл, хотя смысла в том мало... только если она всегда статик, и вы ее в дефайне пропишите, для экономии 1 байта озу)

 

Ссылка на комментарий
Поделиться на другие сайты

20% скидка на весь каталог электронных компонентов в ТМ Электроникс!

Акция "Лето ближе - цены ниже", успей сделать выгодные покупки!

Плюс весь апрель действует скидка 10% по промокоду APREL24 + 15% кэшбэк и бесплатная доставка!

Перейти на страницу акции

Реклама: ООО ТМ ЭЛЕКТРОНИКС, ИНН: 7806548420, info@tmelectronics.ru, +7(812)4094849

4 часа назад, Дмитрий Мамедиев сказал:

мне хотелось бы два знака до запятой и два после.

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

Скрытый текст
Цитата

Строки

Одномерный массив наиболее часто применяется в виде строки символов. Строка — это одномерный массив символов, заканчивающийся нулевым символом. В языке С признаком окончания строки (нулевым символом) служит символ '\0'. Таким образом, строка содержит символы, составляющие строку, а также нулевой символ. Это единственный вид строки, определенный в С.

 

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

"volt"

В конец строковой константы компилятор автоматически добавляет нулевой символ.

те вы получите массив символов вида const char xxx[5]={'v', 'o', 'l', 't', 0} ,  нулевой символ '\0',  в десятичной = 0 , в шестнадцатеричной = 0х00  ,

в данном случае мы сами создаем строку из масса  символов unsigned char Buffer[5]={'0', ',', '0', '0', 0}; 0 - признак окончания строки.

и получим "0,00" == два знака после запятой. :spiteful:

 

Ссылка на комментарий
Поделиться на другие сайты

Выбираем схему BMS для корректной работы литий-железофосфатных (LiFePO4) аккумуляторов

 Обязательным условием долгой и стабильной работы Li-FePO4-аккумуляторов, в том числе и производства EVE Energy, является применение специализированных BMS-микросхем. Литий-железофосфатные АКБ отличаются такими характеристиками, как высокая многократность циклов заряда-разряда, безопасность, возможность быстрой зарядки, устойчивость к буферному режиму работы и приемлемая стоимость. Но для этих АКБ, также как и для других, очень важен контроль процесса заряда и разряда, а специализированных микросхем для этого вида аккумуляторов не так много. Инженеры КОМПЭЛ подготовили список имеющихся микросхем и возможных решений от разных производителей. Подробнее>>

Реклама: АО КОМПЭЛ, ИНН: 7713005406, ОГРН: 1027700032161

10 минут назад, Дмитрий Мамедиев сказал:

Вот где, где это обьясняется?

например здесь: polnyy_spravochnik_po_c_gerbert_shildt.rar

распаковать,  запустить файл main.htm

ЗЫ хочешь что-то сломать? спроси мня как...

Ссылка на комментарий
Поделиться на другие сайты

ф-ция вывода строки

void lcd_string (unsigned char str1[])
{
	wchar_t n;
	for(n=0;str1[n]!='\0';n++)
	lcd_data(str1[n]);
}
void ADC_convert(void)
{
	static unsigned int value;//значение измеренной величины
	static unsigned int ADC_value;//переменная суммы измерений
	static unsigned char count;//переменная суммы количества измерений
	ADC_value+=ADCW;//складываем ADCW 64раза
	if (count==64)//если прошло 64 измерения
	{
		
		ADC_value>>=6;//расчитываем среднее значение побитовым сдвигом
		value=(ADC_value*500L)>>10;//получаем значение напряжения при он 5,00 вольт L-означает 
		//что хотим получить в 32 битах, >>10 сдвинув на 10 получим 16бит
		//делим число и выводим посимвольно в buffer, buffer[1] значение запятой
		buffer[0]=(value/100)+'0';
		buffer[2]=((value%100)/10)+'0';
		buffer[3]=(value%10)+'0';
		ADC_value=0;
		count=0;
	} 
	else
	{
		count++;//делаем следующий замер
	}
}

 и в ADC.h обьявил 

#include "main.h"
#include "LCDHD44780.h"

void ADC_ini(void);
void ADC_convert(void);
unsigned char buffer[5]={'0',',','0','0',0};

 

вот что говорит. 

Безымянный.jpg

Ссылка на комментарий
Поделиться на другие сайты

ADC.h

#ifndef ADC_H_
#define ADC_H_

#include "main.h"
#include "LCDHD44780.h"

void ADC_ini(void);
void ADC_convert(void);
unsigned char buffer[5]={'0',',','0','0',0};

#endif

ADC.c

#include "ADC.h"
void ADC_ini(void)
{
	ADCSRA|= (1<<ADEN)// включаем ацп
	|(1 << ADSC) // запуск преобразования
	|(1 << ADIE) // разрешение прерывания от АЦП
	|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//делитель на 128	
	ADMUX|=(1<<REFS0);ADMUX&=~(1<<REFS1);//он = AVCC
	sei ();// Глобально разрешаем прерывания
}

void ADC_convert(void)
{
	static unsigned int value;//значение измеренной величины
	static unsigned int ADC_value;//переменная суммы измерений
	static unsigned char count;//переменная суммы количества измерений
	ADC_value+=ADCW;//складываем ADCW 64раза
	if (count==64)//если прошло 64 измерения
	{
		
		ADC_value>>=6;//расчитываем среднее значение побитовым сдвигом
		value=(ADC_value*500L)>>10;//получаем значение напряжения при он 5,00 вольт L-означает 
		//что хотим получить в 32 битах, >>10 сдвинув на 10 получим 16бит
		//делим число и выводим посимвольно в buffer, buffer[1] значение запятой
		buffer[0]=(value/100)+'0';
		buffer[2]=((value%100)/10)+'0';
		buffer[3]=(value%10)+'0';
		ADC_value=0;
		count=0;
	} 
	else
	{
		count++;//делаем следующий замер
	}
}

 

main.c на всякий случай

while (1) 
    {
		//lcd_poz(0);
		//lcd_string("I love Maria");
		ADMUX|=(1<<MUX2)|(1<<MUX0);ADMUX&=~((1<<MUX1)|(1<<MUX3));//ADC5 напряжение
		ADC_convert();
		lcd_poz(0);
	    lcd_string(buffer);
		ADMUX|=0;//завершаем преобразование
		ADMUX|=(1<<MUX2);ADMUX&=~((1<<MUX0)|(1<<MUX1)|(1<<MUX3));//ADC4 ток
		ADC_convert();
		lcd_poz(8);
		lcd_string(buffer);
		ADMUX|=0;//завершаем преобразование
		
    }

 

Ссылка на комментарий
Поделиться на другие сайты

может ему не нравится имя buffer ?  может в какой-то функции тоже используется переменная с такимже именем или ты ее где-то повторно  еще определил, попробуй сменить на что-то типа buffer_volt

Ссылка на комментарий
Поделиться на другие сайты

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

Я предполагаю, это потому, что вы не построили в голове четкий алгоритм действий, которые ваша программа должна делать. И, естественно, она и не делает. Плюс какие-то естественные для начинающего ошибки в синтаксисе - полный тупик выходит...

Вот смотрите, как бы делал я.

Сначала главное - main

int main(void){
	// настройка периферии: LCD, USART, ADC и что там еще необходимо
	while(1){ // главный цикл
		// измерение 
		// вывод измеренного значения
	}
}

Этот код может быть скомпилирован, ошибок нет, можно думать дальше.

Давайте начнем с настройки АЦП - я не в курсе, что еще вы там используете... а АЦП - однозначно.

Итак, АЦП требует:

  1. задать источник опорного напряжения
  2. задать режим и скорость работы
  3. задать номер канала измерения

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

static void adc_setup(void){
	// просто на максимальной скорости работа
	ADCSRA	= (1 << ADEN) // разрешаем АЦП
			| (1 << ADSC0) // максимальная скорость
			;
	// встроенный источник опорного с внешним кондером
	ADMUX	= (1 << REFS0)
			| (1 << REFS1)
			;
}

Теперь наша функция main приобретает другой вид

int main(void){
	adc_setup();
	while(1){
		// измерение 
     	// вывод
    }
}

Если попробовать скомпилировать это - будут ошибки, т.к. ADCSRA и т.п. регистры не определены - надо подключать соответствующие заголовки. В итоге од становится таким:

#include <avr/io.h>
  
static void adc_setup(void){
	// просто на максимальной скорости работа
	ADCSRA	= (1 << ADEN) // разрешаем АЦП
			| (1 << ADSC0) // максимальная скорость
			;
	// встроенный источник опорного с внешним кондером
	ADMUX	= (1 << REFS0)
			| (1 << REFS1)
			;
}
  
  
int main(void){
	adc_setup();
	while(1){
		// измерение 
     	// вывод
    }
}

Я привожу пример для avr-gcc, у вас могут быть другие заголовки.

Вот это уже скомпилируетя без ошибок. Можно приступать к тому, как будем измерять. Измерение - это процесс запроса уровня на каком-то входе АЦП. Значит:

// запрос уровня на указанном канале АЦП
int get_adc_value(unsigned char chanel);

Это прототип функции. У  нее один параметр - номер канала, а возвращает она соответствующее значение преобразрвания сигнала на этом канале. 

то есть main у нас снова изменяет свой вид:

int main(void){
	int value; // переменная для результата замера АЦП

	adc_setup();
	while(1){
		// измерение 
		value = get_adc_value(0); // возьмем значение с первого канала
     	// вывод
    }
}

Этот код не скомпилируется, т.к. "тело" функции измерения не реализовано. Что в нем должно быть? Элементарно:

  1. Устанавливаем номер канала
  2. Запускаем АЦП
  3. Ждем, пока измерит
  4. Возвращаем результат

Остаетс только записать это на Си:

int get_adc_value(unsigned char chanel){
	// устанавливаем номер канала
	ADMUX = ADMUX & 0xF8; // надо подавить три младшие бита, ведь номер канала задается ими
	ADMUX |= chanel & 0x07; // устанавливаем три младших бита - номер канала
	// запуск измерения
	ADCSRA |= 1 << ADSC;
	// ожидание конца измерения
	while(ADCSRA & (1 << ADSC)); // тупо ждем, пока упадет флаг преобразования ADSC	
	// возврат значения
	return ADC; // тут можно, конечно, абстрактные биты результата привести к каким-то осмысленным значеним... вольтам, например, или килограммам... но пока - так
}

Далее у нас идет вывод того, что намерили. Очевидно, что это будет как-то так:

int main(void){
	int value; // переменная для результата замера АЦП

	adc_setup();
	while(1){
		// измерение 
		value = get_adc_value(0); // возьмем значение с первого канала
     	// вывод
		lcd_out(value);
    }
}

Само собой, теперь надо задуматься над lcd_out... а там, глядишь, и еще какие-то нюансы всплывут...

Но вот так, от общего к частному, писать код очень просто! На каждом шаге вы четко понимаете, что хотите, видите, что есть, и значете, чего не хватает.

Дерзайте!

Изменено пользователем ARV

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

значит так собрал я ADC.c ADC.h и прочее в одну портянку чтобы выложить сюда иииии... он скомпилировался! правда в протеусе не показывает ничего.

 

Ссылка на комментарий
Поделиться на другие сайты

#define F_CPU 8000000UL
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdio.h>
#include <stdlib.h>
#define RS PD0
#define EN PD1
unsigned char buffer[5]={'0',',','0','0',0};

void port_ini (void)
{
	DDRB=0xFF;
	DDRC=0b1001111;//4 и 5 на вход для ацп
	DDRD=0xFF;
	PORTB=0x00;
	PORTC=0x00;
	PORTD=0x00;
}
// Функция передачи команды
void lcd_com(unsigned char com)
{
	PORTD &= ~(1 << RS); // RS = 0 (запись команд)
	PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
	PORTD &= 0x0F; PORTD |= (com & 0xF0); // старший нибл
	_delay_us(100);
	PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
	_delay_us(100);
	PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
	PORTD &= 0x0F; PORTD |= (com << 4); // младший нибл
	_delay_us(100);
	PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
	_delay_us(100);
}
// Функция передачи данных
void lcd_data(unsigned char data)
{
	PORTD |= (1 << RS)|(1 << EN); // RS = 1 (запись данных), EN - 1 (начало записи команды в LCD)
	PORTD &= 0x0F; PORTD |= (data & 0xF0); // старший нибл
	_delay_us(100);
	PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
	_delay_us(100);
	PORTD |= (1 << EN); // EN = 1 (начало записи команды в LCD)
	PORTD &= 0x0F; PORTD |= (data << 4); // младший нибл
	_delay_us(100);
	PORTD &= ~(1 << EN); // EN = 0 (конец записи команды в LCD)
	_delay_us(100);
}
//функция передачи строки
void lcd_string (unsigned char str1[])
{
	wchar_t n;
	for(n=0;str1[n]!='\0';n++)
	lcd_data(str1[n]);
}
//функция позиционирования курсора
void lcd_poz(unsigned char x)
{
	char adress;
	adress=x|0b10000000;//set DDRAM adress дш стр24
	lcd_com(adress);
}
//функция инициализации дисплея
void lcd_ini(void)
{
	_delay_ms(15);
	// Конфигурирование четырехразрядного режима
	PORTD |= (1 << PIND5);
	PORTD &= ~(1 << PIND4);
	// Активизация четырехразрядного режима
	PORTD |= (1 << EN);
	PORTD &= ~(1 << EN);
	lcd_com(0x20); //0b00100000 - 4 разрядная шина, 1 строки
	_delay_ms(40);
	lcd_com(0x08); // 0b00001000 полное выключение дисплея
	_delay_ms(40);
	lcd_com(0x01); // 0b00000001 очистка дисплея
	lcd_com(0x06);  //0b00000110 - курсор движется вправо, сдвига нет
	_delay_ms(40);
	lcd_com(0x0C);  //0b00001100 - дисплей включен, курсор выключен, мерцание выключено
	_delay_ms(40);
}
//функция очистки дисплея
void lcd_clear (void)
{
	lcd_com(0b00000001);
}
void ADC_ini(void)
{
	ADCSRA|= (1<<ADEN)// включаем ацп
	|(1 << ADSC) // запуск преобразования
	|(1 << ADIE) // разрешение прерывания от АЦП
	|(1<<ADPS2)|(1<<ADPS1)|(1<<ADPS0);//делитель на 128
	ADMUX|=(1<<REFS0);ADMUX&=~(1<<REFS1);//он = AVCC
	sei ();// Глобально разрешаем прерывания
}
void ADC_convert(void)
{
	static unsigned int value;//значение измеренной величины
	static unsigned int ADC_value;//переменная суммы измерений
	static unsigned char count;//переменная суммы количества измерений
	ADC_value+=ADCW;//складываем ADCW 64раза
	if (count==64)//если прошло 64 измерения
	{
		
		ADC_value>>=6;//расчитываем среднее значение побитовым сдвигом
		value=(ADC_value*500L)>>10;//получаем значение напряжения при он 5,00 вольт L-означает
		//что хотим получить в 32 битах, >>10 сдвинув на 10 получим 16бит
		//делим число и выводим посимвольно в buffer, buffer[1] значение запятой
		buffer[0]=(value/100)+'0';
		buffer[2]=((value%100)/10)+'0';
		buffer[3]=(value%10)+'0';
		ADC_value=0;
		count=0;
	}
	else
	{
		count++;//делаем следующий замер
	}
}
int main(void)
{
	port_ini();
	lcd_ini();
	ADC_ini();
	
	while (1)
	{
		//lcd_poz(0);
		//lcd_string("I love Maria");
		ADCSRA|= (1<<ADEN)// включаем ацп
		|(1 << ADSC); // запуск преобразования
		ADMUX|=(1<<MUX2)|(1<<MUX0);ADMUX&=~((1<<MUX1)|(1<<MUX3));//ADC5 напряжение
		ADC_convert();
		lcd_poz(0);
		lcd_string(buffer);
		ADMUX|=0;//завершаем преобразование
		ADCSRA|= (1<<ADEN)// включаем ацп
		|(1 << ADSC); // запуск преобразования
		ADMUX|=(1<<MUX2);ADMUX&=~((1<<MUX0)|(1<<MUX1)|(1<<MUX3));//ADC4 ток
		ADC_convert();
		lcd_poz(8);
		lcd_string(buffer);
		ADMUX|=0;//завершаем преобразование
	}
}

что то я устал совсем

Ссылка на комментарий
Поделиться на другие сайты

:) это так работать не будет.  Вы неправильно работаете с АЦП. 

1. у вас нет обработчика прерывания АЦП. но включаете прерывание от АЦП , флаг не сбрасывается у вас вечный ступор в попытках зайти в прерывание, которого нет...

2. аналоговые входы и включение АЦП проводятся при инициализации один раз.

3. при смене канала перед началом преобразования должна быть выдержана пауза для зарядки емкости АЦП, после старта преобразования нужно дождаться окончание преобразования и только потом можно читать значение АЦП из регистра

35 минут назад, Дмитрий Мамедиев сказал:

ADMUX|=0;//завершаем преобразование

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

по мимо этого, функция ADC_convert() для получения результата должна быть вызвана 64 раза подряд , а вы ее на разные каналы переключаете :)

Ссылка на комментарий
Поделиться на другие сайты

Вроде старался понятно все описывать, последовательность действий, подход к решению... ну, как хотите.

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

2 часа назад, IMXO сказал:

по мимо этого, функция ADC_convert() для получения результата должна быть вызвана 64 раза подряд , а вы ее на разные каналы переключаете 

это как? 

@ARV ADMUX = ADMUX & 0xF8; // надо подавить три младшие бита, ведь номер канала задается ими

а записывая 0xF8 т.е. 0b11111000 мы не испортим состояние других битов регистра ADMUX. и кстати нужно четыре последние бита

Ссылка на комментарий
Поделиться на другие сайты

что как?  функция сохраняет значение АЦП один раз за вызов, результат выдает один раз в 64 вызова.

а ты попеременно результаты разных каналов в одну функцию на усреднение загоняешь. 

для каждого канала должна быть своя функция конвертации. 

 

выбор канала и измерения АЦП правильней всего оформить в виде отдельной функции типа read_ADC(nomer_canal)

ADC_value+=ADCW;//складываем ADCW 64раза

заменить на

ADC_value+=read_ADC(nomer_canal);//складываем ADCW 64раза

 

 

Ссылка на комментарий
Поделиться на другие сайты

51 минуту назад, Дмитрий Мамедиев сказал:

и кстати нужно четыре последние бита

Ну, это от типа МК зависит. У меня и не было цели сделать весь код за вас. Я надеялся показать вам пример, как следует постепенно и шаг за шагом, от общего к частному решать задачу.

Изменено пользователем ARV

Если забанить всех, кто набрался смелости думать независимо, здорово будет на форуме - как на кладбище: тишина, птички поют...

Ссылка на комментарий
Поделиться на другие сайты

11 час назад, IMXO сказал:

что как?  функция сохраняет значение АЦП один раз за вызов, результат выдает один раз в 64 вызова.

точно? я полагал так: заходим в основную функцию

int main(void) проводим инициализацию 

port_ini();//портов
    lcd_ini();//дисплея
    ADC_ini();//ацп

затем идет цикл

 while (1)//в котором

ADMUX|=(1<<MUX2)|(1<<MUX0);ADMUX&=~((1<<MUX1)|(1<<MUX3));//ADC5 напряжение

ADC_convert();//заходим в функцию и ходим в ней пока не завершится 64 измерения
        lcd_poz(0);// выбираем позицию
        lcd_string(buffer);//записываем результат 64 измерений

потом выходим ADMUX|=0;//завершаем преобразование

выбираем канал ADC4 ток и повторяем все тоже самое

повторю вопрос

@ARV ADMUX = ADMUX & 0xF8; // надо подавить три младшие бита, ведь номер канала задается ими

а записывая 0xF8 т.е. 0b11111000 мы не испортим состояние других битов регистра ADMUX?

может все же

void current(void)
{
	ADMUX|=(1<<MUX2);ADMUX&=~((1<<MUX0)|(1<<MUX1)|(1<<MUX3));//ADC4
	ADCSRA|=(1 << ADSC); // запуск преобразования
}
void voltage(void)
{
    ADMUX|=(1<<MUX2)|(1<<MUX0);ADMUX&=~((1<<MUX1)|(1<<MUX3));//ADC5
	ADCSRA|=(1 << ADSC); // запуск преобразования
}

 

Ссылка на комментарий
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы публикуете как гость. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.
Примечание: Ваш пост будет проверен модератором, прежде чем станет видимым.

Гость
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Ответить в этой теме...

×   Вставлено с форматированием.   Восстановить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

Загрузка...
  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу

×
×
  • Создать...