+7(960) 250-82-68 spam@mirossa.ru


  MCU         C           1С         PHP       JAVA      разное 


Статьи
 
 
Работа по учебнику Discovering the STM32 Microcontroller by Geoffrey Brown.

STM32 + Wii Nunchuk + LCD 5110(PCD8544). Интерфейсы SPI(3 wire) и I2C.

Использование игрового контроллера Wii Nunchuk. Получение данных от Wii Nunchuk по шине I2C и их вывод на дисплей Nokia5110 по шине SPI (только передача данных, 3 пина).
Также от джойстика поворачивается сервопривод.

datasheet PCD8544

datasheet servo SG90

- Весь исходный код. ide CooCox. -

Компоненты:
Дисплей Nokia5110
Модуль питания HW-131 (переключатель на 3 вольта)
Wii Nunchuk
Wii Nunchuk adapter (4-пиновый переходник для соединения на макетной плате)
STM32VLDiscovery
Micro Servo SG90

all components

Вывод информации с Wii Nunchuk на дисплей:
в левом верхнем углу - положение двух-осевого джойстика (255, 128),
в левом нижнем углу - состояние кнопок C и Z (нажаты)
остальное - показания трёх-осевого акселерометра в градусах.

display

некоторые модули:

/* main.c */

#include <stm32f10x_i2c.h>
#include <stm32f10x_rcc.h>
#include <stm32f10x_tim.h>
#include <stm32f10x_gpio.h>
#include "I2C.h"
#include "lcd5110.h"
#include <math.h>
#include "images.h"

#define master_addr 0x69 // "любой" адрес
#define speedV 370
#define btn_Z 0x01
#define btn_C 0x02

double pitch; //угол поворота по оси Х
double roll; //угол поворота по оси Y
double yaw; //угол поворота по оси Z

void intToStr(int16_t, char * );
void test();
void init_timers(void);

int main(void){

	uint8_t data [6];
	const uint8_t buf[] = {0};
	Status rez;

	char strImg[6];

	uint8_t pZ = 1, pC = 1; //предыдущее состояние кнопок(отпущены)
	uint16_t CC = 0, ZZ = 0; //количество нажатий
	uint16_t C = 0, Z = 0; //удерживание
	uint8_t JX, JY;
	int16_t AX, AY, AZ;

	strImg[5] = '\0';

	// LCD init and test
	lcd5110_init();
	//lcd5110_invert(1);
	//test();
	//lcd5110_invert(0);
	test();

	// wii nunchuk init
	#define NUNCHUK_ADDRESS 0xA4
	const uint8_t buf1[] = {0xf0 , 0x55}; //{0x40 , 0x00};
	const uint8_t buf2[] = {0xfb , 0x00};

	init_timers();
	I2C_LowLevel_Init(I2C1 , speedV , master_addr);

	rez = I2C_Write(I2C1 , buf1 , 2, NUNCHUK_ADDRESS);
	rez = I2C_Write(I2C1 , buf2 , 2, NUNCHUK_ADDRESS);

    while(1){

    	if ( rez == 1 ){ // от разных ошибок и зависаний
			I2C_LowLevel_Init(I2C1 , speedV , master_addr);
			rez = I2C_Write(I2C1 , buf1 , 2, NUNCHUK_ADDRESS);
			rez = I2C_Write(I2C1 , buf2 , 2, NUNCHUK_ADDRESS);
		}

    	// Read
		rez = I2C_Write(I2C1 , buf , 1, NUNCHUK_ADDRESS);
		if ( rez == 0 ){
			I2C_Read(I2C1 , data , 6, NUNCHUK_ADDRESS);
			if (data[0] == 0xFF && data[1] == 0xFF && data[2] == 0xFF && data[3] == 0xFF &&
					data[4] == 0xFF && data[5] == 0xFF){ //похоже на зависание
				rez = 1;
				continue;
			}

		// состояние кнопок
			if ( (data[5] & btn_C) != btn_C ){
				CC++;			//удерживание
				if( pC == 1 ){
					C++;		//нажатие
					pC = 0;
				}
			}else{
				pC = 1;
			}

			if ( (data[5] & btn_Z) != btn_Z ){ //бит сброшен = кнопка нажата
				ZZ++;			//удерживание
				if( pZ == 1 ){
					Z++;		//нажатие
					pZ = 0;
				}
			}else{
				pZ = 1;
			}

		// Joystick
			JX = data[0];
			JY = data[1];

		// Accelerometer
			AX = (uint16_t)data[2] << 2 | (uint16_t)data[5] >> 2;
			AY = (uint16_t)data[3] << 2 | (uint16_t)data[5] >> 4;
			AZ = (uint16_t)data[4] << 2 | (uint16_t)data[5] >> 6;
			//преобразуем 0..1023 в -512..511
			AX -= 512;
			AY -= 512;
			AZ -= 512;

		// вычисление углов от 0 до 90 градусов по данным акселерометра
			pitch = AX / sqrt( (double)(AY*AY + AZ*AZ) );
			pitch = atan( pitch ); // в радианах
			pitch = pitch * 57.2957; // в градусах

			roll = AY / sqrt( (double)(AX*AX + AZ*AZ) );
			roll = atan( roll ); // в радианах
			roll = roll * 57.2957; // в градусах

			yaw = AZ / sqrt( (double)(AX*AX + AY*AY) );
			yaw = atan( yaw ); // в радианах
			yaw = yaw * 57.2957; // в градусах

			/*double pitch2;
			 * вычисление углов от 0 до 180 градусов
			pitch2 = atan2((double)AX, (double)AZ);
			pitch2 = pitch2 * 57.2957; // в градусах

			double roll2;
			roll2 = atan2((double)AY, (double)AZ);
			roll2 = roll2 * 57.2957; // в градусах

			double yaw2;
			yaw2 = atan2((double)AZ, (double)AX);
			yaw2 = yaw2 * 57.2957; // в градусах
			 */

		// вывод на дисплей
			intToStr(JX, strImg);
			lcd5110_printstr(0, 0, strImg);
			intToStr(JY, strImg);
			lcd5110_printstr(0, 11, strImg);

			intToStr( (int16_t)pitch, strImg);
			lcd5110_printstr(35, 0, strImg);
				lcd5110_printstr(78, 0, "X");
			intToStr((int16_t)roll, strImg);
			lcd5110_printstr(35, 11, strImg);
				lcd5110_printstr(78, 11, "Y");
			intToStr((int16_t)yaw, strImg);
			lcd5110_printstr(35, 22, strImg);
				lcd5110_printstr(78, 22, "Z");
			lcd5110_printstr(44, 33, "УГОЛ");

			if ( !pC )
				lcd5110_printstr(4, 39, "C");
			else
				lcd5110_printstr(4, 39, " ");

			if ( !pZ )
				lcd5110_printstr(20, 39, "Z");
			else
				lcd5110_printstr(20, 39, " ");

			lcd5110_refresh(0);

		// управление Servo (sg90)
		// управлять буду через ось Х
		// Х изменяется от 0 до 255
		// настройки таймера таковы, что регулировочное значение будет
		//  меняться от 50 ( 1мс; -45 градусов ) до 100 ( 2мс; +45 градусов )
		//  средняя точка = 75 ( 1.5мс; 0 градусов )
		// поэтому значение Х от 0 до 255 нужно масштабировать на интервал от 50 до 100
		// для этого перемещаю интервал "от 50 до 100" в ноль: "от 0 до 50"
		// масштабирую: уголСерво = (Х*50)/255; остаток отбрасываю
		// возвращаюсь к интервалу "от 50 до 100" : уголСерво = уголСерво + 50;

			TIM_SetCompare3(TIM4, JX*50/255 + 50);

		}
    }
}

// преобразует число в строку.
// выравнивание по правому краю
void intToStr(int16_t dec, char * s){

	uint16_t rem;
	uint8_t i, neg = 0;

	for ( i = 0; i < 4; i++ )
		s[i] = ' ';
	s[i] = '0';

	if( dec < 0 ){
		neg = 1;
		dec = -1*dec;
	}

	while ( dec > 0 ){
		rem = dec % 10;
		dec = dec / 10;
		s[i--] = (char)(rem + 48);
	}

	if ( neg )
		s[i] = '-';

}


void test(){

	lcd5110_refresh(mirossa84x48);

	Delay(3000);

	/*lcd5110_printstr(5, 9, "ARTYUIBGH9876543210~--1234567890+=|");
	Delay(1000);
	lcd5110_putpixel(0,0);
	lcd5110_putpixel(83,0);
	lcd5110_putpixel(0,47);
	lcd5110_putpixel(83,47);
	lcd5110_refresh(0);
	Delay(1000);
	lcd5110_horiz_line(1, 1, 84);
	lcd5110_horiz_line(1, 13, 82);
	lcd5110_refresh(0);
	Delay(1000);
	lcd5110_vert_line(5, 0, 48);
	lcd5110_vert_line(12, 1, 48);
	lcd5110_vert_line(21, 2, 6);
	lcd5110_refresh(0);
	Delay(1000);
	lcd5110_draw_line(3,3, 70, 30);
	lcd5110_draw_line(83,0, 0, 47);
	lcd5110_refresh(0);

	Delay(1000);

	lcd5110_clear();
	lcd5110_refresh(0);
	*/
}

void init_timers(void) {
    GPIO_InitTypeDef	GPIO_InitStructure;
    //setup GPIO for PA1, the output of TIM2
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);

    GPIO_StructInit(&GPIO_InitStructure);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed	= GPIO_Speed_2MHz;
    GPIO_Init(GPIOB, &GPIO_InitStructure);


    TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
    TIM_OCInitTypeDef TIM_OCInitStructure;

    // enable timer clock
    RCC_APB1PeriphClockCmd ( RCC_APB1Periph_TIM4 , ENABLE );
    // configure timer
    // PWM frequency = 50 hz with 24 ,000 ,000 hz system clock
    //  24 ,000 ,000/480 = 50 ,000
    // 100 ,000/100 = 50

    TIM_TimeBaseStructInit(&TIM_TimeBaseStructure );
    TIM_TimeBaseStructure.TIM_Prescaler = SystemCoreClock /50000 - 1; // 0..479
    TIM_TimeBaseStructure.TIM_Period = 1000 - 1; // 0..999
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up ;
    TIM_TimeBaseInit (TIM4, &TIM_TimeBaseStructure );
// PWM1 Mode configuration : Channel3
//Edge - aligned ; not single pulse mode
    TIM_OCStructInit (&TIM_OCInitStructure );
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM1 ;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable ;
    TIM_OC3Init (TIM4, &TIM_OCInitStructure );
// Enable Timer
    TIM_Cmd (TIM4, ENABLE );


}

#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
	/* Infinite loop */
	/* Use GDB to find out why we're here */
	while (1);
}
#endif


/* lcd5110.h */
#ifndef _H_LCD5110_
#define _H_LCD5110_

#include "spi.h"

#define GPIO_PIN_RST GPIO_Pin_9
#define GPIO_PIN_SCE GPIO_Pin_8
#define GPIO_PIN_DC GPIO_Pin_5
#define LCD5110_PORT GPIOB

void Delay(uint32_t nTime);

void lcd5110_init();

//очистка буфера дисплея
void lcd5110_clear();
//вывод данных из буфера на дисплей при p == 0
// иначе - вывод данных на дисплей из массива(504 байта), указатель
// на который передается в функцию
void lcd5110_refresh(const char * p);

// вывод символа 6x8 в буфер экрана в пределах всего экрана 84x48
// в функцию передаются координаты экрана в пикселях (от 0,0 до 83,47)
// в функции эти координаты преобразуются в : (0,0 до 83,5). y - от 0 до 6
// если символ не уместился по горизонтали, то не вместившаяся часть перенесётся на следующую строку
//  (так же, как и при передаче данных напрямую на дисплей)
// если символ не вместился по вертикали, то он обрежется.
void lcd5110_putchar(uint8_t x, uint8_t y, char ch);

//выводит текст в буфер экрана в пределах всего экрана
// если символ не вмещается в строку полностью, то он полностью переносится на
// следующую строку.
// Если строка не вмещается по вертикали, то обрезается.
void lcd5110_printstr(uint8_t x, uint8_t y, char * ch);

//выводит пиксель в буфер экрана
void lcd5110_putpixel(uint8_t x, uint8_t y);
//выводит горизонтальную строку толщиной в один пиксель в буфер экрана
void lcd5110_horiz_line(uint8_t x, uint8_t y, uint8_t len);
//выводит вертикльную строку толщиной в один пиксель в буфер экрана
void lcd5110_vert_line(uint8_t x, uint8_t y, uint8_t len);
//линия под углом. выводится в буфер экрана
void lcd5110_draw_line(uint8_t x1, uint8_t y1, uint8_t x2, uint8_t y2);
//переключение режима инвертирования изображения.
// предполагается, что дисплей находится в режиме
// приёма основных команд(не расширенных)
void lcd5110_invert(uint8_t m);
//настройка напряжения для работы дисплея
//(влияет на контрастность)
void lcd3110_setVop(uint8_t m);

//Перевод дисплея в/из режима погашенного экрана.
// После выхода из этого режима инициализировать дисплей
// снова не нужно. На экране отобразится последнее изображение.
//
// значения параметра enable:
// 1 (что-то не равное нулю) и 0.
void lcd5110_powerDownMode(uint8_t enable);
#endif

70

Комментарии




information