Pull to refresh

Comments 32

Отличная статья, все просто и доступно. Пишите еще! :)
Спасибо! Если статья окажется востребованной, то постараемся описать все этапы нашей разработки в похожем ключе.
А что ещё прикручивали к дискавери?
С SD картами не пробовали, такого туториала не планируете?
Следующее, что планируем сделать — подключить SD-карточку и аппаратный MP3-декодер для создания простейшего MP3-плеера. К сожалению, в провинции достать тот же MP3-декодер не предоставляется возможности, поэтому всё зависит от сроков работы Почты России и нашего свободного времени.
Если я не ошибаюсь, то дискавери тянет софтварный декодинг с слабым битрейтом. (кажется на том же easyelectronics обсуждалось).
Я звук в speex из буфера играл как-то.
Хотел с карточки, но что-то у меня не завелось с карточкой, а я в этом совсем чайник и на этом застрял пол года тому назад.
С радостью бы почитал туториал на эту тему.
Что тянет VL — не знаю, проц там слабенький, но на STM32F103 mp3-шки даже с приличным битрейтом воспроизводились, а на F4 вообще видео воспроизводится.
Для распаковки MP3 нужно больше памяти, ~24-32кБ. Скорости процессора хватает с запасом.
Смею предположить, что если вы захотите пустить свое изделие в производство, то найти такие дисплеи будет проблематично. Как вариант можно использовать экранчики от Nokia 1202 (сейчас в руках держу такой, датирован мартом 2013). Но у него есть особенность — 9-битный SPI.
Промышленный масштаб мы не планировали, скорее наоборот — хотелось сделать какой-нибудь уникальный гаджет для себя. Пока всё на уровне изучения, составления примерного плана работы и выкройки свободного времени. К слову, китайские запчасти есть в свободном доступе даже в провинции, в том самом сервисном центре, о котором мы писали в статье, по небольшой цене (100 рублей за ту же самую модель)
Оригинальное оформление)
Пара моментов:
— Называть переменные капсом: GPIO_InitTypeDef PORT; — дурной тон. Оставьте капс для макрсов.
— Вот так: VDD_Pin_Port->ODR |= VDD_Pin; писать стоит очень аккуратно. Это типичная проблема Read-Modify-Write, если на середине эта операция прервется прерыванием, которое тоже что-то запишет в порт — получим редкий и трудноуловимый баг. В GPIO портах STM-ок для этого предусмотрены регистры BSR и BRR, обеспечивающие атомарную запись бита в порт.
В остальном неплохая статья для начинающих
Вот так: VDD_Pin_Port->ODR |= VDD_Pin; писать стоит очень аккуратно. Это типичная проблема Read-Modify-Write, если на середине эта операция прервется прерыванием, которое тоже что-то запишет в порт — получим редкий и трудноуловимый баг. В GPIO портах STM-ок для этого предусмотрены регистры BSR и BRR, обеспечивающие атомарную запись бита в порт.

На AVR есть аналогичный механизм для атомарного выставления битов в регистрах. И как минимум gcc умеет распознавать такие языковые конструкции и использует для них правильные последовательности команд.
И как минимум gcc умеет распознавать такие языковые конструкции и использует для них правильные последовательности команд.

Пример можно? А то вот разглядываю ассемблерный листинг gcc, например для модификации бита там всё просто, три команды: IN из порта, модификация, OUT в порт. Вполне можно глюк словить, если не знать.
Поэкспериментировал немного. На stm32 действительно компиляторной магии не получается, надо использовать аппаратную магию в виде BSRRL и BSRRH. А вот на avr оптимизация есть, но она срабатывает не во всех случаях:
void test_or() {
    PORTB |= 1 << 5;
}

void test_and() {
    PORTB &= ~(1 << 5);
}

void test_xor() {
    PORTB ^= 1 << 5;
}

void test_xor2() {
    PORTB ^= ~(1 << 5);
}

void test_or_arg(uint8_t bit) {
    PORTB |= 1 << bit;
}

результат:
00000000 <test_or>:
   0:	2d 9a       	sbi	0x05, 5	; 5
   2:	08 95       	ret

Disassembly of section .text.test_and:

00000000 <test_and>:
   0:	2d 98       	cbi	0x05, 5	; 5
   2:	08 95       	ret

Disassembly of section .text.test_xor:

00000000 <test_xor>:
   0:	85 b1       	in	r24, 0x05	; 5
   2:	90 e2       	ldi	r25, 0x20	; 32
   4:	89 27       	eor	r24, r25
   6:	85 b9       	out	0x05, r24	; 5
   8:	08 95       	ret

Disassembly of section .text.test_xor2:

00000000 <test_xor2>:
   0:	85 b1       	in	r24, 0x05	; 5
   2:	9f ed       	ldi	r25, 0xDF	; 223
   4:	89 27       	eor	r24, r25
   6:	85 b9       	out	0x05, r24	; 5
   8:	08 95       	ret

Disassembly of section .text.test_or_arg:

00000000 <test_or_arg>:
   0:	95 b1       	in	r25, 0x05	; 5
   2:	21 e0       	ldi	r18, 0x01	; 1
   4:	30 e0       	ldi	r19, 0x00	; 0
   6:	08 2e       	mov	r0, r24
   8:	00 c0       	rjmp	.+0      	; 0xa <test_or_arg+0xa>
   a:	22 0f       	add	r18, r18
   c:	0a 94       	dec	r0
   e:	02 f4       	brpl	.+0      	; 0x10 <test_or_arg+0x10>
  10:	92 2b       	or	r25, r18
  12:	95 b9       	out	0x05, r25	; 5
  14:	08 95       	ret

Видно, что в случае, если компилятор видит |= или &= и на этапе компиляции знает, что в маске только один бит, то он применяет специальные атомарные команды. Для ^= он такую оптимизацию применить уже не догадывается. Если операция производится не над константой, то тоже не догадывается и делает неатомарно и неэффективно.

Пожалуй это делает возможный баг еще менее предсказуемым.
Для ^= он такую оптимизацию применить уже не догадывается.

Так для xor вроде бы нету атомарной операции изменения бита?

А так вполне ожидаемо, что на stm32 компилятор не самовольничает. Если в AVR для атомарной установки/сброса бита есть инструкция, то у stm-ок это отдельный регистр. И обращаться по другому адресу компилятор не в праве.
Есть еще такая штука как bit-band-регионы, области памяти, в которых обращение к определенному 32-битному слову эквивалентно обращению к соответствующему биту в другом регионе.
Так для xor вроде бы нету атомарной операции изменения бита?

Там сделано похоже на stm32, для каждого регистра PORTxn есть регистр PINxn. Если туда записать 1, то соответсвующий бит в PORTxn изменится.
И обращаться по другому адресу компилятор не в праве.

Возможно. Хотя это не обычные адреса и запись в BSRRL можно рассматривать как способ обращения к ODR. Но я не готов спорить о таких тонких моментах.
Там сделано похоже на stm32, для каждого регистра PORTxn есть регистр PINxn. Если туда записать 1, то соответсвующий бит в PORTxn изменится

На новых сериях. Если память не изменяет, даже на классической 8й меге этого ещё не было.
Видно, что в случае, если компилятор видит |= или &= и на этапе компиляции знает, что в маске только один бит, то он применяет специальные атомарные команды. Для ^= он такую оптимизацию применить уже не догадывается.

Я именно такой тест и проводил, когда писал коммент. Скорее всего при модификации одного бита порта используются sbi/cbi не из-за соображений атомарности/безопасности, а тупо из-за экономии времени и программной памяти (что логично, раз есть такая инструкция). Достаточно написать PORTB |= (1<<5)|(1<<6); — опять чтение-модификация-запись (по другому ведь нельзя, если не считать, что программист написал бы два sbi, т.к. это выгоднее, но так же небезопасно).

Вот если бы компилятор, например, отключал прерывания при модификации переменных, это можно было бы назвать какой-то защитой, но за такие выкрутасы я бы первый его выкинул.
Вот тут всё замечательно разжевано про SPI.
Студентов учу общаться с памятью типа 25AA010A — у неё замечательная документация, в которой все времена хорошо прописаны и графики красиво нарисованы.

И еще. Мне представляется, что не следует вводить в заблуждение людей, говоря что вы «включаете» или «выключаете» #SS для выбора конкретного слейва. Вы его таки включаете. Просто для #SS активный уровень сигнала — физический ноль, а не единица.
Напишите в следующий раз про реализацию USB :)
Это элементарно. Да и было уже.
Буду благодарен увидеть ссыль. Ибо убил время на своем STM32F уже кучу времени, а он так и не завелся.
А не подскажете в Siemens C55 и Siemens A57 один контроллер дисплея стоит? (единственное отличие в разрешении, 102*64 против 101*64 соответственно)
И даже в этом различий нет. Одна строка является «сервисной» (как мы поняли) и не отображается, поэтому в тех. характеристиках телефонов написано 101х64, а у контроллеров 102х64. Аналогичные дисплеи можно найти в A52\A55\A57
Порадовали! Особенно внимательностью к разъяснению деталей для чайников! Давно примеряюсь к stm32, но пока не начал их использовать. (Фоток бы побольше.)
Расскажите кто-нибудь «для чайников» и по шагам, как это всё компилировать и загружать на устройство.
Допустим, у меня завалялась отладочная плата с мк и готовый дистриб «иде+компилятор+отладчик» под вин скачаный у производителя борды. И в этой связке оно даже работает как нужно, но хочется романтики и писать из под линукса и своей привычной ИДЕшки, компилируя и загружая на устройство чем-либо, что можно поставить из пакетов и поковырять вручную.
Да никаких там проблем. Даже отладка в железе работает. вот например цикл статеек. А вот так я даже всё это на Sublime Text прикручивал.
Моя статья, на которую вы любезно сослались, несколько устарела. Сейчас вместо утилиты stlink я бы порекомендовал использовать OpenOCD, в котором есть отличная поддержка отладчиков ST-Link 1 и 2, которые стоят на платах STM32***Discovery. Про его настройку я читал тут:

we.easyelectronics.ru/CADSoft/ubuntu-eclipse-code-sourcery-openocd-j-link-arm-ili-bystryy-start-dlya-somnevayuschihsya.html

# Только я использую немного другую последовательность команд для OpenOCD:

file Debug/stm32vld_nrf_rx.elf
target extended-remote :3333
monitor reset halt
load
monitor reset

# Сам OpenOCD запускаю так:

sudo openocd -f <config>

# Конфиг для STM32VLDiscovery:

telnet_port 4444
gdb_port 3333

source [find interface/stlink-v1.cfg]
source [find target/stm32f1x_stlink.cfg]

set WORKAREASIZE 0x2000
set CHIPNAME «STM32F100RBT6»

init
reset halt

# Последний раз я использовал OpenOCD 0.6.1 в Ubuntu 12.10

P.S. Если гражданину gaelpa покажется мало приключений, описанных в обеих статьях, он может также попытаться собрать GCC toolchain вручную *громкий дьявольский смех*
Да, эта статья у меня давно в избранном, вот только руки не дойдут испытать.
Уровень «чайников» тоже разный)
Sign up to leave a comment.

Articles