LINUX.ORG.RU

Проблема с I2C в MSP430

 , , ,


0

1

Имею MSP430F5529 Launchpad. Хочу считывать данные с датчиков по I2C и столкнулся с проблемой - I2C работает нормально только после перепрошивки. То есть, загружаю я прошивку с помощью программатора-отладчика Ez-FET, установленного на плате, - всё работает нормально. Можно жать кнопочку RESET, можно запускать отладку - всё будет работать нормально. Но если ввести МК в режим BSL (зажать кнопки BSL и RESET, затем отпустить сначала RESET и только потом BSL), а потом перезагрузить или отключить питание платы, а потом подключить снова, то I2C не работает. И не будет работать, сколько не перезагружай плату. Но стоит залить ТУ ЖЕ САМУЮ прошивку и всё начинает работать. При этом таймеры, UART и GPIO работают как надо всегда (другие модули не использую пока что). Если не заливать заново прошивку, а подключиться для отладки это проблему не решает. Надо именно перезалить прошивку. То есть какая-то инициализация осуществляется программатором при заливке прошивки, а я в своей программе её не делаю. Для того чтобы внешние датчики всегда были в одинаковом состоянии запитал их от GPIO микроконтроллера, благо они жрут единицы миллиампер. Так что на них никак не влияет перепрошивка - они начинают работать с нуля каждый сброс МК (МК при старте сначала выдаёт 0 на питание датчиков, ждёт кучу времени, чтобы точно разрядились все конденсаторы и лишь потом запитывает датчики).

Если плату отключить от питания и быстро подключить снова, то она может и заработать. Также она работает, если отключали только питание, а внешний переходник на UART остался подключен и мог питать МК с помощью паразитного питания через подтяжки линий (но при этом МК не исполнял программу, для этого питания не хватало, но, возможно, мог сохранить состояние ОЗУ или какой-то периферии), а затем подали питание вновь.

Вот код моей библиотеки для работы с I2C: http://pastebin.com/M1WtDKQb. Я не нашёл отличий в инициализации от примеров TI.

«Не работает» - значит любые попытки чтения по I2C возвращают не то, что нужно. Обычно это нули. Но после режима BSL это значение 0xC7.

В чём может быть дело? Какое состояние МК может инициализировать программатор при прошивки и оно сохраняется при перезагрузках, но не сохраняется при пропадании питания.

Смотрел с помощью mspdebug содержимое памяти по тем адресам, где находятся регистры, которые отвечают за I2C - различия между ситуациями, когда работает и когда нет, нету, всё настроено идентично. Больше не знаю, что посмотреть.

★★★★★

Последнее исправление: KivApple (всего исправлений: 1)

Может, у тебя BSL сбрасывает slave address? У тебя BSL не кастомизированный какой под I2C?

Не считаешь нужным UCB1I2CSA = addr всобачить до UCB1CTL1 &= ~UCSWRST;? Все в форме вопросов, потому что я не помню нифига по этим чипам. Надо бы освежить.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

По даташиту UCB1I2CSA можно свободно писать без UCSWRST.

К тому же после перепрошивки всё работает сколько не делай RESET всему девайсу. BSL стандартный, для USB, к тому же я его не использую. Но ладно бы BSL - так ведь I2C не работает если просто отключить питание и снова включить.

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

Получается, что есть какая-то настройка периферии, которая находится за пределами регистров I2C и при этом мешает ему работать. При этом эта настройка может сохраняться между сбросами МК, а ещё её меняют BSL и программатор, но не меняет мой код.

KivApple ★★★★★
() автор топика

Была несколько похожая фигня с GPIO/UART. На плате висело 2 уарта, и из-за кривой схемотехники они могли завестись в режим генерации, если один из них быстро не вырубить сразу после включения платы. Фишка в том, что это надо было делать БЫСТРО, ещё даже до инициализации статических переменных (у техасовского компилятора есть специальная функция _system_pre_init() для такого дела).

Твой случай тут при том, что под отладчиком точно так же всё работало без проблем.

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

alegz ★★★★
()
Ответ на: комментарий от Zubok

Знать бы, что там.

Исходники BSL были где-то на техасовском сайте. Как и документация по его переписыванию при необходимости.

alegz ★★★★
()
Ответ на: комментарий от alegz

Исходники BSL были где-то на техасовском сайте. Как и документация по его переписыванию при необходимости.

Не знаю, насколько это вообще соответствует тому, что ТСа, но видно, что меняются P4MAP4 и P5MAP4, то есть Port Mapping BSL может трогать.

https://git.ti.com/msp430-bsl/msp430-bsl/blobs/master/I2C.c#line139

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

Уже интересно. Не знал, что есть такая функциональность, но это может что-то объяснить. Добавил вот такой код.

// Setup port mapping
PMAPKEYID = 0x2D52;
PMAPCTL = PMAPRECFG;
P4MAP2 = PM_UCB1SCL;
P4MAP1 = PM_UCB1SDA;
PMAPKEYID = 0;	

Увы, но это не помогло. Проверил отладчиком - все значения ушли на свои места, так что регистры PxMAPy были доступны на запись (там же есть защита), всё нормально.

(mspdebug) md 0x1C0
    001c0: a5 96 03 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
    001d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
    001e0: 0d 0f 0e 10 0c 0b 00 00 00 00 00 00 00 00 00 00 |................|
    001f0: ff 3f ff 3f ff 3f ff 3f ff 3f ff 3f ff 3f ff 3f |.?.?.?.?.?.?.?.?|

К моему ланчпаду есть, кстати, такая картинка: http://processors.wiki.ti.com/images/c/cd/MSP-EXP430F5529LP_QSG_PinOut.png

Я там видно, что для I2C есть подписи PM_UCB1SCL и PM_UCB1SDA. Это названия констант для ремапа портов. Вероятно, это намёк на то, что для работы I2C порты таки надо ремапить. Но похоже я сделал это не правильно или не до конца, потому что не помогло.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от Zubok

Попробовал решить проблему в лоб - сделал с помощью отладчика дампы памяти (регистры периферии располагаются согласно даташиту с 0 по 0xFFF адреса) в ситуации когда всё нормально и когда не работает, а потом сделал diff. Результат:

34c34
<     00100: 00 00 82 00 0c 00 00 00 00 00 00 00 00 00 00 00 |................|
---
>     00100: 00 00 c2 00 0c 00 00 00 00 00 00 00 00 00 00 00 |................|
40c40
<     00160: 50 13 20 00 1f 10 00 00 44 00 00 00 cd c1 03 04 |P. .....D.......|
---
>     00160: 48 13 20 00 1f 10 00 00 44 00 00 00 cd c1 03 04 |H. .....D.......|
43c43
<     00190: ff 3f ff 3f ff 3f ff 3f 00 00 00 00 00 00 02 00 |.?.?.?.?........|
---
>     00190: ff 3f ff 3f ff 3f ff 3f 00 00 00 00 00 00 0a 00 |.?.?.?.?........|
50c50
<     00200: fe ff 02 10 00 00 00 00 00 00 00 00 00 00 00 00 |................|
---
>     00200: fe df 02 10 00 00 00 00 00 00 00 00 00 00 00 00 |................|

Отличий мало и это хорошо.

Первое отличие: регистр SFRIFG1. Он касается некоторых прерываний типа NMI или сбоя осциллятора и вряд ли в моём случае интересен.

Второе отличие: регистр UCSCTL0. Один из регистров, отвечающих за настройку осцилляторов. Изменились биты в поле счётчика бит модуляции, которое содержит некоторые служебные данные модуляции и может меняться в ходе его работы автоматически. Не интересно.

Третьей отличие: регистр SYSRSTIV. В нём содержится информация о причине сброса микроконтроллера. Не интересно.

Четвёртое отличие: регистр PAIN. Регистр, отображающий состояние входов порта A. Он Read only, так что точно не интересно.

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

KivApple ★★★★★
() автор топика
Ответ на: комментарий от KivApple

Да вроде правильно. Я надыбал схемку этой борды. Я смотрю, что выводы i2c напрямую к разъему идут. А резисторы pull-up у тебя есть? Подтянуты SCL и SDA? (это я на всякий случай спрашиваю, так как предполагаю, что да).

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Да, конечно же, резисторы есть. Я общаюсь с готовым китайским модулем на базе MPU6050. Он отлично работал с AVR, да и после прошивки MSP430 работает.

Сделал дамп памяти не после сброса МК перед отладкой, а с рабочей программы. Тоже только периферию. Если не смотреть заведомо ненужное (текущий байт данных UART, счётчик таймера, причина сброса и т. п.), то отличается только настройка DMA:

82,84c82,84
<     00510: 00 00 00 84 00 00 35 80 00 00 06 d5 00 00 00 00 |......5.........|
<     00520: 00 00 10 b5 00 00 41 d2 00 00 00 05 00 00 00 00 |......A.........|
<     00530: 00 00 ff ff 00 00 f7 fe 00 00 05 26 00 00 00 00 |...........&....|
---
>     00510: 00 00 01 00 00 00 00 00 00 00 06 c4 00 00 00 00 |................|
>     00520: 00 00 00 a5 00 00 00 42 00 00 00 05 00 00 00 00 |.......B........|
>     00530: 00 00 f7 b7 00 00 f7 fe 00 00 4c 2e 00 00 00 00 |..........L.....|

Но я не использую и не инициализирую DMA в своей программе. Либо это просто мусор, либо там что-то настроено и может мешать. Разбираю данные...

KivApple ★★★★★
() автор топика
Ответ на: комментарий от KivApple

Сразу вопрос-предположение: а не может ли быть так, что в результате запуска BSL у тебя не регистры портятся, а *программа* твоя. Может, он попытался каким-нибудь крэпом перезаписать ту часть кода, где I2C используется и инициализируется или какие-то байты в Flash, которые попадают на твой код. И поэтому у тебя после пересброса и даже выключения питания ничего не работает. А? И поэтому перепрошивка помогает. Выгрузи программу и сравни.

Мне кажется, надо искать в этом направлении.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 4)
Ответ на: комментарий от Zubok

Полный diff памяти (от 0 до 0xFFFF): http://pastebin.com/YXiebxxj

Второй вариант это когда всё работает нормально. Отличия в периферийных регистрах я уже расписал выше и пришёл к выводу, что они ничего не значат. Кроме них на этом дампе видны только значительные различия RAM. В варианте, когда всё работает, нулей значительно больше. Судя по всему при нормальном запуске RAM содержит мусор, а программатор пишет в неё значения из файла ELF. Вопрос в том насколько это критично для программы. По идее она должна инициализировать всё, что не попало в ROM при старте.

Тут нужно знать особенности работы mspgcc.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 2)
Ответ на: комментарий от KivApple

Я не очень понимю, какое влияние может оказать содержимое RAM на твою программу? Разве твой программа зависит от начального содержимого RAM?

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Ну я не планировал, чтобы она зависела, но так получилось.

Итак, проблема была в строчке:

uint8_t mpu6050_request[1] = {MPU6050_RA_ACCEL_XOUT_H};

Это глобальная переменная, описанная в модуле работы с сенсорами. Я ожидал, что она будет инициализирована при запуске программы, но это оказалось не так. GCC поместил её в секцию инициализированных RW данных в ELF вместо того, чтобы сделать код инициализации во время старта программы. Утилита прошивки радостно записала это всё в память и всё работало. Но после отключения питания содержимое RAM теряется, как следствие переменная больше не содержит нужного значения.

Проблему можно решить таким образом:

const uint8_t mpu6050_request[1] = {MPU6050_RA_ACCEL_XOUT_H};

В таком случае переменная попадает в секцию rodata, которая находится уже во флеш памяти. И всё работает.

Интересно, можно ли MSPGCC заставить генерировать код инициализации переменных вместо тупого помещения их значений в ELF. Это для винды и линукса нормальный подход, потому что там есть загрузчики ELF, которые позаботятся об инициализации памяти, а в прошивке для микроконтроллеров всё кроме флеша всегда надо инициализировать вручную. Компилятор для AVR вроде такими проблемами не страдал.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от KivApple

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

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Я наверное неверно выразился. Проблема возникала в двух случаях:

1) МК был обесточен и заново подключен

2) МК был введён в режим BSL и перезагружен

KivApple ★★★★★
() автор топика
Ответ на: комментарий от Zubok

Почему? Флешпамяти же в несколько раз больше, чем RAM, логично её тратить на неизменяемые данные (тем более что код инициализации всё равно отожрёт ещё больше флеша, чем просто константа)

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

Не, я имею в виду, чтобы ты инициализировал ее в main, а не ждал чуда от mspgcc. все равно инициализация в код превратится. Но! Вообще-то я думал, что эта переменная у тебя *изменяемая*. Сразу это не ясно.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от KivApple

А, ну понятно. То, что тебе нужно, называется ROM-based initialisation в отличие от RAM-based (из ELF-файла). В техасовском компиляторе за это отвечают опции --rom и --ram, насколько помню. При компиляции с первой значения инициализируемых переменных кладутся во флэш, а перед запуском main оттуда берутся и присваиваются.

В mspgcc просто обязано быть что-то подобное, ищи.

alegz ★★★★
()
Ответ на: комментарий от Zubok

Не, я имею в виду, чтобы ты инициализировал ее в main, а не ждал чуда от mspgcc.

Дожили, уже предусмотренное стандартом поведение компилятора чудом считается...

(Не, компиляторы, бывает, отклоняются от стандарта - тот же техасовский не обнуляет неинициализированные глобальные переменные, например - но это, по крайней мере, в документации должно быть описано.)

alegz ★★★★
()
Ответ на: комментарий от alegz

Дожили, уже предусмотренное стандартом поведение компилятора чудом считается...

А она инициализированная получается! Но только она уже инициализированная в RAM помещается, но между перезапусками не сохраняет значение. Такое поведение компилятора и линковщика у него. На самом деле оказалось, что он хотел поместить ее в ROM, но это совсем другая хотелка.

Другая хотелка: глобальная переменная в RAM переинициализируется при каждом запуске. Можно, в принципе, проверить. Поставлю mspgcc и проверю, что он генерит, но попозже.

Вот вроде у кого-то очень похожая тема: http://e2e.ti.com/support/microcontrollers/msp430/f/166/p/395350/1396870

А вот тут простой пример с инициализированной глобальной переменной и пустой main() показывает, что mspgcc вроде генерирует код по ее инициализации: https://remyaraj89.wordpress.com/2010/11/25/a-c-startup-code-of-msp430-and-a-... В _do_copy_data инициализированные глобальные переменные, а в _do_clear_bss прописывает 0 в неинициализированные. То есть вроде все верно должно быть.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Кстати, всё не совсем гладко, хотя дело уже не в инициализированных переменных. I2C как-то медленно работает. Ну... Сейчас опрос MPU6050 занимает 13 миллисекунд, в моей программе на AVR на это уходило менее 1.5 мс, хотя там МК был слабее (8-битный на 16 МГц, а тут 16-битный на 20 МГц) и не было никакой асинхронности.

Вот такой у меня код:

const uint8_t mpu6050_request[1] = {MPU6050_RA_ACCEL_XOUT_H};
int16_t mpu6050_response[3 + 3 + 1];

void mpu6050_poll() {
	accel_x = ((int16_t)BYTE_SWAP(mpu6050_response[0])) / 8192.0;
	accel_y = ((int16_t)BYTE_SWAP(mpu6050_response[1])) / 8192.0;
	accel_z = ((int16_t)BYTE_SWAP(mpu6050_response[2])) / 8192.0;
	temperature = ((int16_t)BYTE_SWAP(mpu6050_response[3])) / 340.0 + 36.53;
	gyro_x = ((int16_t)BYTE_SWAP(mpu6050_response[4])) / 32.768 * M_PI / 180.0;
	gyro_y = ((int16_t)BYTE_SWAP(mpu6050_response[5])) / 32.768 * M_PI / 180.0;
	gyro_z = ((int16_t)BYTE_SWAP(mpu6050_response[6])) / 32.768 * M_PI / 180.0;
	i2c_request(MPU6050_ADDR, (uint8_t*)mpu6050_request, sizeof(mpu6050_request), (uint8_t*)(mpu6050_response), sizeof(mpu6050_response), 0);
}

Как можно заметить, я указываю параметр wait = 0 при вызове i2c_request. Это значит, что функция не будет ждать завершения передачи данных, а пойдёт дальше. После опроса датчиков у меня идёт работа с UART, которая занимает где-то 1 миллисекунду. За это время датчики должны успеть опроситься, но это не так, потому что mpu6050_poll выполняется очень долго (значит, i2c_request ждёт освобождения шины перед новым запросом). Дело не в математике - если убрать вызов i2c_request, то функция выполняется менее чем за 1 миллисекунду. Вероятно, я что-то криво настроил.

Исходники моей библиотеки работы с I2C есть в первом посте.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 2)
Ответ на: комментарий от KivApple

А ты тактовый генератор настроил? его настройка у MSP развесистая и можно накосячить. По умолчанию они на внутренней rc-цепочке бегут.

Dark_SavanT ★★★★★
()
Ответ на: комментарий от KivApple

Я наверное неверно выразился. Проблема возникала в двух случаях:

Я думаю, что если бы ты сразу это заметил, то обсуждение гипотетического влияния BSL или переписывания Flash вообще бы не было. Тогда бы сразу инициализированные переменные попали под подозрение (потому что reset не влияет, а снятие напряжения - да). А с самого начала был вывод такой, что без нажатия BSL любые выключения никак не влияют на работоспособоность программы. Это хорошо, что в процессе дошли до того, что ты стал сравнивать память и определил, что RAM значительно меняется. А затем уже и причина найдена была.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Ну если поставить 25, то скорость улучшается где-то на полмиллисекунды, то есть вообще не пропорционально и не значительно. А если 10, то всё перестаёт работать. Но вообще при частоте SMCLK 20 МГц и делителе 50 должно как раз получиться 400 кГц.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от Zubok

Тогда бы UART не работал, а он работает, хотя для него делитель я взял из таблицы из даташита с расчётом для 20 МГц. Ну и делители я таки не ставил.

void setup_clock() {
	P5SEL |= BIT4 | BIT5; // XT1
	P5SEL |= BIT2 | BIT3; // XT2
	UCSCTL6 = XCAP_3; // XT1 capacitors
	UCSCTL0 = 0x1F00;
	UCSCTL1 = DCORSEL_4;
	UCSCTL2 = 20;
	UCSCTL3 = FLLREFDIV__4 | SELREF__XT2CLK;
	UCSCTL4 = SELM__DCOCLK | SELS__DCOCLK | SELA__XT1CLK;
}
KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от Zubok

Нет такого регистра. У MSP430x5xx частоты настраиваются через Unified Clock System. Я инициализирую его так сейчас:

void setup_clock() {
	P5SEL |= BIT4 | BIT5; // XT1
	P5SEL |= BIT2 | BIT3; // XT2
	UCSCTL6 = XCAP_3; // XT1 capacitors
	UCSCTL0 = 0x1F00;
	UCSCTL1 = DCORSEL_4;
	UCSCTL2 = 20;
	UCSCTL3 = FLLREFDIV__4 | SELREF__XT2CLK;
	UCSCTL4 = SELM__DCOCLK | SELS__DCOCLK | SELA__XT1CLK;
	UCSCTL5 = 0;
}

Никакого деления SMCLK нету.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от KivApple

Нет такого регистра. У MSP430x5xx частоты настраиваются через Unified Clock System.

Упс. Было да сплыло.

Zubok ★★★★★
()
Ответ на: комментарий от KivApple

Никакого деления SMCLK нету.

Надо тогда проверить настройки FLL. Нестабильность DCOCLK может быть причиной.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Новая информация: тормозит таки математика.

#define BYTE_SWAP(x) ((uint16_t)((((uint16_t)x << 8) | ((uint16_t)x >> 8))))
...
accel_x = ((int16_t)BYTE_SWAP(mpu6050_response[0])) / 8192.0;
	accel_y = ((int16_t)BYTE_SWAP(mpu6050_response[1])) / 8192.0;
	accel_z = ((int16_t)BYTE_SWAP(mpu6050_response[2])) / 8192.0;
	temperature = ((int16_t)BYTE_SWAP(mpu6050_response[3])) / 340.0 + 36.53;
	gyro_x = ((int16_t)BYTE_SWAP(mpu6050_response[4])) / 32.768 * M_PI / 180.0;
	gyro_y = ((int16_t)BYTE_SWAP(mpu6050_response[5])) / 32.768 * M_PI / 180.0;
	gyro_z = ((int16_t)BYTE_SWAP(mpu6050_response[6])) / 32.768 * M_PI / 180.0;

Вот этот код. Все переменные типа float. На AVR на таком же (по смыслу, писал этот код с нуля) коде тормозов не было (он отрабатывал за примерно 1 мс). Интересно, что если убрать либо преобразования, либо чтения по I2C, то скорость становится нормальной. Тормоза именно когда математика обрабатывает реальные данные, а не нули. Я проверял ассемблерный листинг - оптимизатор ничего не выкидывает, когда я убираю куски, так что замеры нормальные.

А ещё МК перезагружается при попытке использовать %f в snprintf. Это кажется мне несколько странным, но snprintf мне уже не нужен, так что это не так уж критично. Но вообще это не нормально. Почему 16-битный МК тормозит на float-арифметики, которую спокойно переваривает 8-битный? Да ладно бы немного тормозил, так разница практически в 10 раз.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 2)
Ответ на: комментарий от KivApple

Ну, это мне сложно так сказать, что там тормозит, без погружения. У меня msp430 был больше 10 лет назад и я не вспомню таких подробностей сразу. :) Посмотри, кстати, компилятор это 32.768 * M_PI / 180.0; хотя бы в константы преобразует или в лоб долбит? По идее должен.

UPD. И еще попробуй избавиться от деления и замени умножением. Посмотри, даст ли это прирост скорости.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 2)
Ответ на: комментарий от Zubok

Даже простейшая строчка

temperature = (((int16_t)BYTE_SWAP(mpu6050_response[3])) / 340.0 + 36.53) * 100.0;

Выполняется 2 мс. А вот убирание вычисления accel_x, accel_y и accel_z почти не даёт выигрыша. То есть тормозит вычисление температуры и угловых скоростей.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от Zubok

Вычислил 1 / 32.768 * M_PI / 180 на калькуляторе и подсунул умножение на константу. Скорость сильно возросла, хотя до AVR всё равно не дотягивает. Это при том, что я убрал расчёт ускорения и температуры. Просто умножение 3 float на константу занимает около 1.5 мс.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

Вообще, там, где нужна очень высокая скорость вычислений в реальном времени, и в тех случаях, где это возможно, надо уйти в целочисленную арифметику, так как TI ее делает быстро. Например, 234.12*1234.23 лучше считать 23412*123423. С делением то же самое. Но это далеко не везде прокатит. Там же нет FP. все в софте делается.

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

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

KivApple ★★★★★
() автор топика
Ответ на: комментарий от KivApple

Вычислил 1 / 32.768 * M_PI / 180 на калькуляторе и подсунул умножение на константу. Скорость сильно возросла, хотя до AVR всё равно не дотягивает.

Деления убирай. Деление - это самая медленная часть арифметики. Умножай на константу 1/8192. Тут целочисленная арифметика нигде у тебя не покатит?

Zubok ★★★★★
()
Ответ на: комментарий от KivApple

((int16_t)BYTE_SWAP(mpu6050_response[1])) / 8192.0;

Вот это по идее можно в целочисленной посчитать. В long загнать только.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

Целочисленная арифметика может прокатить для температуры.

Формула такая: temperature = (((int16_t)BYTE_SWAP(mpu6050_response[3])) / 340.0 + 36.53). Меня вполне устроит два знака после запятой, а значит можно умножить всё на 100.

Попробовал: temperature = (((int32_t)BYTE_SWAP(mpu6050_response[3])) * 100 / 340 + 3653), результат получается не верным. Где я ошибся?

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

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

На AVR на таком же (по смыслу, писал этот код с нуля) коде тормозов не было (он отрабатывал за примерно 1 мс).

В AVR libm написана на ассемблере и заоптимизирована, а вот в mspgcc не уверен. Надо глянуть.

acos.S      divsf3x.S      fmod.S        fp_rempio2.S  log10.S      signbit.S
addsf3.S    exp.S          fp32def.h     fp_round.S    log.S        sinh.S
addsf3x.S   fdim.S         fp_arccos.S   fp_sinus.S    lrint.S      sin.S
asin.S      Files.am       fp_cmp.S      fp_split3.S   lround.S     sqrt.S
asmdef.h    fixsfdi.S      fp_inf.S      fp_trunc.S    Makefile.am  square.S
atan2.S     fixsfsi.S      fp_mintl.S    fp_zero.S     Makefile.in  tanh.S
atan.S      fixunssfsi.S   fp_mpack.S    frexp.S       modf.S       tan.S
cbrt.S      floatdisf.S    fp_nan.S      gesf2.S       mulsf3.S     trunc.S
ceil.S      floatsisf.S    fp_negdi.S    hypot.S       mulsf3x.S    unordsf2.S
cmpsf2.S    floatundisf.S  fp_norm2.S    inverse.S     negsf2.S
copysign.S  floor.S        fp_powser.S   isfinite.S    ntz.h
cosh.S      fma.S          fp_powsodd.S  isinf.S       pow.S
cos.S       fmax.S         fp_pscA.S     isnan.S       round.S
divsf3.S    fmin.S         fp_pscB.S     ldexp.S       Rules.am

UPD. А вот в MSPGCC, похоже, на C: http://sourceforge.net/p/mspgcc/msp430-libc/ci/a2da19499baed3708658a8f1567834...

Хотя это старые исходники и наверху висит уже, чтобы смотрели на TI. Но 2009 год - это норм. Я не уверен, что что-то изменилось.

Zubok ★★★★★
()
Последнее исправление: Zubok (всего исправлений: 1)
Ответ на: комментарий от Zubok

В ассемблерном листинге моё умножение на константу превращается в вызовы __mspabi_fltlid, __mspabi_mpyd и __mspabi_cvtdf, остальные команды это всякая адресная арифметика и не должны отнимать много времени. Да, у моего МК есть аппаратный 32-битный умножитель.

KivApple ★★★★★
() автор топика
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

В ассемблерном листинге моё умножение на константу превращается в вызовы __mspabi_fltlid, __mspabi_mpyd и __mspabi_cvtdf,

На константу с плавающей точкой? Не понял просто. Быстрее стало при замене деления на умножение?

Zubok ★★★★★
()
Ответ на: комментарий от Zubok

Быстрее стало, но всё равно раза в 3-4 медленнее, чем на AVR.

KivApple ★★★★★
() автор топика
Ответ на: комментарий от KivApple

(((int32_t)BYTE_SWAP(mpu6050_response[3])) * 100 / 340 + 3653),

Посмотри дизассемблер, не вычислил ли gcc 100/340 как константу на этапе компиляции

qbe
()
Ответ на: комментарий от qbe

В таких случаях надо в выражении явно указывать порядок вычисления скобками: (x*const1)/const2

qbe
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.