Создание собственной системы стабилизации
Вот что у меня осталось от DiscoveryF3
Олег, попробуйте на всякий случай повесить резистор помиж кондёрами на кварц (между лапами кварца) килоОм 100-150…
поясните кто-нибудь эту строчку:
memcpy(&mcfg, (char *)FLASH_WRITE_ADDR, sizeof(master_t));
копать от сюда и до заката =))
Приводим значение адреса в указатель на адрес памяти и копируем с этого адреса.
Стандартные функции смотрел,
Олег, вот стандартная инициализация ДУС через стандартные драйверы дисковери (ранее я прилагал архив), см. внимательно, там же есть функции чтения и записи ИММЕНО по Ф3дисковери.
void DUS_Config(void)
{
L3GD20_InitTypeDef L3GD20_InitStructure;
L3GD20_FilterConfigTypeDef L3GD20_FilterStructure;
/* Configure Mems L3GD20 */
L3GD20_InitStructure.Power_Mode = L3GD20_MODE_ACTIVE;
L3GD20_InitStructure.Output_DataRate = L3GD20_OUTPUT_DATARATE_3;
L3GD20_InitStructure.Axes_Enable = L3GD20_AXES_ENABLE;
L3GD20_InitStructure.Band_Width = L3GD20_BANDWIDTH_4;
L3GD20_InitStructure.BlockData_Update = L3GD20_BlockDataUpdate_Continous;
L3GD20_InitStructure.Endianness = L3GD20_BLE_LSB;
L3GD20_InitStructure.Full_Scale = L3GD20_FULLSCALE_2000;
L3GD20_Init(&L3GD20_InitStructure);
L3GD20_FilterStructure.HighPassFilter_Mode_Selection =L3GD20_HPM_NORMAL_MODE_RES;
L3GD20_FilterStructure.HighPassFilter_CutOff_Frequency = L3GD20_HPFCF_3;
L3GD20_FilterConfig(&L3GD20_FilterStructure) ;
L3GD20_FilterCmd(L3GD20_HIGHPASSFILTER_ENABLE);
L3GD20_INT2InterruptCmd(L3GD20_INT2INTERRUPT_ENABLE);
}
кто в переменные глядеть должен, в 16-ти битную ну никак не влезет
пишем исключительно полусловами (16 бит), вот моя перекладка демки
/*------------------- ýìóëÿòîð ôëýø ------------------------*/
FLASH_Status FLASH_Write16(uint32_t addr, uint16_t* buf, uint32_t size)
{
FLASH_Status FLASHStatus = FLASH_COMPLETE;
uint32_t inx = 0;
uint32_t EraseCounter = 0x00, Address = 0x00;
uint32_t NbrOfPage = 0x00;
/* ïðîâåðèì âûõîä çà ïðåäåëû ðàçðåøåííîé îáëàñòè */
if (addr+size*2 > FLASH_USER_END_ADDR) return FLASH_ERROR_PROGRAM;
/* Ðàçáëîêèðîâêà äîñòóïà ê çàïèñè ôëåø */
FLASH_Unlock();
/* Î÷èñòèì ôëàãè */
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_PGERR | FLASH_FLAG_WRPERR);
/* Îïðåäåëèì êîëè÷åñòâî ñòèðàåìûõ ñòðàíèö */
NbrOfPage = 1 + size / FLASH_PAGE_SIZE;
/* Ñòèðàåì ñòðàíèöû */
for(EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
{
FLASHStatus = FLASH_ErasePage(FLASH_USER_START_ADDR + (FLASH_PAGE_SIZE * EraseCounter));
if (FLASHStatus != FLASH_COMPLETE) return FLASHStatus;
}
/* Ïðîãðàììèðóåì ôëåø */
Address = FLASH_USER_START_ADDR;
while (inx < size) {
FLASHStatus = FLASH_ProgramHalfWord(Address, buf[inx]);
if (FLASHStatus == FLASH_COMPLETE){
Address += 2;
inx++;
}
else return FLASHStatus;
}
/* Çàáëîêèðóåì çàïèñü âî ôëåø */
FLASH_Lock();
return FLASHStatus;
}
Вот стандартная функция записи полуслова
/**
* @brief Programs a half word at a specified address.
* @note To correctly run this function, the FLASH_Unlock() function
* must be called before.
* Call the FLASH_Lock() to disable the flash memory access
* (recommended to protect the FLASH memory against possible unwanted operation)
* @param Address: specifies the address to be programmed.
* @param Data: specifies the data to be programmed.
* @retval FLASH Status: The returned value can be: FLASH_ERROR_PG,
* FLASH_ERROR_WRP, FLASH_COMPLETE or FLASH_TIMEOUT.
*/
FLASH_Status FLASH_ProgramHalfWord(uint32_t Address, uint16_t Data)
{
FLASH_Status status = FLASH_COMPLETE;
/* Check the parameters */
assert_param(IS_FLASH_PROGRAM_ADDRESS(Address));
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
if(status == FLASH_COMPLETE)
{
/* If the previous operation is completed, proceed to program the new data */
FLASH->CR |= FLASH_CR_PG;
*(__IO uint16_t*)Address = Data;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation(FLASH_ER_PRG_TIMEOUT);
/* Disable the PG Bit */
FLASH->CR &= ~FLASH_CR_PG;
}
У меня всё отлично заработало, писал в старший сектор, чтоб линкёр в него не лез уменьшил верхнюю границу флеши в настройке проекта. Почитаю про ф4, напишу отличия…
урааааа у меня зажегся первый светодиод(проверочный) !!! 😃
пишем исключительно полусловами (16 бит), вот моя перекладка демки
я имел ввиду адрес сектора отрезанного под еепромку 😃
ну с одной мёртвой точки сдвинулся, пойду дальше копать…
урааааа у меня зажегся первый светодиод(проверочный) !!!
Поздравляю! С флешей разобрался? Вообщем от Ф3 практически не отличается, кроме возможности установить ширину данных, и ещё не увидел в описании процедуры программирования снятие бита программирования
PG bit in the FLASH_CR register, надо смотреть стандартные библиотеки
я имел ввиду адрес сектора отрезанного под еепромку
ну тут без вариантов uint32_t
так у меня проблема - проц запускается на частоте - 53.76 Мгц - чё я где утварил?
Виноват, сам скопировал от Discovery system_stm32f4xx.c и забыл про это…
и ещё не увидел в описании процедуры программирования снятие бита программирования
PG bit in the FLASH_CR register,
тут?
stm32f4xx_flash.c
if(status == FLASH_COMPLETE)
{
/* if the previous operation is completed, proceed to program the new data */
FLASH->CR &= CR_PSIZE_MASK;
FLASH->CR |= FLASH_PSIZE_BYTE;
FLASH->CR |= FLASH_CR_PG;
*(__IO uint8_t*)Address = Data;
/* Wait for last operation to be completed */
status = FLASH_WaitForLastOperation();
/* if the program operation is completed, disable the PG Bit */
FLASH->CR &= (~FLASH_CR_PG);
}
У меня всё отлично заработало, писал в старший сектор, чтоб линкёр в него не лез уменьшил верхнюю границу флеши в настройке проекта.
посмотри st-link видит эту область, а то когда я по незнанке сносил нулевой сектор, то видел что он стирается и в него пишется, а так нет, а отпаивать, что-бы выдрать ввесь дамп, ну не хочется - при запаяном программаторе через uart не достучишься… ага вспомнил, если бы с виртуальником были бы траблы, он бы мне сообщил - светодиод бы моргал часто-часто, да и дальше бы не пошел…
сейчас дошел до автодетекта (его выкинуть надо), а так как нечего детектировать то висим перемигиваемся светодиодиками - почему и заметил, что как-то не так моргают, полез частоту проверять…
тут? stm32f4xx_flash.c
Да, ту всё нормально, а в доке на Ф3 нет ничего про снятие бита программирования.
посмотри st-link видит эту область,
СТ-ЛИНК видит-читает, но непосредственно править в отладчике не даёт, только через программирование.
да-да у меня тоже всё прекрасно записано - отладчиком сейчас заглянул в седьмой сектор 0x08060000 - и до победы, занято только примерно пол килобайта, ещё 127,5 - свободны 😃
#include "board.h"
#include "mw.h"
#include <string.h>
#ifndef FLASH_PAGE_COUNT
#define FLASH_PAGE_COUNT 3
#endif
#define ADDR_FLASH_SECTOR_0 ((uint32_t)0x08000000) /* Base @ of Sector 0, 16 Kbytes */
#define ADDR_FLASH_SECTOR_1 ((uint32_t)0x08004000) /* Base @ of Sector 1, 16 Kbytes */
#define ADDR_FLASH_SECTOR_2 ((uint32_t)0x08008000) /* Base @ of Sector 2, 16 Kbytes */
#define ADDR_FLASH_SECTOR_3 ((uint32_t)0x0800C000) /* Base @ of Sector 3, 16 Kbytes */
#define ADDR_FLASH_SECTOR_4 ((uint32_t)0x08010000) /* Base @ of Sector 4, 64 Kbytes */
#define ADDR_FLASH_SECTOR_5 ((uint32_t)0x08020000) /* Base @ of Sector 5, 128 Kbytes */
#define ADDR_FLASH_SECTOR_6 ((uint32_t)0x08040000) /* Base @ of Sector 6, 128 Kbytes */
#define ADDR_FLASH_SECTOR_7 ((uint32_t)0x08060000) /* Base @ of Sector 7, 128 Kbytes */
#define FLASH_PAGE_SIZE ((uint32_t)0x20000)
#define FLASH_WRITE_ADDR ADDR_FLASH_SECTOR_7 //(0x08000000 + (uint32_t)FLASH_PAGE_SIZE*10 * (FLASH_PAGE_COUNT - 1)) // use the last KB for storage
config_t cfg;
const char rcChannelLetters[] = "AERT1234";
static uint8_t EEPROM_CONF_VERSION = 40;
static uint32_t enabledSensors = 0;
static void resetConf(void);
void parseRcChannels(const char *input)
{
const char *c, *s;
for (c = input; *c; c++) {
s = strchr(rcChannelLetters, *c);
if (s)
cfg.rcmap[s - rcChannelLetters] = c - input;
}
}
static uint8_t validEEPROM(void)
{
const config_t *temp = (const config_t *)FLASH_WRITE_ADDR;//;ADDR_FLASH_SECTOR_7
const uint8_t *p;
uint8_t chk = 0;
// check version number
if (EEPROM_CONF_VERSION != temp->version)
return 0;
// check size and magic numbers
if (temp->size != sizeof(config_t) || temp->magic_be != 0xBE || temp->magic_ef != 0xEF)
return 0;
// verify integrity of temporary copy
for (p = (const uint8_t *)temp; p < ((const uint8_t *)temp + sizeof(config_t)); p++)
chk ^= *p;
// checksum failed
if (chk != 0)
return 0;
// looks good, let's roll!
return 1;
}
void readEEPROM(void)
{
uint8_t i;
// Read flash
memcpy(&cfg, (char *)FLASH_WRITE_ADDR, sizeof(config_t));//ADDR_FLASH_SECTOR_7
for (i = 0; i < 6; i++)
lookupPitchRollRC[i] = (2500 + cfg.rcExpo8 * (i * i - 25)) * i * (int32_t) cfg.rcRate8 / 2500;
for (i = 0; i < 11; i++) {
int16_t tmp = 10 * i - cfg.thrMid8;
uint8_t y = 1;
if (tmp > 0)
y = 100 - cfg.thrMid8;
if (tmp < 0)
y = cfg.thrMid8;
lookupThrottleRC[i] = 10 * cfg.thrMid8 + tmp * (100 - cfg.thrExpo8 + (int32_t) cfg.thrExpo8 * (tmp * tmp) / (y * y)) / 10; // [0;1000]
lookupThrottleRC[i] = cfg.minthrottle + (int32_t) (cfg.maxthrottle - cfg.minthrottle) * lookupThrottleRC[i] / 1000; // [0;1000] -> [MINTHROTTLE;MAXTHROTTLE]
}
cfg.tri_yaw_middle = constrain(cfg.tri_yaw_middle, cfg.tri_yaw_min, cfg.tri_yaw_max); //REAR
}
void writeParams(uint8_t b)
{
FLASH_Status status;
uint32_t i;
uint8_t chk = 0;
const uint8_t *p;
cfg.version = EEPROM_CONF_VERSION;
cfg.size = sizeof(config_t);
cfg.magic_be = 0xBE;
cfg.magic_ef = 0xEF;
cfg.chk = 0;
// recalculate checksum before writing
for (p = (const uint8_t *)&cfg; p < ((const uint8_t *)&cfg + sizeof(config_t)); p++)
chk ^= *p;
cfg.chk = chk;
// write it
FLASH_Unlock();
FLASH_ClearFlag(FLASH_FLAG_EOP | FLASH_FLAG_OPERR | FLASH_FLAG_WRPERR | FLASH_FLAG_PGAERR | FLASH_FLAG_PGPERR|FLASH_FLAG_PGSERR);
if (FLASH_EraseSector(FLASH_Sector_7, VoltageRange_3) == FLASH_COMPLETE) {
for (i = 0; i < sizeof(config_t); i += 4) {
status = FLASH_ProgramWord(FLASH_WRITE_ADDR + i, *(uint32_t *) ((char *) &cfg + i));//ADDR_FLASH_SECTOR_7
if (status != FLASH_COMPLETE)
break; // TODO: fail
}
}
FLASH_Lock();
readEEPROM();
if (b)
blinkLED(15, 20, 1);
}
void checkFirstTime(bool reset)
{
// check the EEPROM integrity before resetting values
if (!validEEPROM() || reset)
resetConf();
}
// Default settings
static void resetConf(void)
{
int i;
const int8_t default_align[3][3] = { /* GYRO */ { 0, 0, 0 }, /* ACC */ { 0, 0, 0 }, /* MAG */ { -2, -3, 1 } };
memset(&cfg, 0, sizeof(config_t));
cfg.version = EEPROM_CONF_VERSION;
cfg.mixerConfiguration = MULTITYPE_QUADX;
featureClearAll();
setFeature(FEATURE_VBAT);
// cfg.looptime = 0;
cfg.P8[ROLL] = 40;
cfg.I8[ROLL] = 30;
cfg.D8[ROLL] = 23;
cfg.P8[PITCH] = 40;
cfg.I8[PITCH] = 30;
cfg.D8[PITCH] = 23;
cfg.P8[YAW] = 55;
cfg.I8[YAW] = 45;
// cfg.D8[YAW] = 0;
cfg.P8[PIDALT] = 11;
cfg.I8[PIDALT] = 15;
cfg.D8[PIDALT] = 7;
cfg.P8[PIDPOS] = 11; // POSHOLD_P * 100;
// cfg.I8[PIDPOS] = 0; // POSHOLD_I * 100;
// cfg.D8[PIDPOS] = 0;
cfg.P8[PIDPOSR] = 20; // POSHOLD_RATE_P * 10;
cfg.I8[PIDPOSR] = 8; // POSHOLD_RATE_I * 100;
cfg.D8[PIDPOSR] = 45; // POSHOLD_RATE_D * 1000;
cfg.P8[PIDNAVR] = 14; // NAV_P * 10;
cfg.I8[PIDNAVR] = 20; // NAV_I * 100;
cfg.D8[PIDNAVR] = 80; // NAV_D * 1000;
cfg.P8[PIDLEVEL] = 70;
cfg.I8[PIDLEVEL] = 10;
cfg.D8[PIDLEVEL] = 20;
cfg.P8[PIDMAG] = 40;
// cfg.P8[PIDVEL] = 0;
// cfg.I8[PIDVEL] = 0;
// cfg.D8[PIDVEL] = 0;
cfg.rcRate8 = 99;
cfg.rcExpo8 = 20;
// cfg.rollPitchRate = 0;
// cfg.yawRate = 0;
// cfg.dynThrPID = 0;
cfg.thrMid8 = 50;
// cfg.thrExpo8 = 0;
// for (i = 0; i < CHECKBOXITEMS; i++)
// cfg.activate[i] = 0;
// cfg.angleTrim[0] = 0;
// cfg.angleTrim[1] = 0;
// cfg.accZero[0] = 0;
// cfg.accZero[1] = 0;
// cfg.accZero[2] = 0;
// cfg.mag_declination = 0; // For example, -6deg 37min, = -637 Japan, format is [sign]dddmm (degreesminutes) default is zero.
memcpy(&cfg.align, default_align, sizeof(cfg.align));
cfg.acc_hardware = ACC_DEFAULT; // default/autodetect
cfg.acc_lpf_factor = 4;
cfg.acc_lpf_for_velocity = 10;
cfg.accz_deadband = 50;
cfg.gyro_cmpf_factor = 400; // default MWC
cfg.gyro_lpf = 42;
cfg.mpu6050_scale = 1; // fuck invensense
cfg.baro_tab_size = 21;
cfg.baro_noise_lpf = 0.6f;
cfg.baro_cf = 0.985f;
cfg.moron_threshold = 32;
cfg.gyro_smoothing_factor = 0x00141403; // default factors of 20, 20, 3 for R/P/Y
cfg.vbatscale = 110;
cfg.vbatmaxcellvoltage = 43;
cfg.vbatmincellvoltage = 33;
// cfg.power_adc_channel = 0;
// Radio
parseRcChannels("AETR1234");
// cfg.deadband = 0;
// cfg.yawdeadband = 0;
cfg.alt_hold_throttle_neutral = 20;
// cfg.spektrum_hires = 0;
cfg.midrc = 1500;
cfg.mincheck = 1100;
cfg.maxcheck = 1900;
// cfg.retarded_arm = 0; // disable arm/disarm on roll left/right
// Failsafe Variables
cfg.failsafe_delay = 10; // 1sec
cfg.failsafe_off_delay = 200; // 20sec
cfg.failsafe_throttle = 1200; // decent default which should always be below hover throttle for people.
// Motor/ESC/Servo
cfg.minthrottle = 1150;
cfg.maxthrottle = 1850;
cfg.mincommand = 1000;
cfg.motor_pwm_rate = 490;
cfg.servo_pwm_rate = 50;
// servos
cfg.yaw_direction = 1;
cfg.tri_yaw_middle = 1500;
cfg.tri_yaw_min = 1020;
cfg.tri_yaw_max = 2000;
// fixed wing
cfg.pitch_min = 1020;
cfg.pitch_mid = 1500;
cfg.pitch_max = 1980;
cfg.yaw_min = 1020;
cfg.yaw_mid = 1500;
cfg.yaw_max = 1980;
cfg.flaperons = 0; // enable (1) / disable (0) flaperon function
cfg.flap_aux = 0; // which AUX channel use to toggle flaps
cfg.vtail = 0; // airplane tail configuration, X (elev + rudd) [0] or V tail [1]
cfg.pitch_direction = 1; // airplane pitch servo orientation
cfg.pitch_direction_l = 1; // (Flying-wing only) left servo - pitch orientation
cfg.pitch_direction_r = -1; // (Flying-wing only) right servo - pitch orientation (opposite sign to pitch_direction_l if servos are mounted mirrored)
cfg.roll_direction_l = 1; // left servo - roll orientation
cfg.roll_direction_r = -1; // right servo - roll orientation (same sign as ROLL_DIRECTION_L, if servos are mounted in mirrored orientation)
// flying wing
cfg.wing_left_min = 1020;
cfg.wing_left_mid = 1500;
cfg.wing_left_max = 1980;
cfg.wing_right_min = 1020;
cfg.wing_right_mid = 1500;
cfg.wing_right_max = 1980;
cfg.pitch_direction_l = 1;
cfg.pitch_direction_r = -1;
cfg.roll_direction_l = 1;
cfg.roll_direction_r = 1;
// gimbal
cfg.gimbal_pitch_gain = 10;
cfg.gimbal_roll_gain = 10;
cfg.gimbal_flags = GIMBAL_NORMAL;
cfg.gimbal_pitch_min = 1020;
cfg.gimbal_pitch_max = 2000;
cfg.gimbal_pitch_mid = 1500;
cfg.gimbal_roll_min = 1020;
cfg.gimbal_roll_max = 2000;
cfg.gimbal_roll_mid = 1500;
// gps/nav stuff
cfg.gps_type = GPS_NMEA;
cfg.gps_baudrate = 115200;
cfg.gps_wp_radius = 200;
cfg.gps_lpf = 20;
cfg.nav_slew_rate = 30;
cfg.nav_controls_heading = 1;
cfg.nav_speed_min = 100;
cfg.nav_speed_max = 300;
// serial (USART1) baudrate
cfg.serial_baudrate = 115200;
// custom mixer. clear by defaults.
for (i = 0; i < MAX_MOTORS; i++)
cfg.customMixer[i].throttle = 0.0f;
writeParams(0);
}
bool getSensors(uint32_t mask)
{
return enabledSensors & mask;
}
void setSensors(uint32_t mask)
{
enabledSensors |= mask;
}
void clearSensors(uint32_t mask)
{
enabledSensors &= ~(mask);
}
uint32_t sensorsMask(void)
{
return enabledSensors;
}
bool getFeature(uint32_t mask)
{
return cfg.enabledFeatures & mask;
}
void setFeature(uint32_t mask)
{
cfg.enabledFeatures |= mask;
}
void featureClear(uint32_t mask)
{
cfg.enabledFeatures &= ~(mask);
}
void featureClearAll()
{
cfg.enabledFeatures = 0;
}
uint32_t featureMask(void)
{
return cfg.enabledFeatures;
}
занято только примерно пол килобайта, ещё 127,5 - свободны
Жалко тереть такую страницу, лучше всёж попытаться уйти в мелкий сектор.
ну пока не напрягает, у меня всего кода на 68к… хотя можно смело спрыгнуть в первый сектор (не нулевой)…
Сергей, у тебя к этому порту ГУЙ Вийский или что-то своё?
Вий но пока до него не добрался… в вингуи сделали сразу терминал, разберусь с датчиками буду пробовать цепляться…
тут обновил
Запускаем .NET MicroFramework на STM32F4Discovery
ток в комментах пишут, что перформанс совсем дохленький…
Добрался до гиро-акселя, пока присматриваю откуда спилить, вопрос такого плана DMA на SPI1 (там мпу висит) использовать?
вопрос такого плана DMA на SPI1 (там мпу висит) использовать?
Конечно использовать, это пзволит избежать затрат времени на тупую пересылку байт. Вообще можно по готовности ДУСа читтать весь набор регистров в буфер, а потом по необходимости программно разбирать.
Буду очень благодарен, если кто нибудь доходчиво пояснит ЧТО и КАК фильтрует HIGH PASS Filter в датчиках, его практическое применение, в переводе с даташита так и не понял…
получается как-то, не слишком хорошо, DMA2: Stream 0, Channel 0 - ADC1; DMA2: Stream 2, Channel 4 - USART1_RX; Stream 7, Channel 4 - USART1_TX;
ага разобрался сделать: DMA2: Stream 5, Channel 4 - USART1_RX; Stream 7, Channel 4 - USART1_TX; и Stream 2, Channel 3 - SPI1_RX; Stream 3 Channel 3 - SPI1_TX;
картинко
ЧТО и КАК фильтрует HIGH PASS Filter
высокочастотный фильтр, пропускающий частоты выше некоторой частоты среза и ослабляющий более низкие частоты…
ну типа решили мы полетать в дождь, а у нас две-три капли в секунду барабанят (ну в моём случае по салатнице) - аксель с ума сводят - можно обрезать данное мероприятие (к гироскопам не применимо)…
о только щас подумал, а нафига акселю вообще вибрации чувствовать - зарезать с двух сторон - пусть доплывает потиху?
Stream 3 Channel 3 - SPI1_TX;
а это наверно и ненадо, зачем мне буфер на выход если я один раз при инициализации записываю данные?