Todas as etapas da criação de um robô para seguir a linha ou como coletar todos os ancinhos com o STM32

Olá Habr!

Esta publicação discutirá minha experiência na criação de um robô para competições, ou seja, seguindo a linha. Vou tentar contar todas as etapas, desde o projeto de circuitos e a solicitação de uma placa de circuito impresso até um algoritmo e um programa.

A corrida de robôs ao longo da linha tem sido um teste básico para a robótica iniciante. Quase todas as competições regionais e internacionais incluem essa direção, portanto, com um robô, você pode participar em quase todos os lugares. No primeiro ano de universidade, essa foi a próxima tarefa após o primeiro piscar do LED.

Formulação do problema


Qualquer desenvolvimento começa com uma atribuição técnica; no nosso caso, os regulamentos de competição das competições de Robofinistas são usados ​​como TK . Nele, estamos interessados ​​nos requisitos de dimensões (não mais que 25x25x25 cm) e no peso do robô (não mais que 1 kg), bem como no formato da pista e sua largura de 1,5 cm. Dependendo da complexidade da pista, é definido um limite para o tempo mínimo de viagem (60 s )

A tarefa é clara: precisamos de um robô capaz de detectar uma linha e conduzi-la do início ao fim em um período mínimo de tempo.

Projeto conceitual


O núcleo do robô é o microcontrolador STM32F103C8T6, lê as informações da pista usando sensores ópticos QRE1113 e controla os motores através do driver A3906. Para maior clareza, cada sensor recebe seu próprio LED, assim que o sensor "vê" a linha preta, o LED acende, isso simplifica bastante o processo de depuração. Como fonte de alimentação, foram escolhidas uma bateria Li-po do tipo 18650 com tensão de 3,7 V e um chip de carga LTC4054ES5. Porém, para a operação correta dos motores, são necessários pelo menos 6 V; portanto, instalei o conversor de voltagem LM2577 DC-DC e o LM1117 linear 3,3 V para fornecer a parte lógica.

imagem

É hora de falar sobre os primeiros erros. Misturei desatentamente um par de pinos de energia do microcontrolador e, na inicialização, passei várias horas procurando um problema, como resultado, tive que mordê-los. Surgiu então o problema de que nem todos os sensores reagiram à linha, minha primeira reação foi que eu queimei o microcontrolador ou as pernas quebradas da fonte de alimentação no último estágio não eram supérfluas ou que soldava mal os contatos.

Usando um osciloscópio, descobri que o problema estava nos próprios sensores; dos 10 sensores 4 não funcionavam, substituindo-os por novos, o seguinte problema apareceu - três LEDs indicadores PB3, PB4 e PA15 não funcionaram. Depois de reescrever e examinar todo o meu código, não consegui encontrar diferenças na inicialização de portas que não funcionavam e funcionavam em todos os lugares: Push Pull, 10 MHz e clock ativados. Tendo examinado o oscilograma, notei que um pino foi atraído pelo sinal de mais e, mesmo no modo de depuração, ele não foi redefinido, percebi que o microcontrolador estava queimado. Fechando a folha de dados, notei que, por uma estranha coincidência, esses pinos são usados ​​para JTAG e eu uso SWD, mas, tendo decidido que não iria piorar, comecei a pesquisar no Google como desativar o JTAG e isso realmente ajudou (veja abaixo).

Mini-conclusão, verifique a nutrição correta em todas as etapas para que você não pense que algo queimou mais tarde ou não funcione por outros motivos.

O que precisa ser aprimorado no esquema


Adivinhei colocar o chip de carga, mas esqueci a descarga, embora não seja menos importante; além disso, em 2020, coloquei mini USB em vez do tipo C. Também gostaria de usar o microcontrolador para monitorar a carga da bateria, mas não tinha pinos ADC gratuitos. Ao escolher um driver, contei com as características do motor de 6 V e 700 mA, mas não levei em conta que sua tensão máxima é de 9 V, mas gostaria de 12 V.

Design de PCB


Antes de tudo, foi decidido determinar o tamanho e a forma do PP e da plataforma de tempo parcial do robô. Lembramos que, de acordo com os regulamentos, as dimensões não devem exceder 25x25 cm, mas também vale a pena considerar que, ao encomendar software em uma fábrica (PCBWay) na China, placas acima de 10x10 cm aumentam significativamente de preço. Eu acho que a escolha das dimensões é óbvia.

Em seguida, você deve distribuir os sensores simetricamente (na parte inferior da placa), levando em consideração a largura da linha e, em seguida, os elementos mais pesados, como motores e bateria. Os LEDs são montados no mesmo eixo que os sensores correspondentes, no lado oposto da placa. Por conveniência, instalamos um botão liga / desliga e um conector de carga da bateria na parte traseira da plataforma. Os elementos restantes são ajustados mais perto do centro.

Depois de cortar as bordas vazias do tabuleiro em vez de um quadrado, é obtida uma imagem completamente decente da plataforma do robô.

O conselho acabou sendo bastante simples devido ao pequeno número de elementos únicos. Espessura mínima total da esteira de 2 camadas 0,25 mm. Depois de salvar a placa-mãe no formato Gerber, enviei-a para a fábrica selecionando a cor branca da máscara e não tive problemas especiais.

imagem

E uma vista 3D do quadro com os elementos. A foto ao vivo estará no final.

imagem

O que precisa ser finalizado no quadro


Instalei alguns elementos muito próximos ao estojo de plástico da bateria e foi difícil soldá-los e ainda mais difícil de soldar. Esqueci de assinar a pinagem do conector de programação SWD, UART e motores, como resultado, tenho que olhar para o circuito.

Compra de componentes


O desenvolvimento de tudo o que é novo requer os custos relativos mais altos por unidade de mercadoria, você pode e deve tentar economizar, mas dentro de limites razoáveis, para que os pedidos de sensores de linha no Aliexpress sejam 4,5 vezes mais baratos que em uma loja de eletrônicos, paguei com o fato de que cerca de 40% deles eram não está funcionando, e este é o momento de encontrar e corrigir erros. Por outro lado, eu não queria comprar o chip LM2577 por 460 rublos, enquanto o módulo com todas as cintas custa 100 rublos. Eu pedi parte das peças para Ali, outra parte em uma loja local.

Aqui estão meus custos estimados de componentes:

  1. Motores e rodas - 800 rublos;
  2. Bateria e estojo - 400 rublos;
  3. Carregador - 300 rublos;
  4. Microcontrolador e motorista - 200 rublos;
  5. Estabilizadores - 400 rublos;
  6. Taxas 10 unidades com entrega 2400/10 = 240 rublos por unidade;
  7. O resto do chicote de fios - 150 rublos.

Total: 4650 rublos no custo total de componentes ou 2490 se você subtrair o preço das nove placas restantes não utilizadas.

UPD: Comecei a calcular o custo de placas e instalação de componentes e por lote de 10 unidades. o preço aumentou para 16.000 rublos. Existem idéias de como otimizar, mas essa é outra hora.

Algoritmo


O algoritmo implementou o mais simples: se a linha estiver, por exemplo, no lado direito, o mecanismo direito começará a desacelerar e, quanto mais próxima a linha da borda, mais lento o motor funcionará, até parar completamente, enquanto o motor esquerdo funcionará na velocidade máxima. tentando virar em direção à linha. Uma situação semelhante se a linha estiver no lado esquerdo. Assim, o robô tenta se estabilizar, retornando a linha aos sensores centrais.

imagem

Algoritmo em imagens
image

image

image

image

image

Também é necessário prever outras situações, por exemplo, se todos os sensores detectaram uma linha, isso provavelmente significa que estamos atravessando a interseção e provavelmente precisamos ir além. Ou se apenas os sensores extremos detectaram a linha, chegamos à linha de chegada e precisamos parar.

Programação


Nesta fase, você precisa pegar um pandeiro e bater nele.

Escreveu no Atollic True Studio usado apenas CMSIS e FreeRTOS. Passei meia semana para iniciar o FreeRTOS, fez tudo de acordo com as instruções, mas detectou erros durante o processo de compilação, corrigiu 1, 63 apareceu ou não houve erros, mas o controlador travou. Em algum momento, percebi que o nível da minha mão ... atingia alturas sem precedentes e era hora de pensar no que estava fazendo e no que estava fazendo. Graças a camaradas da comunidade, comecei a cavar na direção certa e, em várias fontes redesenhadas, encontrei várias que me ajudaram (veja abaixo). Como resultado, consegui derrotar o FreeRTOS e até agora o usei em 0%, porque reuni tudo em uma tarefa.

Aqui o vídeo e o artigo e este artigo me ajudaram mais .

O sistema de temporização, aqui ainda não tenho certeza se funciona como deveria. Depois de sintonizar a frequência do sistema através do PLL para 48 MHz, vi 24 MHz (/ 2) no pé do MCO; não, vi um sinal de 22-25 MHz remotamente parecido com um seno. Ao mesmo tempo, configurei 48 MHz na configuração do FreeRTOS e configurei o atraso para 500 ms e executei-o, vi que meus 500 ms se transformaram em 300, passou por todas as configurações possíveis, notei que só recebi o alvo 500 ms quando com o clock do gerador interno em 8 MHz e o valor da frequência A configuração do FreeRTOS não afetou nada, defina 1 Hz e 48 GHz. Decidi retornar 48 MHz e trabalhar com 300 ms, após o que reiniciei acidentalmente o True Studio e funcionou maravilhosamente como eu pensava, e desde o início eu já havia indicado todos os caminhos de arquivo.

O problema de JTAG e LEDs, descrito acima, a solução foi encontrada aquie você só precisa desativá-lo, liberando assim os pinos PB3, PB4 e PA15.

AFIO->MAPR |= AFIO_MAPR_SWJ_CFG_JTAGDISABLE;

A estrutura do projeto é aproximadamente a seguinte:

Muito código
1. ;

void RCC_Init(void) {						// Quartz 16 MHz

	RCC->CFGR |= RCC_CFGR_PLLXTPRE;	// PLLXTPRE set divider (/2) = 8 MHz

	RCC->CR |= RCC_CR_HSEON;			// On HSE
	while (!(RCC->CR & RCC_CR_HSERDY)) {
	};

	RCC->CFGR |= (RCC_CFGR_PLLMULL6);     // Setup PLLMULL set multiplier (x6) = 48 MHz

	RCC->CFGR |= RCC_CFGR_PLLSRC;		// PLLSRC set HSE

	RCC->CR |= RCC_CR_PLLON;			// ON PLL
	while (!(RCC->CR & RCC_CR_PLLRDY)) {
	};

	FLASH->ACR &= ~FLASH_ACR_LATENCY;	// Setup FLASH
	FLASH->ACR |= FLASH_ACR_LATENCY_1;

	RCC->CFGR |= RCC_CFGR_HPRE_DIV1; 	// Set not divider (AHB) = 48 MHz
	RCC->CFGR |= RCC_CFGR_PPRE1_DIV2; 	// Set divider (/2) (APB1) = 24 MHz
	RCC->CFGR |= RCC_CFGR_PPRE2_DIV1; 	// Set not divider (APB2) = 48 MHz

	RCC->CFGR &= ~RCC_CFGR_SW; 		// Setup SW, select PLL
	RCC->CFGR |= RCC_CFGR_SW_PLL;

//	RCC->CFGR |= RCC_CFGR_MCO_SYSCLK;

}

2. , , , Push Pull;


void GPIO_Init(void) {

	RCC->APB2ENR |=	(RCC_APB2ENR_IOPAEN | RCC_APB2ENR_IOPBEN | RCC_APB2ENR_AFIOEN); // Enable clock portA, portB and Alternative function

	AFIO->MAPR |= AFIO_MAPR_SWJ_CFG_JTAGDISABLE;	// Disable JTAG for pins B3,B4 and A15

	//---------------LED: (B3 to B9; A11, A12, A15); Output mode: Push Pull, max 10 MHz---------------//

	GPIOB->CRL &= ~(GPIO_CRL_CNF3 | GPIO_CRL_MODE3);	// Setup B3 pins PP, 10MHz
	GPIOB->CRL |= GPIO_CRL_MODE3_0;

	GPIOB->CRL &= ~(GPIO_CRL_CNF4 | GPIO_CRL_MODE4);	// Setup B3 pins PP, 10MHz
	GPIOB->CRL |= GPIO_CRL_MODE4_0;

	GPIOB->CRL &= ~(GPIO_CRL_CNF5 | GPIO_CRL_MODE5);	// Setup B4 pins PP, 10MHz
	GPIOB->CRL |= GPIO_CRL_MODE5_0;

	GPIOB->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_MODE6);	// Setup B5 pins PP, 10MHz
	GPIOB->CRL |= GPIO_CRL_MODE6_0;

	GPIOB->CRL &= ~(GPIO_CRL_CNF7 | GPIO_CRL_MODE7);	// Setup B6 pins PP, 10MHz
	GPIOB->CRL |= GPIO_CRL_MODE7_0;

	GPIOB->CRH &= ~(GPIO_CRH_CNF8 | GPIO_CRH_MODE8);	// Setup B7 pins PP, 10MHz
	GPIOB->CRH |= GPIO_CRH_MODE8_0;

	GPIOB->CRH &= ~(GPIO_CRH_CNF9 | GPIO_CRH_MODE9);	// Setup B8 pins PP, 10MHz
	GPIOB->CRH |= GPIO_CRH_MODE9_0;

	GPIOA->CRH &= ~(GPIO_CRH_CNF11 | GPIO_CRH_MODE11);	// Setup A11 pins PP, 10MHz
	GPIOA->CRH |= GPIO_CRH_MODE11_0;

	GPIOA->CRH &= ~(GPIO_CRH_CNF12 | GPIO_CRH_MODE12);	// Setup A12 pins PP, 10MHz
	GPIOA->CRH |= GPIO_CRH_MODE12_0;

	GPIOA->CRH &= ~(GPIO_CRH_CNF15 | GPIO_CRH_MODE15);	// Setup A15 pins PP, 10MHz
	GPIOA->CRH |= GPIO_CRH_MODE15_0;

	//--Motors: (A8 to A10; B12 to B15); Output and input (B12, B13) mode: Alternative function, PWM - Push Pull, max 10 MHz--//

	GPIOB->CRH &= ~(GPIO_CRH_CNF12 | GPIO_CRH_MODE12);		// Setup B12 pins analog input

	GPIOB->CRH &= ~(GPIO_CRH_CNF13 | GPIO_CRH_MODE13);		// Setup B13 pins analog input

	GPIOB->CRH &= ~(GPIO_CRH_CNF14   | GPIO_CRH_MODE14);	// Setup B14 pins PP, 10MHz
	GPIOB->CRH |=  GPIO_CRH_MODE14_0;

	GPIOB->CRH &= ~(GPIO_CRH_CNF15   | GPIO_CRH_MODE15);	// Setup B15 pins PP, AF, 10MHz
	GPIOB->CRH |=  (GPIO_CRH_CNF15_1 | GPIO_CRH_MODE15_0);

	GPIOA->CRH &= ~(GPIO_CRH_CNF8 | GPIO_CRH_MODE8);		// Setup A10 pins PP, 10MHz
	GPIOA->CRH |= GPIO_CRH_MODE8_0;

	GPIOA->CRH &= ~(GPIO_CRH_CNF9    | GPIO_CRH_MODE9);		// Setup A9 pins PP, AF, 10MHz
	GPIOA->CRH |=  (GPIO_CRH_CNF9_1  | GPIO_CRH_MODE9_0);

	GPIOA->CRH &= ~(GPIO_CRH_CNF10 | GPIO_CRH_MODE10);		// Setup A10 pins PP, 10MHz
	GPIOA->CRH |= GPIO_CRH_MODE10_0;

	//--UART3: (B10 - TX; B11 - RX); Output mode: Alternative function, UART - Push Pull, max 10 MHz--//

	GPIOB->CRH &= ~(GPIO_CRH_CNF10   | GPIO_CRH_MODE10);	// Setup B10 pins PP, AF, 10MHz
	GPIOB->CRH |=  (GPIO_CRH_CNF10_1 | GPIO_CRH_MODE10_0);

	GPIOB->CRH &= ~(GPIO_CRH_CNF11   | GPIO_CRH_MODE11);	// Setup B11 pins PP, AF, 10MHz
	GPIOB->CRH |=  (GPIO_CRH_CNF11_1 | GPIO_CRH_MODE11_0);

	//--Optical sensors: (B0, B1, A0 - A7); Input mode: Analog--//

	GPIOB->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_MODE0);	// Setup B0 pins analog input
	GPIOB->CRL &= ~(GPIO_CRL_CNF1 | GPIO_CRL_MODE1);	// Setup B1 pins analog input
	GPIOA->CRL &= ~(GPIO_CRL_CNF0 | GPIO_CRL_MODE0);	// Setup A0 pins analog input
	GPIOA->CRL &= ~(GPIO_CRL_CNF1 | GPIO_CRL_MODE1);	// Setup A1 pins analog input
	GPIOA->CRL &= ~(GPIO_CRL_CNF2 | GPIO_CRL_MODE2);	// Setup A2 pins analog input
	GPIOA->CRL &= ~(GPIO_CRL_CNF3 | GPIO_CRL_MODE3);	// Setup A3 pins analog input
	GPIOA->CRL &= ~(GPIO_CRL_CNF4 | GPIO_CRL_MODE4);	// Setup A4 pins analog input
	GPIOA->CRL &= ~(GPIO_CRL_CNF5 | GPIO_CRL_MODE5);	// Setup A5 pins analog input
	GPIOA->CRL &= ~(GPIO_CRL_CNF6 | GPIO_CRL_MODE6);	// Setup A6 pins analog input
	GPIOA->CRL &= ~(GPIO_CRL_CNF7 | GPIO_CRL_MODE7);	// Setup A7 pins analog input

}

3. DMA, ;


extern uint16_t adc_buf[10];

void ADC_Init(void) {

	 RCC->AHBENR |= RCC_AHBENR_DMA1EN;
	 DMA1_Channel1->CPAR = (uint32_t) &ADC1->DR;		//   
	 DMA1_Channel1->CMAR = (unsigned int) adc_buf;   	//    

	 DMA1_Channel1->CNDTR = 10;			  				//    
	 DMA1_Channel1->CCR &= ~DMA_CCR_EN;			   		//   
	 DMA1_Channel1->CCR |= DMA_CCR_MSIZE_0;		  		//   16 bit
	 DMA1_Channel1->CCR |= DMA_CCR_PSIZE_0;		 		//   16 bit
	 DMA1_Channel1->CCR |= DMA_CCR_MINC;			  	// memory increment mode
	 DMA1_Channel1->CCR |= DMA_CCR_CIRC;
	 DMA1_Channel1->CCR |= DMA_CCR_TCIE;				//    
	 DMA1_Channel1->CCR |= DMA_CCR_EN;				  	//  
	 NVIC_SetPriority(DMA1_Channel1_IRQn, 10);
	 NVIC_EnableIRQ(DMA1_Channel1_IRQn);

	 RCC->CFGR &= ~RCC_CFGR_ADCPRE;
	 RCC->CFGR |= RCC_CFGR_ADCPRE_DIV8;
	 RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;

	 ADC1->SQR3 = 0;			// 1
	 ADC1->SQR3 |= 1 << 5;		// 2     
	 ADC1->SQR3 |= 2 << 10;		// 3
	 ADC1->SQR3 |= 3 << 15;		// 4
	 ADC1->SQR3 |= 4 << 20;		// 5
	 ADC1->SQR3 |= 5 << 25;		// 6
	 ADC1->SQR2 = 6;			// 7
	 ADC1->SQR2 |= 7 << 5;		// 8
	 ADC1->SQR2 |= 8 << 10;		// 9
	 ADC1->SQR2 |= 9 << 15;		// 10

	 ADC1->CR2 = ADC_CR2_EXTSEL_0 | ADC_CR2_EXTSEL_1 | ADC_CR2_EXTSEL_2
	 | ADC_CR2_EXTTRIG;
	 ADC1->SMPR1 = 0;					 		//   
	 ADC1->SMPR2 = 0;					 		//
	 ADC1->SMPR2 |= (uint32_t) (6 << (0 * 3)); 	// 0,   6 
	 ADC1->SMPR2 |= (uint32_t) (6 << (1 * 3)); 	// 1,   6 
	 ADC1->SMPR2 |= (uint32_t) (6 << (2 * 3)); 	// 2,   6 
	 ADC1->SMPR2 |= (uint32_t) (6 << (3 * 3)); 	// 3,   6 
	 ADC1->SMPR2 |= (uint32_t) (6 << (4 * 3)); 	// 4,   6 
	 ADC1->SMPR2 |= (uint32_t) (6 << (5 * 3)); 	// 5,   6 
	 ADC1->SMPR2 |= (uint32_t) (6 << (6 * 3)); 	// 6,   6 
	 ADC1->SMPR2 |= (uint32_t) (6 << (7 * 3)); 	// 7,   6 
	 ADC1->SMPR2 |= (uint32_t) (6 << (8 * 3)); 	// 8,   6 
	 ADC1->SMPR2 |= (uint32_t) (6 << (9 * 3)); 	// 9,   6 
	 ADC1->SMPR1 |= (uint32_t) (6 << (0 * 3)); 	// 10,   6 

	 ADC1->CR2 |= ADC_CR2_ADON;

	 ADC1->CR2 |= ADC_CR2_RSTCAL;
	 while ((ADC1->CR2 & ADC_CR2_RSTCAL) == ADC_CR2_RSTCAL) {
	 }

	 ADC1->CR2 |= ADC_CR2_CAL;

	 while ((ADC1->CR2 & ADC_CR2_RSTCAL) == ADC_CR2_CAL) {
	 }

	 ADC1->SQR1 |= 9 << 20;						//  
	 ADC1->CR1 |= ADC_CR1_SCAN;					//  
	 ADC1->CR2 |= ADC_CR2_DMA;				  	// DMA on
	 // ADC1->CR2 |= ADC_CR2_CONT;

	 //  ADC1->CR2 |= ADC_CR2_SWSTART;
	 ADC1->CR2 |= ADC_CR2_ADON;

}

4. , ;

void TIM1_Init(void) {

	/*************** Enable TIM1 (CH1) ***************/
	RCC->APB2ENR |= RCC_APB2ENR_TIM1EN;
	TIM1->CCER = 0;										//  CCER ( )
	TIM1->ARR = 1000; 									//  ,     
	TIM1->PSC = 48 - 1;                					// 
	TIM1->BDTR |= TIM_BDTR_MOE;     					//     

	TIM1->CCR2 = 0; 									//       (  0  TIM1->ARR)
	TIM1->CCMR1 |= TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2; //     
	TIM1->CCER |= (TIM_CCER_CC2P |TIM_CCER_CC2E); 		//        
														//   -   3
	TIM1->CCR3 = 0; 									//       (  0  TIM1->ARR)
	TIM1->CCMR2 |= TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2; //     
	TIM1->CCER |= TIM_CCER_CC3NE; 						//        
	TIM1->CCER &= ~TIM_CCER_CC3NP;

	TIM1->CR1 |= TIM_CR1_CEN;
}

5. UART , ;

void UART3_Init(void) {

	RCC->APB1ENR |= RCC_APB1ENR_USART3EN;

	USART3->BRR = 0xD0;						// Speed = 115200; (24 000 000 + (115200 / 2)) / 115200 = 208 -> 0xD0

	USART3->CR1 |= USART_CR1_TE | USART_CR1_RE | USART_CR1_UE;

//	USART3->CR1 |= USART_CR1_RXNEIE;
//	NVIC_EnableIRQ(USART3_IRQn);
}

void UART3_Send(char chr) {
	while (!(USART3->SR & USART_SR_TC))
		;
	USART3->DR = chr;
}

void UART3_Send_String(char* str) {
	uint8_t i = 0;

	while (str[i])
		UART3_Send(str[i++]);
}

void UART3_Send_Number_Float(float data){

	char str[100];

	char *tmpSign = (data < 0) ? "-" : "";
	float tmpVal = (data < 0) ? -data : data;

	int tmpInt1 = tmpVal;                  // Get the integer (678).
	float tmpFrac = tmpVal - tmpInt1;      // Get fraction (0.0123).
	int tmpInt2 = trunc(tmpFrac * 10);  // Turn into integer (123).	int tmpInt2 = trunc(tmpFrac * 10000)

	// Print as parts, note that you need 0-padding for fractional bit.

	sprintf (str, "%s%d.%01d", tmpSign, tmpInt1, tmpInt2);

	UART3_Send_String(str);
}

6. FreeRtos config

/*
 * FreeRTOS Kernel V10.3.1
 * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 * http://www.FreeRTOS.org
 * http://aws.amazon.com/freertos
 *
 * 1 tab == 4 spaces!
 */

#ifndef FREERTOS_CONFIG_H
#define FREERTOS_CONFIG_H

/* Library includes. */
//#include "stm32f10x_lib.h"
/*-----------------------------------------------------------
 * Application specific definitions.
 *
 * These definitions should be adjusted for your particular hardware and
 * application requirements.
 *
 * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE
 * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. 
 *
 * See http://www.freertos.org/a00110.html
 *----------------------------------------------------------*/

#define vPortSVCHandler SVC_Handler		// fix problem
#define xPortPendSVHandler PendSV_Handler
#define vPortSVCHandler SVC_Handler
#define xPortSysTickHandler SysTick_Handler


#define configUSE_PREEMPTION		1
#define configUSE_IDLE_HOOK			0
#define configUSE_TICK_HOOK			1
#define configCPU_CLOCK_HZ			( ( unsigned long ) 48000000 )
#define configTICK_RATE_HZ			( ( TickType_t ) 1000 )
#define configMAX_PRIORITIES		( 5 )
#define configMINIMAL_STACK_SIZE	( ( unsigned short ) 32 )
#define configTOTAL_HEAP_SIZE		( ( size_t ) ( 17 * 1024 ) )
#define configMAX_TASK_NAME_LEN		( 16 )
#define configUSE_TRACE_FACILITY	0
#define configUSE_16_BIT_TICKS		0
#define configIDLE_SHOULD_YIELD		1
#define configUSE_MUTEXES			1

/* Co-routine definitions. */
#define configUSE_CO_ROUTINES 		0
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

/* Set the following definitions to 1 to include the API function, or zero
 to exclude the API function. */

#define INCLUDE_vTaskPrioritySet		1
#define INCLUDE_uxTaskPriorityGet		1
#define INCLUDE_vTaskDelete				1
#define INCLUDE_vTaskCleanUpResources	0
#define INCLUDE_vTaskSuspend			1
#define INCLUDE_vTaskDelayUntil			1
#define INCLUDE_vTaskDelay				1

/* This is the raw value as per the Cortex-M3 NVIC.  Values can be 255
 (lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY 		255
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
 See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY 	191 /* equivalent to 0xb0, or priority 11. */

/* This is the value being used as per the ST library which permits 16
 priority values, 0 to 15.  This must correspond to the
 configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest
 NVIC value of 255. */
#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY	15

#endif /* FREERTOS_CONFIG_H */


7. main;

#include "main.h"

void vTaskUART2(void *argument);
void vTaskConvADC(void *argument);

uint32_t Motor(int32_t which, int32_t speed, int32_t turn);

void IndicatorLED(uint32_t number, uint32_t status);


//define      typedef enum :)
#define MotorLeft		1
#define MotorRight		0
#define MoveBack		0
#define MoveForward		1
#define MaxSpeedMotor	1000

uint16_t adc_buf[10];				// Buffer DMA ADC sensors
uint16_t SensWhiteOrBlack[10];		// Buffer sensors white or black

int main(void) {

	RCC_Init();
	GPIO_Init();
	TIM1_Init();
	UART3_Init();
	ADC_Init();

	xTaskCreate(vTaskUART2, "UART", 512, NULL, 1, NULL);
	xTaskCreate(vTaskConvADC, "ADC", 128, NULL, 1, NULL);

	UART3_Send_String("Start program\r\n");

	GPIOA->BSRR |= GPIO_BSRR_BS10;	// Motor enable

	vTaskStartScheduler();

	while (1) {

	}
}
/*************************************Tasks***************************************/

void vTaskUART2(void *argument) {

	while (1) {

//-------------------Read sensors from buff DMA ADC, and print to UART3-------------//

		for (uint32_t i = 0; i <= 9; i++)
		{

			if (adc_buf[i] <= 300)		// value sens is white
			{
				IndicatorLED(i, 0);
				SensWhiteOrBlack[i] = 0;
			} else						// else value sens is black
			{
				IndicatorLED(i, 1);
				SensWhiteOrBlack[i] = 1;
			}

//			UART3_Send_Number_Float(SensWhiteOrBlack[i]);
//			UART3_Send_String("\t");
		}
///		UART3_Send_String("\r\n");

//-----------------------------------------Move-----------------------------------------//


		//----Normal mode----//
		float devMotorLeft  = 1;
		float devMotorRight = 1;
		uint32_t flagSizeBuf = 0;

		for (int32_t i = 6; i <= 9; i++)
		{
			if (SensWhiteOrBlack[i])
			{
				flagSizeBuf = 1;

				if(i == 6 && SensWhiteOrBlack[i] == 1)
				{
					devMotorRight = 0.75;
				}
				else if(i == 7 && SensWhiteOrBlack[i] == 1)
				{
					devMotorRight = 0.5;
				}
				else if(i == 8 && SensWhiteOrBlack[i] == 1)
				{
					devMotorRight = 0.25;
				}
				else if(i == 9 && SensWhiteOrBlack[i] == 1)
				{
					devMotorRight = 0.0;
				}
			}
		}

		for(int32_t i = 5; i >= 0; i--)
		{
			if(SensWhiteOrBlack[i])
			{
				flagSizeBuf = 1;

				if(i == 3 && SensWhiteOrBlack[i] == 1)
				{
					devMotorLeft = 0.75;
				}
				else if(i == 2 && SensWhiteOrBlack[i] == 1)
				{
					devMotorLeft = 0.5;
				}
				else if(i == 1 && SensWhiteOrBlack[i] == 1)
				{
					devMotorLeft = 0.25;
				}
				else if(i == 0 && SensWhiteOrBlack[i] == 1)
				{
					devMotorLeft = 0.0;
				}
			}
		}

		if(!flagSizeBuf)
		{
			devMotorLeft  = 0;
			devMotorRight = 0;
		}

		Motor(MotorRight, (int32_t)(MaxSpeedMotor * (float)devMotorRight), MoveForward);
		Motor(MotorLeft,  (int32_t)(MaxSpeedMotor * (float)devMotorLeft),  MoveForward);


/*
		if (adc_buf[0] > 1000)
		{
			Motor(MotorLeft, 500, MoveBack);
		}
		else if (adc_buf[0] <= 1000)
		{
			Motor(MotorLeft, 0, MoveForward);
		}

		if (adc_buf[9] > 1000)
		{
			Motor(MotorRight, 500, MoveBack);//Motor(0, 500, 1);	MotorRight	MoveBack
		}
		else if (adc_buf[9] <= 1000)
		{
			Motor(MotorRight, 0, MoveForward);
		}
*/

		vTaskDelay(50);
	}
}
void vTaskConvADC(void *argument) {

	while (1) {

		// just void :)

		vTaskDelay(5000);
	}

}
/**********************************Function*************************************/

void IndicatorLED(uint32_t number, uint32_t status) {

	if (number == 0 && status == 0) {
		GPIOB->BSRR |= GPIO_BSRR_BS9;
	}

	else if (number == 0 && status == 1) {
		GPIOB->BSRR |= GPIO_BSRR_BR9;
	}

	if (number == 1 && status == 0) {
		GPIOB->BSRR |= GPIO_BSRR_BS8;
	}

	else if (number == 1 && status == 1) {
		GPIOB->BSRR |= GPIO_BSRR_BR8;
	}

	if (number == 2 && status == 0) {
		GPIOB->BSRR |= GPIO_BSRR_BS7;
	}

	else if (number == 2 && status == 1) {
		GPIOB->BSRR |= GPIO_BSRR_BR7;
	}

	if (number == 3 && status == 0) {
		GPIOB->BSRR |= GPIO_BSRR_BS6;
	}

	else if (number == 3 && status == 1) {
		GPIOB->BSRR |= GPIO_BSRR_BR6;
	}

	if (number == 4 && status == 0) {
		GPIOB->BSRR |= GPIO_BSRR_BS5;
	}

	else if (number == 4 && status == 1) {
		GPIOB->BSRR |= GPIO_BSRR_BR5;
	}

	if (number == 5 && status == 0) {
		GPIOB->BSRR |= GPIO_BSRR_BS4;
	}

	else if (number == 5 && status == 1) {
		GPIOB->BSRR |= GPIO_BSRR_BR4;
	}

	if (number == 6 && status == 0) {
		GPIOB->BSRR |= GPIO_BSRR_BS3;
	}

	else if (number == 6 && status == 1) {
		GPIOB->BSRR |= GPIO_BSRR_BR3;
	}

	if (number == 7 && status == 0) {
		GPIOA->BSRR |= GPIO_BSRR_BS15;
	}

	else if (number == 7 && status == 1) {
		GPIOA->BSRR |= GPIO_BSRR_BR15;
	}

	if (number == 8 && status == 0) {
		GPIOA->BSRR |= GPIO_BSRR_BS12;
	}

	else if (number == 8 && status == 1) {
		GPIOA->BSRR |= GPIO_BSRR_BR12;
	}

	if (number == 9 && status == 0) {
		GPIOA->BSRR |= GPIO_BSRR_BS11;
	}

	else if (number == 9 && status == 1) {
		GPIOA->BSRR |= GPIO_BSRR_BR11;
	}

}

uint32_t Motor(int32_t which, int32_t speed, int32_t turn) {

	if (which == 0)	//Right motor
	{
		UART3_Send_Number_Float(speed);
		UART3_Send_String("\t");

		if (speed > 0 && speed <= 1000)
		{
			TIM1->CCR3 = speed;
			if (turn == 0) 	// back
			{
				GPIOB->BSRR |= GPIO_BSRR_BS14;
			}
			else			//forward
			{
				GPIOB->BSRR |= GPIO_BSRR_BR14;
			}
		}
		else		//disable motor
		{
			TIM1->CCR3 = 0;
			GPIOB->BSRR |= GPIO_BSRR_BR14;

		}

	}

	else if (which == 1)	//left motor
	{
		UART3_Send_Number_Float(speed);
		UART3_Send_String("\r\n");

		if (speed > 0 && speed <= 1000)
		{
			TIM1->CCR2 = speed;
			if (turn == 1) 	// back
			{
				GPIOA->BSRR |= GPIO_BSRR_BS8;
			}
			else			//forward
			{
				GPIOA->BSRR |= GPIO_BSRR_BR8;
			}
		}
		else		//disable motor
		{
			TIM1->CCR2 = 0;
			GPIOA->BSRR |= GPIO_BSRR_BS8;

		}

	}



	return 0;
}

/*************************************IRQ***************************************/
/*
 void USART2_IRQHandler(void) {

 if (USART2->SR & USART_CR1_RXNEIE) {

 USART2->SR &= ~USART_CR1_RXNEIE;

 if (USART2->DR == '0') {
 UART2_Send_String("OFF\r\n");
 GPIOC->BSRR = GPIO_BSRR_BS13;
 } else if (USART2->DR == '1') {
 UART2_Send_String("ON\r\n");
 GPIOC->BSRR = GPIO_BSRR_BR13;
 } else if (USART2->DR == '2') {
 uint16_t d0 = ADC1->DR;
 float Vdd = 1.2 * 4069 / d0;
 UART2_Send(Vdd);
 GPIOC->BSRR = GPIO_BSRR_BR13;
 }
 }
 }
 */
void DMA1_Channel1_IRQHandler(void) {

	DMA1->IFCR = DMA_IFCR_CGIF1 | DMA_IFCR_CTCIF1;	// clear DMA interrupt flags
	DMA1_Channel1->CCR &= ~DMA_CCR_EN;					//  
	/*
	 * Code
	 */
	DMA1_Channel1->CCR |= DMA_CCR_EN;			   //  
	ADC1->CR2 |= ADC_CR2_ADON;
}



Falar um pouco


Este projeto está pendurado nos meus planos há vários anos e foi adiado o tempo todo, mas agora realmente chega à sua conclusão lógica, que estou muito feliz em concluir, gostaria de torná-lo parcialmente aberto e vendê-lo como outra plataforma / construtor, se possível; o robô do sumô não está longe, e outros irão.
Não havia planos de escrever um artigo, mas a participação na competição exige.

Eu quase me esqueci da foto, alguns componentes ainda não chegaram, e o prazo da competição é amanhã, então eu tive que usar o bricolage não em seu melhor desempenho.

Foto do robô
image

E aqui está um mini vídeo de teste

All Articles