/* * This file is part of the Black Magic Debug project. * * Copyright (C) 2011 Black Sphere Technologies Ltd. * Written by Gareth McMullin * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ /* This file implements the platform specific functions for the STM32 * implementation. */ #include #include #include #include #include #include #include #include #include "platform.h" #include "jtag_scan.h" #include #include uint8_t running_status; volatile uint32_t timeout_counter; jmp_buf fatal_error_jmpbuf; static void morse_update(void); static void adc_init(void); /* Pins PB[7:5] are used to detect hardware revision. * 000 - Original production build. * 001 - Mini production build. */ int platform_hwversion(void) { static int hwversion = -1; if (hwversion == -1) { gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO7 | GPIO6 | GPIO5); gpio_clear(GPIOB, GPIO7 | GPIO6 | GPIO5); hwversion = gpio_get(GPIOB, GPIO7 | GPIO6 | GPIO5) >> 5; } return hwversion; } int platform_init(void) { rcc_clock_setup_in_hse_8mhz_out_72mhz(); /* Enable peripherals */ rcc_peripheral_enable_clock(&RCC_APB1ENR, RCC_APB1ENR_USBEN); rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPAEN); rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_IOPBEN); rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_AFIOEN); rcc_peripheral_enable_clock(&RCC_AHBENR, RCC_AHBENR_CRCEN); /* Setup GPIO ports */ gpio_clear(USB_PU_PORT, USB_PU_PIN); gpio_set_mode(USB_PU_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USB_PU_PIN); gpio_set_mode(JTAG_PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, TMS_PIN | TCK_PIN | TDI_PIN); /* This needs some fixing... */ /* Toggle required to sort out line drivers... */ gpio_port_write(GPIOA, 0x8100); gpio_port_write(GPIOB, 0x2000); gpio_port_write(GPIOA, 0x8180); gpio_port_write(GPIOB, 0x2002); gpio_set_mode(LED_PORT, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, LED_UART | LED_IDLE_RUN | LED_ERROR); /* FIXME: This pin in intended to be input, but the TXS0108 fails * to release the device from reset if this floats. */ gpio_set_mode(GPIOA, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO7); /* Setup heartbeat timer */ systick_set_clocksource(STK_CTRL_CLKSOURCE_AHB_DIV8); systick_set_reload(900000); /* Interrupt us at 10 Hz */ SCB_SHPR(11) &= ~((15 << 4) & 0xff); SCB_SHPR(11) |= ((14 << 4) & 0xff); systick_interrupt_enable(); systick_counter_enable(); if (platform_hwversion() > 0) { adc_init(); } else { gpio_clear(GPIOB, GPIO0); gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO0); } SCB_VTOR = 0x2000; // Relocate interrupt vector table here cdcacm_init(); usbuart_init(); jtag_scan(NULL); return 0; } void platform_delay(uint32_t delay) { timeout_counter = delay; while(timeout_counter); } void sys_tick_handler(void) { if(running_status) gpio_toggle(LED_PORT, LED_IDLE_RUN); if(timeout_counter) timeout_counter--; morse_update(); } /* Morse code patterns and lengths */ static const struct { uint16_t code; uint8_t bits; } morse_letter[] = { { 0b00011101, 8}, // 'A' .- { 0b000101010111, 12}, // 'B' -... { 0b00010111010111, 14}, // 'C' -.-. { 0b0001010111, 10}, // 'D' -.. { 0b0001, 4}, // 'E' . { 0b000101110101, 12}, // 'F' ..-. { 0b000101110111, 12}, // 'G' --. { 0b0001010101, 10}, // 'H' .... { 0b000101, 6}, // 'I' .. {0b0001110111011101, 16}, // 'J' .--- { 0b000111010111, 12}, // 'K' -.- { 0b000101011101, 12}, // 'L' .-.. { 0b0001110111, 10}, // 'M' -- { 0b00010111, 8}, // 'N' -. { 0b00011101110111, 14}, // 'O' --- { 0b00010111011101, 14}, // 'P' .--. {0b0001110101110111, 16}, // 'Q' --.- { 0b0001011101, 10}, // 'R' .-. { 0b00010101, 8}, // 'S' ... { 0b000111, 6}, // 'T' - { 0b0001110101, 10}, // 'U' ..- { 0b000111010101, 12}, // 'V' ...- { 0b000111011101, 12}, // 'W' .-- { 0b00011101010111, 14}, // 'X' -..- {0b0001110111010111, 16}, // 'Y' -.-- { 0b00010101110111, 14}, // 'Z' --.. }; const char *morse_msg; static const char * volatile morse_ptr; static char morse_repeat; void morse(const char *msg, char repeat) { morse_msg = morse_ptr = msg; morse_repeat = repeat; SET_ERROR_STATE(0); } static void morse_update(void) { static uint16_t code; static uint8_t bits; if(!morse_ptr) return; if(!bits) { char c = *morse_ptr++; if(!c) { if(morse_repeat) { morse_ptr = morse_msg; c = *morse_ptr++; } else { morse_ptr = 0; return; } } if((c >= 'A') && (c <= 'Z')) { c -= 'A'; code = morse_letter[c].code; bits = morse_letter[c].bits; } else { code = 0; bits = 4; } } SET_ERROR_STATE(code & 1); code >>= 1; bits--; } static void adc_init(void) { rcc_peripheral_enable_clock(&RCC_APB2ENR, RCC_APB2ENR_ADC1EN); gpio_set_mode(GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG, GPIO0); adc_off(ADC1); adc_disable_scan_mode(ADC1); adc_set_single_conversion_mode(ADC1); adc_disable_external_trigger_regular(ADC1); adc_set_right_aligned(ADC1); adc_set_sample_time_on_all_channels(ADC1, ADC_SMPR_SMP_28DOT5CYC); adc_power_on(ADC1); /* Wait for ADC starting up. */ for (int i = 0; i < 800000; i++) /* Wait a bit. */ __asm__("nop"); adc_reset_calibration(ADC1); adc_calibration(ADC1); } const char *platform_target_voltage(void) { if (platform_hwversion() == 0) return gpio_get(GPIOB, GPIO0) ? "OK" : "ABSENT!"; static char ret[] = "0.0V"; const u8 channel = 8; adc_set_regular_sequence(ADC1, 1, (u8*)&channel); adc_start_conversion_direct(ADC1); /* Wait for end of conversion. */ while (!adc_eoc(ADC1)); u32 val = adc_read_regular(ADC1) * 99; /* 0-4095 */ ret[0] = '0' + val / 81910; ret[2] = '0' + (val / 8191) % 10; return ret; } void assert_boot_pin(void) { gpio_set_mode(GPIOB, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO12); gpio_clear(GPIOB, GPIO12); } void exti15_10_isr(void) { if (gpio_get(USB_VBUS_PORT, USB_VBUS_PIN)) { /* Drive pull-up high if VBUS connected */ gpio_set_mode(USB_PU_PORT, GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, USB_PU_PIN); } else { /* Allow pull-up to float if VBUS disconnected */ gpio_set_mode(USB_PU_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, USB_PU_PIN); } exti_reset_request(USB_VBUS_PIN); } void setup_vbus_irq(void) { nvic_set_priority(USB_VBUS_IRQ, IRQ_PRI_USB_VBUS); nvic_enable_irq(USB_VBUS_IRQ); gpio_set(USB_VBUS_PORT, USB_VBUS_PIN); gpio_set(USB_PU_PORT, USB_PU_PIN); gpio_set_mode(USB_VBUS_PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USB_VBUS_PIN); /* Configure EXTI for USB VBUS monitor */ exti_select_source(USB_VBUS_PIN, USB_VBUS_PORT); exti_set_trigger(USB_VBUS_PIN, EXTI_TRIGGER_BOTH); exti_enable_request(USB_VBUS_PIN); exti15_10_isr(); }