PY32F002A — Cortex-M0+ 32KB flash, 4KB RAM: ардуиним ниже плинтуса

Если кто-то из проницательных читателей сталкивался с этим очередным китайским чудом, наверняка спросит — какую траву автор курит? У них же по спецификации 20 KB флеша и 3 KB оперативки. Странная цифра 3 в наше время, когда у каждого настоящего программиста 16 пальцев на руках — чтобы считать было легче. Но еще не вечер, подождите и почитайте дальше. Мимо такого чуда, даже если спецификации были бы правдой, пройти невозможно — его продают за 9...11 центов штука в розницу. Увидев такую халяву, я немедленно заказал 20 штук на попробовать. Попробовав — еще с полсотни, в кулацком хозяйстве пулемет завсегда сгодится. За такую цену эта вещь для пионеров и пенсионеров — действительно вещь.
Для интересующихся — все равно же кто-то спросит — я его покупал здесь.

Любителем Windows больших проблем с программированием этих контроллеров нет, Keil uVision поддерживает эти микроконтроллеры, хотя и не их «каропки», но все же. А с сайта Puya Semiconductor можно скачать загрузчик, позволяющий с помощью последовательного интерфейса прошивать эти контроллеры. Но Keil и разработка… Нет, все понятно — их компиляторы лучшие. Но редактор — Боже мой, как можно так жить? Лет 30 назад лучше ничего и не было, но сейчас же куча редакторов, которые и дополнять код умеют, и 90 процентов ошибок еще на этапе написания выявят и помогут их исправить? И все это совершенно безвозмездно, т. е. даром.
У Keil есть еще один неоспоримый плюс — его можно поставить под Wine в Linux вообще без проблем. Естественно, вся отладочная аппаратура работать не будет, но компилировать можно. Учитывая, что программы размером до 32 кбайт он компилирует без денег — для PY32F002A и любительских проектов очень подходит.
С линуксистами несколько сложнее, но с конце концов, оказывается даже лучше.
— Что же из этого следует? — Следует жить,
шить сарафаны и легкие платья из ситца.
— Вы полагаете, все это будет носиться?
— Я полагаю, что все это следует шить.
Когда эти микросхемы пришли, я два дня убил, пытаясь заставить их работать через SWD и свисток-клон st-link. В конце-концов терпение лопнуло и хотел было все это забросить. Но если китайцам хватает последовательного загрузчика, почему мне это должно быть в падлу? Вроде религия позволяет, и миллионы ардуинщиков вполне довольны загрузкой AVR и всяких ESP32 через адаптеры последовательного порта USB? Миллионы мух не могут ошибаться.

Еще за два дня я напитонил такой загрузчик. До ума не довел — но он читает память, записывает, сбрасывает процессор — да вот возьмите его здесь и пользуйтесь на доброе здоровье, хоть под Linux, хоть под Windows. Может, потом сделаю автоматический перевод PY32F002A в режим загрузки и сброс и допишу свой загрузчик — а может кнопки буду жать.

Загрузчик лежит здесь — но скачать может быть проблемой, mail.ru стал давать скачивать файл только 5 раз в день. Меркантильные кю, куда этот мир катится? За снятие ограничений рубль просят — а где я вам его возьму?

Пока я этот загрузчик писал, решил заодно тест памяти сделать — ну не может микросхема такого сорта за такую цену быть без подвоха. Когда я тестировал флеш, чип смотрел на меня, как кот, который знает, где спрятал колбасу: подвох нашелся — 20 кбайт протестировалась, а он все тестирует и тестирует. Натестировал 32 кбайта, на этом остановился. Интересно девки пляшут…
А сколько же оперативки тогда? С нормальным тестом обломился — оказалось, что встроенный загрузчик позволяет из оперативной памяти читать только по 4 байта, а писать вообще не позволяет. Ну да ладно, читаю каждый 256-байт и смотрю на его случайность — как должно быть после сброса. После примитивного теста — 4 кбайта у микропроцессора всяко разно есть.
Стал искать, что поэтому поводу в интернетах пишут — да, не я первый. Народ уже давно нашел у чипа и избыток памяти, и обнаружил недокументированный PLL, с которым контроллер уже и на 48 МГц работать может. Особо буйные разогнали его на 64 МГц, но, говорят, уже не вся периферия держится, ну и скорость чтения флеша уже подстраивать надо. Так же у него обнаружили еще один UART, SPI и якобы несуществующий DMA. Судя по всем найденным ништякам — это PY32F030.
Крвткий список обещаний и реальности:
Cortex-M0+ (24 МГц) -> Cortex-M0+ (48 МГц) с PLL и DMA
Flash 20 КБ -> 32 КБ
RAM 3 КБ -> 4 КБ
UART 1 -> 2
SPI 1 -> 2
ADC 1x12-bit
Компаратор - 2
I²C
TIM1, TIM16, LPTIM -> +3 таймера
Питание 1.7–5.5 В
Небольшое лирическое отступление — так как PlusPda не позволяет никаких вложений, кроме графики, и размер текста ограничен — а исходники программатора перевалили 30 килобайт, стал думать, а как же все это обойти? Выход оказался прост — заархивировал исходники и разбил их в несколько QR кодов. Самый большой их них позволяет хранить почти 3 килобайта, заархивированные исходники заняли 4 картинки. Интересно, какой длинны нужен забор, чтобы на него наклеить закартиненный инсталлятор Windows?
Если кому идея понравилась, ниже найдете питоновый код — пользуйтесь на доброе здоровье, он преобразует любые файлы в картинки и обратно — картинки в файлы. Главное — порядок картинок не перепутать, это никак не контролируется — хотя и можно, но лень.
#!/usr/bin/env python3
import argparse
import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers import SquareModuleDrawer
import os
import sys
import zlib
import base64
import re
import warnings
from typing import List
from PIL import Image
import pyzbar.pyzbar as pyzbar
# Suppress warnings
warnings.filterwarnings("ignore")
os.environ['ZBAR_SILENT'] = '1'
def show_usage_instructions():
print("""
QR File Encoder/Decoder Tool
===========================
Usage:
To encode a file into QR code(s):
qr_file.py encode -i <input_file> [-o <output_template>] [-v <version>] [-e <error_level>] [-b <box_size>]
To decode QR code(s) back to a file:
qr_file.py decode -i <input_qr> -o <output_file>
Options:
encode:
-i, --input Input file to encode (required)
-o, --output Output template (default: output.png)
-v, --version Max QR version (1-40, optional)
-e, --error Error correction level (L,M,Q,H, default: L)
-b, --box QR module size in pixels (default: 10)
decode:
-i, --input Input QR image (required)
-o, --output Output file name (required)
Examples:
qr_file.py encode -i document.pdf -o doc_qr.png
qr_file.py decode -i doc_qr.png -o restored.pdf
Note: For large files, multiple QR codes will be created automatically.
""")
def calculate_max_qr_size(version: int, error_correction: int) -> int:
"""Calculate maximum data size for specified version and error correction level"""
capacity_table = {
qrcode.constants.ERROR_CORRECT_L: [
17, 32, 53, 78, 106, 134, 154, 192, 230, 271, 321, 367, 425, 458, 520, 586, 644, 718, 792, 858,
929, 1003, 1091, 1171, 1273, 1367, 1465, 1528, 1628, 1732, 1840, 1952, 2068, 2188, 2303, 2431,
2563, 2699, 2809, 2953
],
qrcode.constants.ERROR_CORRECT_M: [
14, 26, 42, 62, 84, 106, 122, 152, 180, 213, 251, 287, 331, 362, 412, 450, 504, 560, 624, 666,
711, 779, 857, 911, 997, 1059, 1125, 1190, 1264, 1370, 1452, 1538, 1628, 1722, 1809, 1911, 1989,
2099, 2213, 2331
],
qrcode.constants.ERROR_CORRECT_Q: [
11, 20, 32, 46, 60, 74, 86, 108, 130, 151, 177, 203, 241, 258, 292, 322, 364, 394, 442, 482,
509, 565, 611, 661, 715, 751, 805, 868, 908, 982, 1030, 1112, 1168, 1228, 1283, 1351, 1423, 1499,
1579, 1663
],
qrcode.constants.ERROR_CORRECT_H: [
7, 14, 24, 34, 44, 58, 64, 84, 98, 119, 137, 155, 177, 194, 220, 250, 280, 310, 338, 382,
403, 439, 461, 511, 535, 593, 625, 658, 698, 742, 790, 842, 898, 958, 983, 1051, 1093, 1139, 1219, 1273
]
}
return capacity_table[error_correction][version-1] if version <= 40 else capacity_table[error_correction][-1]
def find_optimal_version(data_size: int, error_correction: int) -> int:
"""Find minimum QR version that can accommodate the data"""
for version in range(1, 41):
if data_size <= calculate_max_qr_size(version, error_correction):
return version
raise ValueError("Data too large for QR code (max version 40)")
def split_filename(filename: str) -> tuple:
"""Split filename into base name and number"""
match = re.match(r'^(.*?)(\d+)?$', os.path.splitext(filename)[0])
return match.group(1), int(match.group(2)) if match.group(2) else None
def encode_file(input_file: str, output_template: str, qr_version: int = None,
error_correction: int = qrcode.constants.ERROR_CORRECT_L, box_size: int = 10):
"""Encode file into one or multiple QR codes"""
with open(input_file, 'rb') as f:
file_data = f.read()
compressed_data = zlib.compress(file_data)
encoded_data = base64.b64encode(compressed_data)
max_chunk_size = calculate_max_qr_size(40, error_correction) - 10
chunks = [encoded_data[i:i+max_chunk_size] for i in range(0, len(encoded_data), max_chunk_size)]
if len(chunks) > 1:
print(f"File too large, splitting into {len(chunks)} QR codes")
for i, chunk in enumerate(chunks):
chunk_with_meta = f"{i+1}/{len(chunks)}:".encode() + chunk
optimal_version = find_optimal_version(len(chunk_with_meta), error_correction)
if qr_version is not None:
optimal_version = min(optimal_version, qr_version)
qr = qrcode.QRCode(
version=optimal_version,
error_correction=error_correction,
box_size=box_size,
)
qr.add_data(chunk_with_meta)
qr.make(fit=True)
img = qr.make_image(image_factory=StyledPilImage, module_drawer=SquareModuleDrawer())
if len(chunks) > 1:
base, ext = os.path.splitext(output_template)
output_file = f"{base}{i+1:02d}{ext}"
else:
output_file = output_template
img.save(output_file)
print(f"Created QR code: {output_file} (version {optimal_version})")
def find_related_qr_files(base_file: str) -> List[str]:
"""Find all related QR files for assembly"""
base, ext = os.path.splitext(base_file)
base_name, _ = split_filename(os.path.basename(base))
dir_path = os.path.dirname(base_file) or '.'
pattern = re.compile(rf'^{re.escape(base_name)}(\d+){re.escape(ext)}$')
related_files = []
for f in os.listdir(dir_path):
if pattern.match(f):
related_files.append(os.path.join(dir_path, f))
if not related_files:
return [base_file]
related_files.sort(key=lambda x: int(split_filename(os.path.basename(x))[1]))
return related_files
def decode_file(input_file: str, output_file: str):
"""Decode one or multiple QR codes back to file"""
qr_files = find_related_qr_files(input_file)
print(f"Found {len(qr_files)} QR files to process")
all_data = bytearray()
total_parts = None
for qr_file in qr_files:
try:
img = Image.open(qr_file)
decoded = pyzbar.decode(img)
if not decoded:
raise ValueError(f"Failed to decode QR code: {qr_file}")
data = decoded[0].data
if b':' in data:
meta, chunk = data.split(b':', 1)
part_info = meta.decode().split('/')
current_part = int(part_info[0])
total_parts = int(part_info[1]) if total_parts is None else total_parts
if total_parts != int(part_info[1]):
raise ValueError("Mismatch in QR code parts count")
else:
chunk = data
if len(qr_files) > 1:
raise ValueError("Missing part number information in QR code")
all_data.extend(chunk)
print(f"Processed QR code: {qr_file}")
except Exception as e:
raise ValueError(f"Error processing {qr_file}: {str(e)}")
if total_parts is not None and len(qr_files) != total_parts:
raise ValueError(f"Found {len(qr_files)} parts, expected {total_parts}")
try:
compressed_data = base64.b64decode(all_data)
file_data = zlib.decompress(compressed_data)
with open(output_file, 'wb') as f:
f.write(file_data)
print(f"\nFile successfully restored: {output_file}")
print(f"File size: {len(file_data)} bytes")
except Exception as e:
raise ValueError(f"Error decoding data: {str(e)}")
def main():
# Show instructions if no arguments provided
if len(sys.argv) == 1:
show_usage_instructions()
sys.exit(0)
parser = argparse.ArgumentParser(description='Convert file to QR code(s) and back', add_help=False)
subparsers = parser.add_subparsers(dest='command', required=True)
encode_parser = subparsers.add_parser('encode', help='Convert file to QR code(s)')
encode_parser.add_argument('-i', '--input', required=True, help='Input file')
encode_parser.add_argument('-o', '--output', default='output.png',
help='Output image template')
encode_parser.add_argument('-v', '--version', type=int,
help='Max QR code version (1-40)')
encode_parser.add_argument('-e', '--error', default='L', choices=['L', 'M', 'Q', 'H'],
help='Error correction level (L, M, Q, H)')
encode_parser.add_argument('-b', '--box', type=int, default=10, help='QR module size')
decode_parser = subparsers.add_parser('decode', help='Convert QR code(s) back to file')
decode_parser.add_argument('-i', '--input', required=True,
help='Input QR image file')
decode_parser.add_argument('-o', '--output', required=True, help='Output file name')
# Add help option manually
parser.add_argument('-h', '--help', action='help', default=argparse.SUPPRESS,
help='Show this help message and exit')
args = parser.parse_args()
error_mapping = {
'L': qrcode.constants.ERROR_CORRECT_L,
'M': qrcode.constants.ERROR_CORRECT_M,
'Q': qrcode.constants.ERROR_CORRECT_Q,
'H': qrcode.constants.ERROR_CORRECT_H
}
try:
if args.command == 'encode':
encode_file(
args.input,
args.output,
qr_version=args.version,
error_correction=error_mapping[args.error],
box_size=args.box
)
elif args.command == 'decode':
decode_file(args.input, args.output)
except Exception as e:
print(f"Error: {e}", file=sys.stderr)
sys.exit(1)
if __name__ == '__main__':
main()




Чем то это напоминает перфокартное время — когда и перфокарты были картоннее, и трава зеленее, и деревья больше. Про девок вообще молчу.

Загрузка загрузкой, но ведь нужно что-то написать, чтобы это загружать можно было. Ага, что бы продать что-нибудь ненужное, нужно сперва купить что-нибудь ненужное, а у нас денег нет.
Не буду вас долго утомлять, большинству это будет абсолютно фиолетово, а для интересующихся все лежит тут.
Работает все двояко — с одной стороны в Visual Studio Code и Platformio вы можете пользоваться всеми супер-пупер возможностями хорошей среды разработки, а когда все написано — запускаете make и имеете бинарный файл, который тут же прошиваете.


И не благодарите — все файлы и библиотеки я стащил у Keil, естественно, с gcc все это работать не будет. Некоторое время пришлось убить, чтобы все это перелопатить — кроме меня, все это никто не тестировал, но у меня пока все работало без проблем — мой тестовый пример, где я тестировал таймер и последовательный интерфейс.
#include "py32f002ax5.h"
void Delay(uint32_t count)
{
while (count--) __NOP();
}
// Константы для конфигурации тактовой частоты
#define HSI_FREQUENCY 24000000UL // Частота HSI в Гц
#define HSI_FS_VALUE 4 // Значение для 24 МГц
// Функция настройки системного тактирования
void SystemClock_Config(void)
{
// 1. Настройка и включение HSI (24 МГц)
RCC->ICSCR &= ~RCC_ICSCR_HSI_FS_Msk; // Очистка поля HSI_FS
RCC->ICSCR |= (HSI_FS_VALUE << RCC_ICSCR_HSI_FS_Pos); // Установка 24 МГц
RCC->CR |= RCC_CR_HSION; // Включение HSI
// 2. Ожидание стабилизации HSI
while (!(RCC->CR & RCC_CR_HSIRDY)); // Ждем готовности HSI
// 3. Настройка делителей и источника тактирования
RCC->CR &= ~RCC_CR_HSIDIV_Msk; // Отключение делителя HSI
RCC->CFGR &= ~(RCC_CFGR_HPRE | RCC_CFGR_PPRE); // AHB и APB без деления
RCC->CFGR &= ~RCC_CFGR_SW; // Выбор HSI как источника
RCC->CFGR |= RCC_CFGR_MCOSEL_0; // Настройка MCO (HSI)
// 4. Ожидание переключения на HSI
while (RCC->CFGR & RCC_CFGR_SWS); // Ждем подтверждения
// 5. Обновление переменной системной частоты
SystemCoreClock = HSI_FREQUENCY;
}
void TIM1_PWM_Init(void)
{
// Включить тактирование TIM1 и GPIOA, GPIOB
RCC->APBENR2 |= RCC_APBENR2_TIM1EN; // Включить тактирование TIM1
RCC->IOPENR |= RCC_IOPENR_GPIOAEN | RCC_IOPENR_GPIOBEN; // Включить тактирование GPIOA и GPIOB
// Настройка пинов PB3 (TIM1_CH2), PA0 (TIM1_CH3), PA1 (TIM1_CH4), PA7 (TIM1_CH1)
GPIOB->MODER &= ~(GPIO_MODER_MODE3); // Сбросить режим для PB3
GPIOB->MODER |= GPIO_MODER_MODE3_1; // Альтернативная функция
GPIOA->MODER &= ~(GPIO_MODER_MODE0 | GPIO_MODER_MODE1 | GPIO_MODER_MODE7); // Сбросить режим для PA0, PA1, PA7
GPIOA->MODER |= (GPIO_MODER_MODE0_1 | GPIO_MODER_MODE1_1 | GPIO_MODER_MODE7_1); // Альтернативная функция
GPIOB->AFR[0] &= ~GPIO_AFRL_AFSEL3; // Сбросить AF для PB3
GPIOB->AFR[0] |= (1 << GPIO_AFRL_AFSEL3_Pos); // AF1 для TIM1_CH2
GPIOA->AFR[0] &= ~(GPIO_AFRL_AFSEL0 | GPIO_AFRL_AFSEL1 | GPIO_AFRL_AFSEL7); // Сбросить AF для PA0, PA1, PA7
// AF13 для TIM1_CH3, AF13 для TIM1_CH4, AF2 для TIM1_CH1N
GPIOA->AFR[0] |= (13 << GPIO_AFRL_AFSEL0_Pos) | (13 << GPIO_AFRL_AFSEL1_Pos) | (2 << GPIO_AFRL_AFSEL7_Pos);
// Настройка TIM1 для PWM 16 кГц
TIM1->ARR = 1499; // Период для 16 кГц при 24 МГц (24,000,000 / 16,000 - 1)
TIM1->PSC = 0; // Предделитель = 1 (без деления)
// Настройка PWM режима для каналов 1, 2, 3, 4
TIM1->CCMR1 &= ~(TIM_CCMR1_OC1M | TIM_CCMR1_OC2M); // Сбросить режимы каналов 1 и 2
TIM1->CCMR1 |= (TIM_CCMR1_OC1M_1 | TIM_CCMR1_OC1M_2 | TIM_CCMR1_OC1PE); // PWM Mode 1 + предзагрузка для CH1
TIM1->CCMR1 |= (TIM_CCMR1_OC2M_1 | TIM_CCMR1_OC2M_2 | TIM_CCMR1_OC2PE); // PWM Mode 1 + предзагрузка для CH2
TIM1->CCMR2 &= ~(TIM_CCMR2_OC3M | TIM_CCMR2_OC4M); // Сбросить режимы каналов 3 и 4
TIM1->CCMR2 |= (TIM_CCMR2_OC3M_1 | TIM_CCMR2_OC3M_2 | TIM_CCMR2_OC3PE); // PWM Mode 1 + предзагрузка для CH3
TIM1->CCMR2 |= (TIM_CCMR2_OC4M_1 | TIM_CCMR2_OC4M_2 | TIM_CCMR2_OC4PE); // PWM Mode 1 + предзагрузка для CH4
// Включить выходы каналов (CH0 - комплементарный)
TIM1->CCER |= TIM_CCER_CC1NE | TIM_CCER_CC2E | TIM_CCER_CC3E | TIM_CCER_CC4E;
// Установить duty cycle
TIM1->CCR1 = 150; // 10% для CH1 (PA7 NE)
TIM1->CCR2 = 375; // 25% для CH2 (PB3)
TIM1->CCR3 = 750; // 50% для CH3 (PA0)
TIM1->CCR4 = 1125; // 75% для CH4 (PA1)
// Включить автопредзагрузку и таймер
TIM1->CR1 |= TIM_CR1_ARPE; // Включить автопредзагрузку
TIM1->BDTR |= TIM_BDTR_MOE; // Включить основные выходы
TIM1->CR1 |= TIM_CR1_CEN; // Включить TIM1
}
// Инициализация UART (PA2 - TX, PA3 - RX)
void UART_Init(void)
{
// Включить тактирование USART1
RCC->APBENR2 |= RCC_APBENR2_USART1EN;
// Настройка пинов PA2 (TX) и PA3 (RX)
GPIOA->MODER &= ~(GPIO_MODER_MODE2 | GPIO_MODER_MODE3); // Сбросить режим
GPIOA->MODER |= (GPIO_MODER_MODE2_1 | GPIO_MODER_MODE3_1); // Альтернативная функция
GPIOA->AFR[0] &= ~(GPIO_AFRL_AFSEL2 | GPIO_AFRL_AFSEL3); // Сбросить AF
GPIOA->AFR[0] |= (1 << GPIO_AFRL_AFSEL2_Pos) | (1 << GPIO_AFRL_AFSEL3_Pos); // AF1 для USART1
// Настройка USART1: 115200 бод, 8 бит, 1 стоп-бит, без паритета
//USART1->BRR = SystemCoreClock / 115200; // Делитель для 115200 бод при 24 МГц
// HSI not calibrated well
USART1->BRR = 22300000 / 115200; // Делитель для 115200 бод при 24 МГц
USART1->CR1 = USART_CR1_RE | USART_CR1_TE | USART_CR1_UE | USART_CR1_RXNEIE; // Включить приемник, передатчик, USART и прерывание по приему
NVIC_EnableIRQ(USART1_IRQn); // Включить прерывание USART1
}
// Функция отправки символа по UART
void UART_Transmit(uint8_t data)
{
while (!(USART1->SR & USART_SR_TXE)); // Ждать, пока буфер передачи не пуст
USART1->DR = data; // Отправить символ
}
// Переменная для хранения последнего принятого печатаемого символа
volatile uint8_t last_printable_char = '#'; // По умолчанию '#'
volatile uint8_t rx_received = 0; // Флаг получения символа
// Обработчик прерывания USART1
void USART1_IRQHandler(void)
{
if (USART1->SR & USART_SR_RXNE) // Проверка флага RXNE
{
uint8_t received = USART1->DR; // Чтение принятого символа
if (received >= 32 && received <= 126) // Проверка на печатаемый символ
{
last_printable_char = received; // Сохранение символа
rx_received = 1; // Установка флага получения
}
}
}
int main(void)
{
SystemClock_Config();
// Настройка GPIOB
RCC->IOPENR |= RCC_IOPENR_GPIOBEN; // Включить тактирование GPIOB
GPIOB->MODER &= ~GPIO_MODER_MODE0; // Сбросить режим
GPIOB->MODER |= GPIO_MODER_MODE0_0; // Выход
GPIOB->OTYPER &= ~GPIO_OTYPER_OT0; // Push-Pull
TIM1_PWM_Init();
UART_Init();
while (1)
{
GPIOB->ODR ^= GPIO_ODR_OD0; // Переключить пин
UART_Transmit(last_printable_char); // Отправить последний печатаемый символ или '#'
Delay(500000); // Задержка
}
}

Ну а с ардуино я пошутил — не собираюсь я писать ардуино фреймворк, зачем мне это? Может кто напишет, а может и уже где-то лежит написанный. А для большинства народа любая плата с микропроцессором — это ардуино, детали никому не интересны.
Нафига козе баян — вообще-то я хотел переделать газонокосилку, простую тупую газонокосилку, которая ездит как попало по газону, ограниченному проводом-периметром.
Со временем — кстати, не таким и большим, ей чуть больше 5 лет, у нее разъемы пришли в состояние, как было когда-то у советских ЭВМ — попинаешь платы, пошевелишь разъемы — она дальше работает. Подозреваю, связь самая прямая — ее произвела фирма из Израиля, а там сплошь наши люди. Вот и хотел выкинуть все провода и разъемы, оставить только питание и прокинуть LIN между модулями — CAN для нее все-таки жирновато.

А тут такой контроллер — дешевле, чем провода. И чего уж от этой газонокосилки никак не ожидал — LiFePO4 аккумуляторы за это время практически не сдулись, хотя по современным меркам их изначальная емкость не очень большая.

Но что делать с ее навигацией — так пока и не решил. Супер-пупер GPS с сантиметровой точность и дорогая, и под деревьями работать не будет. Пылесосные лидары солнечный свет плохо переносят — косить по ночам? Ну, да — «в самый жуткий час мы волшебную косим трын-траву!». Думал про фазовую радиопеленгацию — за небольшие деньги точность получается метр и хуже. С оптикой свои проблемы — но, наверно, самые простые. Короче, буду дальше думать и косить бензиновой жужжалкой. А то заедет на соседский газон — проблем не оберешься. Сосед — какой-то косильный маньяк, и если его лишить удовольствия косить свой газон — я даже боюсь думать о последствиях.
На сем позвольте откланяться, надеюсь кому-то пригодится.
+97 |
5446
145
|
+64 |
5827
99
|
а для широкого круга обывателй, можно рассказать, как из этого градусник «запилить»?
А, то не совсем понятно зачем все это нужно.
(Лайкнул, конечно, добра автору и творческих успехов, от всей души желаю)
Нужно просто завести армию фанатов!
Пример:
Эмоциональная поклонница сжимает в руках пучок травы, по которому прошёл Ринго Старр — ударник группы The Beatles. Аэропорт Лос-Анджелеса, США, 1964 год.
По которому ты ходила ..."
С годами статьи, их использующие, превращаются в
Но проще и правильнее на гитхаб, конечно.
Обойти ограничение символов картинками.
Между двумя точками у меня на расстоянии в 20 метров средний разброс получается порядка 10см… проверить на большем количестве точек пока не могу — еще не приехали еспшки.
если вы определяетесь по RSSI, то при наличии препятствий в виде деревьев и прочего — точности не будет. Если футбольное поле — то да, это вариант.
башку не грей
Ничего страшного, бывает
А читать все это Вас вроде никто и не заставлял.;)
— Я полагаю, это стоит скурить.
тут какбы именно вопрос в контроллере весьма удовлетворяющем многим потребностям за 0,1.
плюсом енергопотребление относительно всяких есп+
Зачем искать проблемы и героически их преодолевать.
У тех кто активен может часто возникать куча задач решаемых простейшим контроллером.
Например не так давно собирал девайсину, ВСЯ работа которой была отсчитать 5минут и активировать выход. Условный блинк )
начал собирать на 555 и рассыпухе, возникли нюансы, плюнул поставил подобный проц.
Вышло проще и дешевле.
зачем мне вместо десятков модулей по 0,1 покупать десятки модулей по 2-5-10$/шт просто потому что так привычно комуто в самоделках и он не видит смысла экономить ?? А мне ради этого нужно бесполезно потратить сотни вечнозеленых…
если вам цена вопрос второстепенный, предлагаю процент с каждой вашей диайваины присылать мне. вам же все-равно, а мне будет приятно )
Ну и у кого как, но по идее лишний час, потраченный на копание в какой-то фигне будет стоить минимум как штук 20-30 готовых понятных платок. А этот час вы на копание с новым непонятным контроллером потратите гарантированно.
и я уверен он их применит )
нет, я не планировал покупать, у меня пока есть запас других простых распаяных. до их израсходования или специфичности задачи мне неособо интересен выбор иных.
если б я был в процесе выбора, скорее купил бы борды из топика. «зачем платить больше»©
Но, и признаю. при цене в районе бакса-полутора 2040 тоже наверное имеет неплохие шансы занять место в самоделках и у меня. мои 5 баксом — быстрый поиск который в большинстве локальное показал.
ибо есп такая себе штука. весьма специфическая. хотя и с несомненными плюсами.
raspberry по баксу на али в bundle deals почти всегда висят. А есп я и сам недолюбливаю, уж больно странностей много — те же ноги в момент инициализации.
и, да. Это действительно, с моей точки зрения все СИЛЬНО меняет.
В такой ситуации я тоже выберу борду 2040 за бакс-полтора, вообще не обсуждается.
Читал недавно статью про какие-то китайские МК (не помню), там автор убил его программно — он предположил, что в маленьком корпусе выводы просто соединены вместе, поэтому если на один вывести 0, а на другой 1, будет КЗ, которое и убьет контроллер. Вот вы хотели бы в свои проекты такое ставить? Я что-то не очень.
Соединение выводов — это не «глючность», это особенность. Например, можно использовать для повышения тока вывода. Или если они не выведены… Не знаю, не использовать совсем? Часто у вас в проектах возникает нужда подать случайный логический уровень на неиспользуемые выводы? У меня чет за десяток лет ни разу.
Ну, почти…
Или «засеять» газон «светлячками» с датчиками движения и питанием от микроаккума с солнечной батареей — прошелся — а за тобой шлейф света…
Или имеется ввиду другая тинька, например 85?
Во втором случае — уже активно используется в нише с низким вхождением типа Ардуины.
А с процессорами у меня очень хорошо — еще бы где силы найти все MSP430 выбросить. В свое время их можно было бесплатными образцами набрать — но не более 50 штук и 6 разновидностей за сутки :). У меня из столько, конечно, нет — но место все равно занимают — каждой твари по паре — и выбросить жалко.
Но времена халявы кончились — а много лет назад за домашние проекты платить вообще не приходилось, сплошные образцы.
С другой стороны, начинать проекты и бросать их незавершенными — тоже вполне нормальная практика, она хорошо вписывается в философию радиолюбительства.
Ну и про игры. Как пример Silver Snipers — шведская киберспортивная команда по Counter-Strike, состоящая из игроков в возрасте от 62 до 81 года. Не утверждаю, что это правильно или нет. Просто факт. А что вы все время по себе остальных оцениваете?
И отказываться от мечты не стоит. Ну если природа не обделила многозадачностью.
ПыСы Картинку уже публиковал, чуть ниже )))
Мы не хотим что бы в наши двери стучала радость, мы не хотим что бы в наши двери стучало горе, мы просто будем спокойно делать нужное и полезное дело.
ПыСы У Автора (именно с большой буквы) времени несмотря на возраст вполне хватает, возможно он просто не суетится.
И как там:
-Не бери больше чем можешь унести…
Право не стоит судить по себе, ресурс штука индивидуальная. Вы себя еще с Пушкиным сравните, по количеству сделанного и отпущенного времени. Это так, пример для понимания
А то как тут:
Так-то оно конечно да, отбросить лишнее, сконцентрироваться на главном… но слишком уж это философская тема получается, слишком уж много может быть мнений… Например такое: в 2х метрах под землей нет разницы между наличием или отсутствием микроконтроллера. А потому и нет разницы, главный он или нет.
А -а, прочитал, там так и сделано! :)
Opencv?
Задача решается гораздо проще — без всяких танцев с бубном вокруг питона:
или На выходе получается контейнер, который читается простым WinRAR.
Что касается загрузчика — с PY32F040 он будет работать?
Оно же через CMSIS-DAP USB и наверно, отлаживаться сможет?
Такие мк есть и " осминожки" и у них прикольно, несколько пинов подключены к одному выводу!
При особо сильном желании можно на ось вращения катушки повешать энкодер номер раз, а на сам кол экнодер номер два, таким образом получив координату косилки в радиальной системе координат с началом отсчёта непосредственно в колу. После чего можно начать выстригать фигурные фигуры, квадрат, треугольник, звезду давида…
А ещё вместо косилки можно завести козу. С ней способ с колом работает ещё лучше — вместо поводка хватит обычной верёвки
Смотри: www.yaplakal.com/forum2/topic1837959.html
Например это в STM32 вообще открытом текстом это говорят. Дословно: «Вы можете использовать все не документируемые блоки, но в связи с тем, что тестирование они не проходили вы делаете это на свой страх и риск».
Так что да-же на младших чипах можно найти полный объём флеша, памяти и всех функциональных блоков. Но без гарантии. Физически отключаются только блоки аппаратного шифрования. Это связано с экспортными ограничениями США.
И об stm32 в целом, время идет и китайцы предлагают целые линейки МК есть из чего выбирать. Вот прямо сейчас держу в руках gd32l233 (это как бы stm32l4, только с ядром m23), который сходил и купил за 80р локально для одного своего проекта, по критерию что есть в магазине рядом подешевле в корпусе на 64 вывода и небольшим потреблением, и у него заявлено и шифрование DES, TDES, AES (128, 192, 256) просто раз упоминулось шифрование. Сейчас такой выбор благодаря китайцам, я stm32 в руки уже несколько лет не брал.
Гугл пишет, что есть способ интегрировать компилятор Кайла и в Visual Studio, и в халявную Visual Code
Для последней:
https://marketplace.visualstudio.com/items?itemName=Arm.keil-studio-pack
Про навигацию. А что если встроить в косилку компас, плюс оптический радар, в котором модулировать лазер какой-нибудь частотой, чтобы синхронным детектированием воспользоваться.
Ну и по углам участка поставить колышки с катафотами-отражателями. С точки зрения качества, вероятно, не самый лучший метод — но для себя может сработать.
Второй вариант поставить вращающуюся камеру/массив камер на 360 градусов/камеру с объективом на 360. А по углам участка маяки, например колы с чёрно-белыми кольцами ввиде штрихкода. Или ИК маяки с уникальным кодом вспышек на каждом.
Хотя тут ещё от размеров участка зависит конечно.
есть более профильные системы для этого nanoloc, UWB — и то уровень вхождения там нешуточный
Кстати, когда прочел, что автор пользует mail.ru в качестве лежалки файла и ещё жалится на него, то чтиво бросил и написал этот коммент. Лично мне всё стало ясно.
Потом периодически находишь, радуешься, что такой запасливый.
Потом находишь, но выбросить рука не подымается.
Потом, спустя пару десятков лет, всё же выкидываешь в процессе борьбы с хламом.
Самое обидное, когда приходится разбирать собственноручно сделанные устройства, за давностью лет ставшие ненужными.
Актуальный дешевый МК для РФ это CH32V003. Лоты с ним есть и на али, и локально представлен, в моем случае 14р в рознице в ближайшем магазине здесь и сейчас.