2 3 + . \ Сложение 2+3 и вывод результата
: double 2 * ; \ Определение слова для умножения на 2
5 double . \ Результат: 10
\ Переключение в режим компиляции в RAM (будет потеряно при перезагрузке)
compiletoram
\ Переключение в режим компиляции во Flash (сохранится после перезагрузки)
compiletoflash
\ Создание точки восстановления с именем "basic_image"
compiletoflash \ Важно: cornerstone работает только во Flash
: basic_image
begin here dup flash-pagesize 1- and while 0 h, repeat
does> begin dup dup flash-pagesize 1- and while 2+ repeat
cr eraseflashfrom ;
basic_image \ Откат всех изменений, сделанных после создания cornerstone
eraseflash \ Полная очистка пользовательской программы
\ Начало разработки
compiletoram \ Компиляция в RAM для тестирования
\ Тестирование базовых функций
: test-led IO3 iox! ;
\ После отладки, сохранение во Flash
compiletoflash
: test-led IO3 iox! ;
\ Создание точки восстановления
: basic_image
begin here dup flash-pagesize 1- and while 0 h, repeat
does> begin dup dup flash-pagesize 1- and while 2+ repeat
cr eraseflashfrom ;
\ Продолжение разработки...
: more-functions
\ ... дополнительный код
\ В случае проблем возврат к базовой версии
basic_image
\ Настройка пина на вывод (push-pull)
OMODE-PP IO3 io-mode!
\ Настройка пина на ввод с подтяжкой вверх
IMODE-HIGH IO7 io-mode!
\ Настройка пина на ввод с подтяжкой вниз
IMODE-LOW IO2 io-mode!
\ Настройка пина для аналогового ввода
IMODE-ADC IO6 io-mode!
\ Установка высокого уровня
1 IO3 io!
\ Установка низкого уровня
0 IO3 io!
\ Инверсия состояния пина
IO3 iox!
\ Чтение состояния пина (результат: 0 или -1)
IO7 io@ .
\ Инициализация ADC
: adc-init ( -- )
9 bit $40021018 bis! \ Включаем тактирование ADC (RCC_APB2ENR)
adc-calib
1 ADC-CR ! \ Включаем ADC (устанавливаем ADEN)
adc-once drop ;
\ Деинициализация ADC
: adc-deinit ( -- )
1 bit ADC-CR bis! \ Отключаем ADC
9 bit $40021018 bic! ; \ Выключаем тактирование ADC
\ Калибровка ADC: устанавливаем бит ADCAL (бит 31) в ADC-CR и ждём его сброса
: adc-calib ( -- )
31 bit ADC-CR bis! \ Устанавливаем ADCAL
begin 31 bit ADC-CR bit@ 0= until ;
\ Однократное измерение ADC
: adc-once ( -- u )
2 bit ADC-CR bis! \ Устанавливаем ADSTART (бит 2)
begin 2 bit ADC-ISR bit@ until \ Ожидаем установки флага EOC
ADC-DR @ ;
\ Чтение значения ADC с выбранного канала.
\ Функция выполняет два измерения для обхода errata.
: adc ( pin -- u )
io# bit ADC-CHSELR ! \ Выбираем канал ADC, используя вашу функцию io#
adc-once drop adc-once ;
\ Инициализация АЦП
adc-init
\ Настройка пина для АЦП
IMODE-ADC IO3 io-mode!
\ Чтение сырого значения АЦП (0-4095)
IO3 adc .
\ Чтение значения в милливольтах
IO3 adc-mv .
\ Чтение температуры кристалла в градусах Цельсия
adc-temp .
: check-voltage
IMODE-ADC IO1 io-mode!
adc-init
." Напряжение на IO1: " IO1 adc-mv . ." мВ" cr
adc-deinit ;
: timer-base ( n -- addr )
drop $40000000 ; \ Используем TIM2 для упрощения
\ Бит и адрес включения TIM2
: timer-enabit ( n -- bit addr )
drop 0 bit RCC RCC_APB1ENR_OFFSET + ;
\ Инициализация таймера
: timer-init ( u n -- ) \ u = combined prescaler and auto-reload value
dup timer-enabit bis! \ Clock enable
timer-base >r
dup 16 rshift \ Extract upper 16 bits (PSC)
TIM.PSC r@ + ! \ Write to PSC register
$FFFF and \ Extract lower 16 bits (ARR)
TIM.ARR r@ + ! \ Write to ARR register
8 bit TIM.DIER r@ + bis! \ Update event enable
%010 4 lshift TIM.CR2 r@ + ! \ MMS = update
0 bit TIM.CR1 r@ + bis! \ Enable timer
r> drop ;
: timer-deinit ( n -- ) \ disable timer n
timer-enabit bic! ;
\ The following pins are supported for PWM setup on STM32F042:
\ TIM2: PA0 PA1 PA2 PA3
\ Только TIM2
: p2tim ( pin -- n ) drop 2 ;
\ Получаем номер канала по пину (PA0=0, PA1=1, PA2=2, PA3=3)
: p2cmp ( pin -- n ) $3 and ;
\ Настройка альтернативных функций для всех поддерживаемых пинов
: pin-af2 ( pin -- )
dup PA0 = if
GPIO-BASE GPIO.AFRL + dup @
$FFFFFFF0 and 2 or swap ! \ AF2 для PA0
else dup PA1 = if
GPIO-BASE GPIO.AFRL + dup @
$FFFFF0FF and 2 4 lshift or swap ! \ AF2 для PA1
else dup PA2 = if
GPIO-BASE GPIO.AFRL + dup @
$FFFF0FFF and 2 8 lshift or swap ! \ AF2 для PA2
else dup PA3 = if
GPIO-BASE GPIO.AFRL + dup @
$FFF0FFFF and 2 12 lshift or swap ! \ AF2 для PA3
then then then then drop ;
: pwm-init ( hz pin -- )
>r
OMODE-AF-PP r@ io-mode!
r@ pin-af2
\ Корректный расчет PSC и ARR для заданной частоты
48000000 swap / \ Делитель для достижения нужной частоты
\ Разделим на PSC и ARR (оба 16-битные)
dup 65536 > if \ Если делитель > 65536, нужен предделитель
dup 65536 / 1+ dup ( div div/65536+1 div/65536+1 )
>r ( div div/65536+1 ) ( R: div/65536+1 )
* r> ( div*div/65536+1 div/65536+1 )
swap over / 1- \ ARR = (делитель / PSC) - 1
swap 1- \ PSC = div/65536+1 - 1
else \ Если делитель <= 65536
1 swap 1- \ PSC = 0, ARR = делитель - 1
then
\ Установка PSC и ARR
swap 16 lshift or \ Создаем комбинированное значение PSC:ARR
r@ p2tim timer-init
\ Настройка режима PWM mode 1
r@ p2cmp dup 2 < if
\ Каналы 1 и 2 (PA0, PA1) используют CCMR1
2 * 8 * $68 swap lshift
r@ p2tim timer-base TIM.CCMR1 + bis!
else
\ Каналы 3 и 4 (PA2, PA3) используют CCMR2
2 - 2 * 8 * $68 swap lshift
r@ p2tim timer-base TIM.CCMR2 + bis!
then
\ Включение выхода
r@ p2cmp 4 * bit r> p2tim timer-base TIM.CCER + bis!
;
\ Установка значения PWM
: pwm ( u pin -- )
>r
r@ p2tim timer-base TIM.ARR + @ \ Получаем значение ARR
1+ \ ARR+1 = полный период
swap 10000 */ \ Масштабирование: u * (ARR+1) / 10000
r@ p2cmp cells \ Смещение для нужного канала CCR
r> p2tim timer-base $34 + + \ Адрес регистра CCRx
! \ Записываем значение
;
\ Инициализация PWM на пине IO3 с частотой 1000 Гц
1000 IO3 pwm-init
\ Установка заполнения 50% (5000 из 10000)
5000 IO3 pwm
: led-fade
1000 IO3 pwm-init \ инициализация PWM
10 0 do \ 10 циклов
i 1000 * IO3 pwm \ значение PWM от 0 до 9000
500 ms-delay \ пауза 500 мс
loop
0 IO3 pwm ; \ выключаем в конце
\ Инициализация системного таймера
systick-init
\ Чтение текущего значения счетчика миллисекунд
ms_tick_cnt @ .
: measure-time
systick-init
ms_tick_cnt @
\ выполняем измеряемую операцию
1000 0 do loop
ms_tick_cnt @ swap -
." Время выполнения: " . ." мс" ;
\ Инициализация IWDG с таймаутом 1000 мс
1000 iwdg-init-timeout
\ Сброс сторожевого таймера (необходимо вызывать периодически)
iwdg-refresh
: safe-loop
1000 iwdg-init-timeout
begin
\ Основной код
." Работаю... " cr
500 ms-delay
\ Сброс сторожевого таймера
iwdg-refresh
again ;
: settings-write ( value addr -- )
SETTINGS_PAGE + \ ( value abs_addr )
2dup h@ = if \ Check if value equals current value at abs_addr
2drop \ If equal, do nothing
else
$08000000 - hflash! \ If different, write value
then ;
: settings-read ( addr -- value )
SETTINGS_PAGE + h@ ; \ Read half-word from absolute address
\ Сохранение настроек во флеш
: settings-erase ( -- )
SETTINGS_PAGE $08000000 - flashpageerase
;
\ Стирание страницы настроек
settings-erase
\ Запись значения в адрес
123 0 settings-write \ Запись числа 123 по смещению 0 последнего блока 1 КБ
\ Чтение значения из адреса
0 settings-read . \ Чтение значения по смещению 0
\ Структура конфигурации
0 constant CFG_PWM_FREQ \ смещение для частоты PWM
2 constant CFG_ADC_THRESH \ смещение для порога ADC
: save-config ( pwm-freq adc-thresh -- )
settings-erase
CFG_ADC_THRESH settings-write
CFG_PWM_FREQ settings-write ;
: load-config
CFG_PWM_FREQ settings-read
CFG_ADC_THRESH settings-read ;
: blink
10 0 do
1 LED1 io!
500 ms-delay
0 LED1 io!
500 ms-delay
loop ;
: wait-button
IMODE-HIGH IO7 io-mode! \ Кнопка с подтяжкой вверх
begin
IO7 io@ 0= \ Проверка на нажатие (0 = нажата)
until
." Кнопка нажата!" ;
: servo-init
50 IO3 pwm-init ; \ 50 Гц для стандартных сервоприводов
: servo-set ( angle -- ) \ angle: 0-180
\ Преобразуем угол в длительность импульса
\ от 500 до 2500 мкс (50 до 250 из 10000)
180 min 0 max \ Ограничиваем значение от 0 до 180
2000 * 10000 / \ Масштабируем
500 + \ Добавляем смещение
IO3 pwm ;
: servo-test
servo-init
0 servo-set 1000 ms-delay
90 servo-set 1000 ms-delay
180 servo-set 1000 ms-delay
90 servo-set ;
: threshold-alert
IMODE-ADC IO4 io-mode! \ Аналоговый вход
OMODE-PP IO7 io-mode! \ Светодиод для сигнализации
adc-init
begin
IO4 adc-mv \ Измеряем значение в мВ
dup . ." мВ "
2000 > \ Порог 2000 мВ
if
." ТРЕВОГА!" cr
1 IO7 io! \ Включаем светодиод
else
cr
0 IO7 io! \ Выключаем светодиод
then
500 ms-delay
key? until \ Выход по нажатию клавиши
adc-deinit ;
: multi-pwm-test
\ Инициализация нескольких PWM каналов
1000 IO3 pwm-init \ IO3 = PA0 (канал 1)
1000 IO7 pwm-init \ IO7 = PA1 (канал 2)
1000 IO2 pwm-init \ IO2 = PA2 (канал 3)
1000 IO6 pwm-init \ IO6 = PA3 (канал 4)
\ Установка разных уровней PWM
2500 IO3 pwm
5000 IO7 pwm
7500 IO2 pwm
9000 IO6 pwm
2000 ms-delay
\ Сброс всех PWM
0 IO3 pwm
0 IO7 pwm
0 IO2 pwm
0 IO6 pwm ;
: show-device-info
." Chip ID: " chipid drop drop drop .
." Hardware ID: " hwid .
." Flash size: " flash-kb . ." KB" ;