; ============================================================================== ; Title: USB to Serial (RS232) converter supporting the USB defined baudrates. ; ; Author: Rob Jansen, Copyright (c) 2020..2021, all rights reserved. ; ; Revision: ; 2020-02-15 : Initial version. ; 2021-10-23 : V2.0. Using the baudrate settings defined by the USB connection. ; Removed baudrate jumpers and added an extra led in order to show ; to which device data is flowing. More baudrates are supported. ; ; Compiler: 2.5r5 ; ; Description: This program exchanges data between the USB and the serial ; (RS232) interface. It support baudrates from 110 baud to 230400 ; baud using the settings defined by the USB connection. ; Serial data is transmitted as 8 bits, no parity, 1 stopbit. ; When configuring the USB via a terminal program, make sure to ; enable flow control via RTS/CTS as to configure the USB device. ; ; Sources: Jallib sample program 16f1455_usb_serial, ; see: http://justanotherlanguage.org/ ; Intial version can be found at Instructables, ; see: https://www.instructables.com/USB-to-Serial-TTL/ ; ;=============================================================================== include 16f1455 pragma target clock 48_000_000 ; Settings for external 12 MHz crystal and system clock 48 MHz. pragma target OSC HS ; External crystal pragma target CLKOUTEN ENABLED ; CLKOUT function is enabled pragma target PLLMULT N4X ; PLL Multipler Selection Bit, 4x Output Frequency Selected ; Settings for internal clock and system clock 48 MHz. ; pragma target OSC INTOSC_NOCLKOUT ; Internal clock ; pragma target CLKOUTEN DISABLED ; CLKOUT function is disabled. I/O or oscillator function on the CLKOUT pin ; pragma target PLLMULT N3X ; PLL Multipler Selection Bit, 3x Output Frequency Selected ; Other fuses pragma target CPUDIV P1 ; NO CPU system divide pragma target USBLSCLK F48MHZ ; System clock expects 48 MHz, FS/LS USB CLKENs divide-by is set to 8. pragma target PLLEN ENABLED ; 3x or 4x PLL Enabled pragma target FCMEN DISABLED ; Fail-Safe Clock Monitor is disabled pragma target WRT DISABLED ; Write protection off pragma target STVR ENABLED ; Stack Overflow or Underflow will cause a Reset pragma target LPBOR DISABLED ; Low-Power BOR is disabled pragma target IESO DISABLED ; Internal/External Switchover Mode is disabled pragma target PWRTE DISABLED ; power up timer pragma target BROWNOUT DISABLED ; no brownout detection pragma target WDT DISABLED ; Watchdog disabled pragma target MCLR EXTERNAL ; External reset pragma target LVP ENABLED ; allow low-voltage programming pragma target VOLTAGE MAXIMUM ; brown out voltage pragma target CP DISABLED ; Program memory code protection is disabled OSCCON = 0b1111_1100 ; Select PLL,3x, 16MHz internal oscillator ; Enable weak pull-up for port a and and set port c to output just to ; have no floating input pins. OPTION_REG_WPUEN = FALSE ; Enable weak pull-up for port a. WPUA = 0b0011_1111 ; Weak-pull up for all port a bits. TRISC = 0b0010_0000 ; Port c output but not C5. enable_digital_io() ; All pins digital io instead of analog. ; Give the hardware some time to stabilize. Especially the HEF needs this time. _usec_delay(250_000) ; Pin aliases. alias configured_led is pin_c0 ; Pin 10. pin_c0_direction = output alias green_led is pin_c1 ; Pin 9. pin_c1_direction = output alias red_led is pin_c2 ; Pin 8. pin_c2_direction = output ; Set USART port direction pin_c4_direction = output ; Pin 6, TX output pin_c5_direction = input ; Pin 5, RX input ; Include and initialize the USB serial driver. include usb_serial include print ; From the usb_serial we can get the cdc line coding to initialze the USART ; from the cdc_line_coding array. The definition is as follows: ; var volatile byte cdc_line_coding[7] ; var volatile dword cdc_line_coding_dte_rate at cdc_line_coding[0] ; var volatile byte cdc_line_coding_stop_bits at cdc_line_coding[4] ; var volatile byte cdc_line_coding_parity at cdc_line_coding[5] ; var volatile byte cdc_line_coding_data_bits at cdc_line_coding[6] ; We only use the cdc_line_coding_dte_rate and ignore the other settings. ; List of baudrates supported. See page 262 of the datasheet. ; Formula for baudrate calclation for 8 byte asynchronous mode ; with for sync = FALSE and brg16 = TRUE and brgh = FALSE: ; brg = target_clock/(baudrate * 16) - 1 const dword SUPPORTED_BAUDRATES[] = { 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 115200, 230400 } ; ========================= Constants and Variables ========================== const word USART_NO_BAUDRATE = 0 const bit USART_TX_FULL = FALSE const bit USART_RX_AVAILABLE = TRUE const word LED_ON_TIME = 10_000 ; Just a random value to keep the LED on. const bit LED_ON = TRUE const bit LED_OFF = FALSE var dword current_baudrate, baudrate_calculation var word red_led_timer, green_led_timer var byte character var bit configured ; ========================= Functions and Procedures ========================== ; Disable the usart. procedure usart_disable() is ; Disable transmitter and receiver. TXSTA_TXEN = FALSE RCSTA_CREN = FALSE end procedure ; Enable the usart. procedure usart_enable() is ; Enable transmitter and receiver. TXSTA_TXEN = TRUE RCSTA_CREN = TRUE end procedure ; Initialize the USART with the given baudrate value. The USART is enabled. procedure usart_init(word in baudrate_value) Is usart_disable() ; Use 16 bit baudrate generator and set baudrate BAUDCON = 0b0000_1000 SPBRG = baudrate_value ;Initialise transmitter, 8 bits, asynchronous mode, low speed. TXSTA = 0b0000_0000 ; Initialise receiver and serial port, 8 bits RCSTA = 0b1000_0000 usart_enable() end procedure ; Check if usart data was received and return TRUE when data is available ; and return the data. In case of errors clear the error flags. function usart_serial_read(byte out data) return bit Is var bit data_available = FALSE if RCSTA_FERR then ; Framing error, re-enable receiver. RCSTA_SPEN = FALSE RCSTA_SPEN = TRUE elsif RCSTA_OERR then ; Overflow error. Reset receiver otherwise no characters are received. RCSTA_CREN = FALSE RCSTA_CREN = TRUE elsif (PIR1_RCIF == USART_RX_AVAILABLE) then ; All seems well. Return TRUE if a character is available. data = RCREG data_available = TRUE end if return data_available end function ; Pseudo variable for writing a byte to the USART. Blocking procedure. procedure usart_serial_data'put(byte in data) is ; Check if Transmit buffer empty, if not wait for it to empty. while (TXSTA_TRMT == USART_TX_FULL) loop ; Wait until TX buffer is empty end Loop TXREG = data end procedure ; Check if the given baudrate is supported and return TRUE if so. function supported_baudrate(dword in baudrate) return bit is var byte index var bit found = FALSE for count(SUPPORTED_BAUDRATES) using index loop if (SUPPORTED_BAUDRATES[index] == baudrate) then found = TRUE end if end loop return found end function ; ========================= Main program starts here ========================== ; Initialize the usb driver and some global variables and pins. usb_serial_init() current_baudrate = USART_NO_BAUDRATE configured = FALSE red_led_timer = 0 green_led_timer = 0 ; Switch LEDs off. red_led = LED_OFF green_led = LED_OFF configured_led = LED_OFF forever loop ; Poll the USB ISR function on a regular base, in order to serve the USB requests. usb_serial_flush() ; Check if USB device has been configured by the Host. Check if the ; baudrate is correct and if so check if it needs to be (re-)set. if (usb_cdc_line_status() != 0x00) then ; We are configured, check if the baudrate is valid. if supported_baudrate(cdc_line_coding_dte_rate) then ; Only (re-)set the baudrate if it is changed. if (current_baudrate != cdc_line_coding_dte_rate) then current_baudrate = cdc_line_coding_dte_rate baudrate_calculation = (target_clock/(current_baudrate * 16)) - 1 usart_init(word(baudrate_calculation)) configured = TRUE end if else ; Not supported baudrate, disable USART. usart_disable() current_baudrate = USART_NO_BAUDRATE configured = FALSE end if else configured = FALSE end if ; Transfer the data between the USB and the USART if the device is configured. if configured then configured_led = LED_ON ; Check for received charactaer from the USB and if present copy it to the USART. if usb_serial_read(character) then usart_serial_data = character green_led = LED_ON green_led_timer = 0 end if ; Check for received charactaer from the UART and if present copy it to the USB. if usart_serial_read(character) then usb_serial_data = character red_led = LED_ON red_led_timer = 0 end if ; The timer is used to keep the LEDs on for some time. Note that the ; timing is not exact since there is no real reference time but the precise ; on time of the LED is not that relevant. if (red_led_timer == LED_ON_TIME) then red_led = LED_OFF else red_led_timer = red_led_timer + 1 end if if (green_led_timer == LED_ON_TIME) then green_led = LED_OFF else green_led_timer = green_led_timer + 1 end if else configured_led = LED_OFF end if end loop