Программирование на СИ. Помощь в написании кода.

Решаем проблемы новичков. Не знаете где задать вопрос?
Задавайте здесь! .. и другие разные вопросы
alvikagal
Posts: 4657
Joined: 18 Sep 2013, 01:58
Your CAR: ВАЗ-21099 1,5л.
SECU version: official SECU-3i TBZ
Location: Украина, Павлоград
Has thanked: 624 times
Been thanked: 1020 times
Contact:

Программирование на СИ. Помощь в написании кода.

Post by alvikagal »

Всем привет.
Обращаюсь к опытным программистам, а так же к начинающим.
Если Вам будет не сложно, то заходите в эту тему, чтобы помогать и исправлять ошибки начинающих писак кода, чтобы меньше было говнокода ;) .
За ранее всем участникам спасибо.


Пишу для ATmega8 в AtmelStudio (вернее собираю прошивку в нём, а пишу в Notepad++).
Сейчас изучаю приём/передачу данных по UART.
Стояла задача передать массив с 16 битными данными. С передачей массива немного разобрался, но вот передавать 16 бит через 8 битный буфер зашёл в тупик. Передаю только 8 бит данных из массива.
Как передавать 16 бит?
Пока решил таким способом.
Вот код:

Code: Select all

#define F_CPU 1000000UL 	// 1 MHz
#include <avr/io.h>
#include <uart.h>
#include <util/delay.h>
 
#define FOSC 1000000L		//Тактовая частота
#define BAUD 4800L 			//Скорость порта
#define MYUBRR FOSC/16/BAUD-1
 
void USART_Transmit(void);
void USART_Init( unsigned int ubrr);
 
uint16_t Massiv [16]		= {0,1,2,3,4,5,6,7,8,9,10,11,24,255,256,1140};	// массив 2 байтых данных
 
//Функция отправки данных
void USART_Transmit(void)
{
	for (int i=0;i<16;i++)
		{
			uint16_t Temp=Massiv[i];
			uint8_t TempH=Temp>>8;
			uint8_t TempL=Temp;
			while (!(UCSRA & (1<<UDRE))); //Ожидание опустошения буфера приема			
			UDR=TempH;
			while (!(UCSRA & (1<<UDRE))); //Ожидание опустошения буфера приема			
			UDR=TempL;
		}	
} 
 
//Инициализация модуля USART
void USART_Init( unsigned int ubrr)
{
//Задаем скорость работы USART
UBRRH = (unsigned char)(ubrr>>8);
UBRRL = (unsigned char)ubrr;

UCSRB = (1<<TXEN); //разрешёна передача
 
UCSRC=(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0); 
}

int main(void)
{
USART_Init (MYUBRR);

while(1)//вечный цикл
{ 
	USART_Transmit();
	_delay_ms(1000);
}	
}
Вот, что получилось:
UART.jpg
60-2/SECU-3iTBZ-v4.9/бенз-газ/ДПДЗ/ДТВ/УДК/ГБО-4/ВЗ.
Отчёт ГБО-4 на карбюратор с SECU-3i - http://secu-3.org/forum/viewtopic.php?f=19&t=1549
Продаю бортовой компьютер - viewtopic.php?p=47182#p47182
User avatar
STC
Posts: 13785
Joined: 30 Apr 2013, 23:41
Your CAR: AZLK 2140
SECU version: DIY SECU-3
Location: North Korea
Has thanked: 2143 times
Been thanked: 4310 times
Contact:

Re: Программирование на СИ. Помощь в написании кода.

Post by STC »

Code: Select all

uint8_t TempH=Temp>>8;
uint8_t TempL=Temp;
Все правильно сделал.
Author of the SECU-3™ project http://SECU-3.org. An open source engine control unit / Ignition control system, (C) 2007.
Клуб проекта в Facebook https://www.facebook.com/groups/secu3club
Клуб проекта ВКонтакте https://vk.com/secu3club (вступаем!)
KOT
Posts: 328
Joined: 10 May 2013, 14:23
Your CAR: I go on foot
SECU version: DIY SECU-3
Location: Запорожье
Has thanked: 6 times
Been thanked: 89 times

Re: Программирование на СИ. Помощь в написании кода.

Post by KOT »

Лично мне не нравится этот код, ожиданием пока освободится буфера. Лучше использовать прерывание по опустошению буфера передачи, в нем обрабатывать передачу массива.
Еще никто не дает гарантии, что компилятор не сделает сдвиг вправо 8 раз, а не воспользуется взятием старшего регистра; uint8_t TempH=Temp>>8;
Но это смотря для чего программа, недавно корректировал прошивку так там вобще общение по USB через прерывания, программная задержка и вычитка данных с нескольких датчиков 1-wire и работает)).

Еще мне не особо нравится как компилятор winAVR работает с флешь памятью больше 64 килобайт, с ИАРом попроще и ИАР создает более читабельный ассемблерный код, если нужно посмотреть че натвроил.

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

ПС сам только недавно пишу на СИ, до этого долгие трудные годы ассемблерного кода :D
Если что пишите в ЛС - там быстрей отвечу, чем смогу помогу.
Машина ЗАЗ 1103i, ГБО4. Управление ДВС: аналог Secu-3 и MegasquirtAVR модифицированное железо и своя прошивка.
User avatar
STC
Posts: 13785
Joined: 30 Apr 2013, 23:41
Your CAR: AZLK 2140
SECU version: DIY SECU-3
Location: North Korea
Has thanked: 2143 times
Been thanked: 4310 times
Contact:

Re: Программирование на СИ. Помощь в написании кода.

Post by STC »

Лично мне не нравится этот код, ожиданием пока освободится буфера. Лучше использовать прерывание по опустошению буфера передачи, в нем обрабатывать передачу массива.
Однозначно, но человек только учится. Использование прерываний будет следующим этапом развития программы.
Еще никто не дает гарантии, что компилятор не сделает сдвиг вправо 8 раз, а не воспользуется взятием старшего регистра; uint8_t TempH=Temp>>8;
Оптимизатор кода современных компиляторов работает четко, он не будет сдвигать 8 раз, он просто возьмет 2-й байт. Если не хочется сдвигать, то можно взять адрес, привести к указателю на uint8_t*, затем прибавить нужное число для смещения и взять значение по указателю. Вот так у меня сделано:

Code: Select all

#define _AB(src,rel) *(((unsigned char*)&(src)+(rel)))
Макрос берет значение N-го байта в указанной переменной.
Author of the SECU-3™ project http://SECU-3.org. An open source engine control unit / Ignition control system, (C) 2007.
Клуб проекта в Facebook https://www.facebook.com/groups/secu3club
Клуб проекта ВКонтакте https://vk.com/secu3club (вступаем!)
Samtorr
Posts: 342
Joined: 16 Jun 2015, 21:45
Your CAR: Golf II 1.3i
SECU version: DIY SECU-3T
Has thanked: 11 times
Been thanked: 114 times

Re: Программирование на СИ. Помощь в написании кода.

Post by Samtorr »

Есть еще несколько более хитрый вариант, но с подобными фокусами надо быть внимательным:

Code: Select all

#define F_CPU 1000000UL    // 1 MHz
#include <avr/io.h>
//#include <uart.h>
#include <util/delay.h>

#define FOSC 1000000L      //Тактовая частота
#define BAUD 4800L          //Скорость порта
#define MYUBRR FOSC/16/BAUD-1

void USART_Transmit(uint16_t*, uint8_t);
void USART_Init( unsigned int);

#define ARR_SIZE 16
uint16_t Massiv [ARR_SIZE]      = {0,1,2,3,4,5,6,7,8,9,10,11,24,255,256,1140};   // массив 2 байтых данных


//Функция отправки данных
void USART_Transmit(uint16_t* array_16bit, uint8_t arr_size_8bit)
{
	uint8_t * ptr_arr =(uint8_t*) array_16bit;
	for (uint8_t i=0;i<arr_size_8bit-1;i++)
	{
	
		while (!(UCSRA & (1<<UDRE))); //Ожидание опустошения буфера приема
		UDR=*(ptr_arr+i);
		
	}
}

//Инициализация модуля USART
void USART_Init( unsigned int ubrr)
{
	//Задаем скорость работы USART
	UBRRH = (unsigned char)(ubrr>>8);
	UBRRL = (unsigned char)ubrr;

	UCSRB = (1<<TXEN); //разрешёна передача
	
	UCSRC=(1<<URSEL)|(1<<UCSZ1)|(1<<UCSZ0);
}

int main(void)
{
	USART_Init (MYUBRR);

	while(1)//вечный цикл
	{
		USART_Transmit(Massiv,sizeof(Massiv));
		_delay_ms(1000);

	}
}
alvikagal
Posts: 4657
Joined: 18 Sep 2013, 01:58
Your CAR: ВАЗ-21099 1,5л.
SECU version: official SECU-3i TBZ
Location: Украина, Павлоград
Has thanked: 624 times
Been thanked: 1020 times
Contact:

Re: Программирование на СИ. Помощь в написании кода.

Post by alvikagal »

КОТ, спасибо. Без коментов мне пока сложно понять что написано ;-) . Сам стараюсь пока писать коменты, чтобы не забыть, что написал.
Насчёт прерывания уже думал. В подпрограмме передачи разрешать прерывание по UDR, по завершению передачи отключать прерывание. Попробую, как время будет.

Насчёт ИАР тоже мечтаю им собирать прошивку, но надо разобраться как им собирать, всё установлено т.к. для Secu им собираю.
60-2/SECU-3iTBZ-v4.9/бенз-газ/ДПДЗ/ДТВ/УДК/ГБО-4/ВЗ.
Отчёт ГБО-4 на карбюратор с SECU-3i - http://secu-3.org/forum/viewtopic.php?f=19&t=1549
Продаю бортовой компьютер - viewtopic.php?p=47182#p47182
User avatar
STC
Posts: 13785
Joined: 30 Apr 2013, 23:41
Your CAR: AZLK 2140
SECU version: DIY SECU-3
Location: North Korea
Has thanked: 2143 times
Been thanked: 4310 times
Contact:

Re: Программирование на СИ. Помощь в написании кода.

Post by STC »

Выкладываю готовый пример обработки UART при помощи прерываний c буферизацией приемника и передатчика (только бери да читай или подкидывай байты из/в очередь). Пример называется AVR306.
Attachments
avr306.zip
(7.38 KiB) Downloaded 189 times
AVR306-Using-the-AVR-UART-in-C.pdf
Sample software from atmel.com
(62.91 KiB) Downloaded 320 times
Author of the SECU-3™ project http://SECU-3.org. An open source engine control unit / Ignition control system, (C) 2007.
Клуб проекта в Facebook https://www.facebook.com/groups/secu3club
Клуб проекта ВКонтакте https://vk.com/secu3club (вступаем!)
KOT
Posts: 328
Joined: 10 May 2013, 14:23
Your CAR: I go on foot
SECU version: DIY SECU-3
Location: Запорожье
Has thanked: 6 times
Been thanked: 89 times

Re: Программирование на СИ. Помощь в написании кода.

Post by KOT »

Мои грабли начинающего:

Есть нюансы к примеру при передачи данных из флешь памяти контроллера, нужно писать отдельно функцию.
И когда я пытался передавать данные их меги128, которые находились в конце флешь памяти и компилировать это все ВинАВР то чуть себе мозг не сламал. В итоге заработало, но сколько же нужно работать руками и головой в ВинАВР меня смутило. Но впрочем че жаловатся на бесплатных компилятор.

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

О к стати у меня тоже вопрос: как сделать округление до целого при делении, чтоб ИАР не выполнял повторное деление? Проблема такова, либо нужно пользоватся функцией div, ldiv, в которой по умолчанию есть остаток от деления
ldiv_t res = ldiv(a, b);
if (res.rem > (b>>1)) res.quot++;
В первом случае недостаток если я хочу поделить 32 битно на 8 битное число и округлить, то из за встроеной либы я буду делить 32 на 32 бита

либо uint32_t rem = a % b;
uint32_t result = a / b;
if (rem > (b>>1)) result++;
Но в последнем случае ИАР выполняет деление 2 раза. Но может быть (нет никаких гарантий) ИАР будет делить 32 на 8 битное число.

Выход я конечно нашел - сделал асемблерную вставку и передавал ей указатель на структуру, но про переносимость кода можно забыть.
Машина ЗАЗ 1103i, ГБО4. Управление ДВС: аналог Secu-3 и MegasquirtAVR модифицированное железо и своя прошивка.
Samtorr
Posts: 342
Joined: 16 Jun 2015, 21:45
Your CAR: Golf II 1.3i
SECU version: DIY SECU-3T
Has thanked: 11 times
Been thanked: 114 times

Re: Программирование на СИ. Помощь в написании кода.

Post by Samtorr »

KOT wrote: О к стати у меня тоже вопрос: как сделать округление до целого при делении, чтоб ИАР не выполнял повторное деление?
Если правильно помню, что-то вроде:

Code: Select all

uint32_t result = (a+(b>>1))/b;
Только если числа знаковые, надо быть внимательным, есть нюансы
KOT
Posts: 328
Joined: 10 May 2013, 14:23
Your CAR: I go on foot
SECU version: DIY SECU-3
Location: Запорожье
Has thanked: 6 times
Been thanked: 89 times

Re: Программирование на СИ. Помощь в написании кода.

Post by KOT »

О спасибо, нужно попробовать. Знаковых чисел пока боюсь, для меня мне каждый раз за справочником нужно лезть что понять в каком случае как преобразовываются числа.
Машина ЗАЗ 1103i, ГБО4. Управление ДВС: аналог Secu-3 и MegasquirtAVR модифицированное железо и своя прошивка.
Post Reply

Return to “Решение проблем. Новичкам сюда!”