Сперва лёгкая прелюдия...
STMicroelectronics за последние 4-5 месяцев заставила мелких и средне сочных производителей микроэлектроники капитально "затянуть пояса". Не секрет что за это время оригинальные чипы капитально взлетели в цене, а последние пару месяцев чипов так вообще и не достать... Поэтому потянулись, в связи с дефицитом, к китайским аналогам.
Вот к примеру HK32F030C8T6 по заявлениям производителя (Шэньчжэнь Hangshun Chip Technology R&D Co., Ltd.) является полной копией своего с позволения сказать старшего брата STM32F030C8T6. Итак, давайте посмотрим...
Открываем техническую документацию...
Судя по описанию: "HK32F030x4 / HK32F030x6 / HK32F030x8. HK32F030 Перечисленные микросхемы разработаны компанией Shenzhen Haohan Tianji Processor Co., Ltd., дочерней компанией Shenzhen Hangshun Chip Technology Research Co., Ltd.".
И тут честно говоря я был в восторге! Во первых чипы могут работать в диапазоне питающего напряжения от 2,0 В до 5,5 В. Во вторых: максимальная тактовая частота ядра аж до 72 МГц!!! Прям сказка да и только!!! Убедитесь сами!!!:
Скрин тех. документа
Ах да. "Даташит" на китайском...
...читаем дальше!!!
Скрин тех. документа
Тут я добираюсь до функциональной схемы модуля генерации частот... и слегка недоумеваю...
...что такое RCC_CFGR4, у STM'ки его нет! Открываем библиотечный файл "stm32f0xx.h":
typedef struct
{
__IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */
__IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x04 */
__IO uint32_t CIR; /*!< RCC clock interrupt register, Address offset: 0x08 */
__IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x0C */
__IO uint32_t APB1RSTR; /*!< RCC APB1 peripheral reset register, Address offset: 0x10 */
__IO uint32_t AHBENR; /*!< RCC AHB peripheral clock register, Address offset: 0x14 */
__IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock enable register, Address offset: 0x18 */
__IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x1C */
__IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x20 */
__IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x24 */
__IO uint32_t AHBRSTR; /*!< RCC AHB peripheral reset register, Address offset: 0x28 */
__IO uint32_t CFGR2; /*!< RCC clock configuration register 2, Address offset: 0x2C */
__IO uint32_t CFGR3; /*!< RCC clock configuration register 3, Address offset: 0x30 */
__IO uint32_t CR2; /*!< RCC clock control register 2, Address offset: 0x34 */
} RCC_TypeDef;
Но сравнив данный модуль с оригинальным, я понимаю что на CFGR4 мне пофиг, куска которым управляет данный регистр у оригинального чипа нет:
...просмотрев весь документ я понимаю что информации о чипе в документе явно не достаточно, можно было бы предположить что в нем точно повторен STM32F030, однако тут я вспомнил про регистр CFGR4...
В итоге прорыв просторы интернета я таки нашел ссылку на неведомую библиотеку HK32F0_LibraryV1.0.3... и оказывается вон оно как:
typedef struct
{
__IO uint32_t CR; /*!< RCC clock control register, Address offset: 0x00 */
__IO uint32_t CFGR; /*!< RCC clock configuration register, Address offset: 0x04 */
__IO uint32_t CIR; /*!< RCC clock interrupt register, Address offset: 0x08 */
__IO uint32_t APB2RSTR; /*!< RCC APB2 peripheral reset register, Address offset: 0x0C */
__IO uint32_t APB1RSTR; /*!< RCC APB1 peripheral reset register, Address offset: 0x10 */
__IO uint32_t AHBENR; /*!< RCC AHB peripheral clock register, Address offset: 0x14 */
__IO uint32_t APB2ENR; /*!< RCC APB2 peripheral clock enable register, Address offset: 0x18 */
__IO uint32_t APB1ENR; /*!< RCC APB1 peripheral clock enable register, Address offset: 0x1C */
__IO uint32_t BDCR; /*!< RCC Backup domain control register, Address offset: 0x20 */
__IO uint32_t CSR; /*!< RCC clock control & status register, Address offset: 0x24 */
__IO uint32_t AHBRSTR; /*!< RCC AHB peripheral reset register, Address offset: 0x28 */
__IO uint32_t CFGR2; /*!< RCC clock configuration register 2, Address offset: 0x2C */
__IO uint32_t CFGR3; /*!< RCC clock configuration register 3, Address offset: 0x30 */
__IO uint32_t CR2; /*!< RCC clock control register 2, Address offset: 0x34 */
uint32_t RESERVED[42]; /*!< Reserved, Address offset: 0x38-dc */
__IO uint32_t HSECTL; /*!< RCC clock HSE control, Address offset: 0xe0 */
uint32_t RESERVED1; /*!< Reserved, Address offset: 0xe4 */
__IO uint32_t CFGR4; /*!< RCC clock control register 4, Address offset: 0xe8 */
__IO uint32_t AHBENR2; /*!< RCC clock configuration ahb, Address offset: 0xec */
} RCC_TypeDef;
...интересно китайцы специально отступили (uint32_t RESERVED[42]; /*!< Reserved) чтоб в лоб не получилось найти доп регистры или это фишка реализации?
Посмотрев библиотеку, увидел что по регистрам периферии прям одно и то же с оригиналом! Ну дык и давайте шить прошивку!!! (...достаю из закромов родины забавную программную реализацию USB для STM32F0xx...) Зашиваю, и получаю:
Не понял, беру STM32F030, зашиваю:
Но я ж не просто так, меня попросили помочь переехать с STM на HK и чтоб программное USB работало... и тут мне слегка не по себе стало... кто реализовывал программный USB на ARM, тому наверное сразу вспомнится цитата из документашки: "NOP
performs no operation and is not guaranteed to be time consuming. The processor might remove it from the pipeline before it reaches the execution stage. " Иными словами NOP пользуется для выравнивания команд и с конвейера его проц может "выплевывать" не задерживаясь на выполнение пустой операции... но я не совсем про это говорю, как ровнять тайминги если неизвестно сколько тактов тратит каждая инструкция?
Этож целая экспедиция переписать тактозависимые функции приема и передачи для ядра ARM!!!
Тут абсолютно понятно что HK явно не является "клоном" по количеству тактов на инструкции. Однако где найти документацию по ассемблерным командам и самое главное по тактам затрачиваемым на выполнение.
...
И тут нам поможет System Tick Timer! Настраиваем его, отключаем прерывания, не включаем Watchdog (чтоб проц не отвлекался на что-то иное кроме выполнения). Переходим в отладчик, выполняем инструкции по шагам и сравниваем сколько успел насчитать таймер во время выполнения инструкции. Берем два абсолютно одинаковых куска кода и сравниваем кол-во тактов на инструкцию:
Оригинал (STM32F030):
asm_usb_tx
PUSH {R4-R7,LR} ;10
LDR R3,DataTable+0x08 ;6
LDR R2,DataTable+0x14 ;6
STR R2,[R3, #+0x18] ;5
LDR R4,[R3, #+0] ;5
MOVS R5,#+5 ;4
LSLS R5,R5,#+22 ;4
ORRS R5,R5,R4 ;4
STR R5,[R3, #+0] ;5
MOVS R7,#+0 ;4
ADDS R1,R1,#+1 ;4
ut_0
LDRB R5,[R0,#+0] ;5
NOP ;4
NOP ;4
SUBS R1,R1,#+1 ;4
BEQ ut_2 ;4
MOVS R6,#+8 ;4
LDRB R4,[R0,#+0] ;5
ADDS R0,R0,#+1 ;4
"Китайское" (HK32F030):
asm_usb_tx
PUSH {R4-R7,LR} ;10
LDR R3,DataTable+0x08 ;6
LDR R2,DataTable+0x14 ;6
STR R2,[R3, #+0x18] ;9 GPIOA->BSRR
LDR R4,[R3, #+0] ;8 GPIOA->MODER
MOVS R5,#+5 ;4
LSLS R5,R5,#+22 ;4
ORRS R5,R5,R4 ;4
STR R5,[R3, #+0] ;6 GPIOA->MODER
MOVS R7,#+0 ;4
ADDS R1,R1,#+1 ;4
ut_0
LDRB R5,[R0,#+0] ;9
NOP ;4
NOP ;4
SUBS R1,R1,#+1 ;4
BEQ ut_2 ;4->8
MOVS R6,#+8 ;4
LDRB R4,[R0,#+0] ;9 Load transmited Byte
ADDS R0,R0,#+1 ;6 Increment TX Buffer Pointer
Вот блин бабушка и приехали... Инструкции LDR/STR выполняются в полтора раза дольше и те же инструкции тратят на один такт больше если используем Immediate Value... и мало того, - по сути идентичные инструкции выполняются разное кол-во тактов, и ещё опа, - времени банально не хватает на текущую реализацию... Пусть, ядро может работать на 72 MHz, пробуем.
Меняем множитель с 6 на 9. И в теории, времени теперь навалом, а по факту впритык, потому что проц пожирает массу времени на часто используемые команды, подгоняем реализацию под нужные временные интервалы антинаучным методом "подбора" и "научного тыка". И теперь оно работает.
Резюме:
...HK это не STM...
Отсутствие нормальной документации, если разработчики подумали что фиг с ним, есть стандартный мануал от STM, там читайте, то хоть бы скопипастили даташит, не понятно даже где адреса GPIO. Текущий документ выглядит так как будто начали писать, потом устали.
Микроконтроллер HK по периферии, похож на STM, лично я проверил ADC/TIM/USART/IWDG/GPIO, - работают схоже, но документации точной нет, поэтому угадайка где что вылезет.
Не предсказуемое время выполнения кода и даже заявленное преимущество в 72MHz собственно полностью "закрашивается" временем выполнения операций условного перехода и загрузкой/выгрузкой регистров. "Слегка" промахнулись с "фетчем" инструкций и данных. При переезде на этот проц, если вы работали на STM'ке на 48MHz, задирайте на HK частоту до 72, так как среднее время выполнения "по поликлинике" на 48MHz для STM приблизительно будет равно для HK на 72MHz, и придется переписывать все таймеры ибо 72 != 48, а таймеры, неожиданно прям, тикают в этих кристаллах одинаково.
За сим разрешите откланяться.
Отличного дня! Благ, доброты и удачи.
gleb_l
Все поделия из Поднебесной такие — от транзисторов до космических аппаратов. Просто слегка другие. Если вспомнить отечественную элементную базу времен СССР — она тоже не везде и всегда была цельнотянутая — были и (разумные) отклонения. Главное — не пытаться соединить Европу и Азию в одной системе — это невозможно в гуманитарном контексте, и практически так же невозможно в техическом. Но если просто жить в контексте Китая (или, скажем СССР) — то можно вполне успешно запустить ракету на Марс, не парясь насчет таймингов и карты адресов регистров периферии — просто не зная, что она может быть другая ;)
ramfactory Автор
Тут я совершенно согласен! Взять хотя бы КР580ВВ55 (который 8255 от Intel). Просто да, слегка другие ;)
sfrolov
А что там другое в ВВ55?