-- Título: USART hardware control -- Autor: David Persi, Copyright (c) 2011..., Todos los derechos reservados. -- Adaptado por: -- Versión 0.1 -- -- -- -- Descripción: Control de la USART por hardware. -- Rutinas para enviar y recibir datos a traves del PIC-USART, ambos, sincrónicos y asincrónicos son soportados. -- La velocidad de transimisión (Baudrate) solo se puede establecer, a través de una constante, porque los registros de velocidad de transmisión -- se calculan en función de esta unidad. La velocidad de transmisión se calcula, a partir de la bandera de velocidad de transmisión de alta, -- lo que asegurará la mayor precisión posible. -- -- -------------------------------------------------------------------------------------------------------------------------------------------- -- GUÍA DE USO. -- ------------ -- -- 01) Definir const byte modo_usart = 0xVV -- Donde 0xVV: -- 0x01 = Modo Asíncrono (Full duplex) 1 bit de arranque (start), 8 bit de datos, 1 bit de parada (stop). -- 0x02 = Modo Síncrono Maestro (Half duplex) 1 bit de arranque (start), 8 bit de datos, 1 bit de parada (stop). -- 0x03 = Modo Síncrono Esclavo (Half duplex) 1 bit de arranque (start), 8 bit de datos, 1 bit de parada (stop). -- -- 02) Definir const bit hab_int_rx = -- 1 = se habilita la interrupción. El usuario debe realizar la rutina para leer el valor recibido en la interrupción. -- 0 = se des-habilita la interrupción. El usuario debe chequear, permanentemente, la función para recibir datos. -- -- Definit const bit hab_int_tx = -- 1 = se habilita la interrupción. El usuario debe realizar la rutina para necesaria en la interrupción. -- 0 = se des-habilita la interrupción. No se provocará la interrupción cuando se finaliza el envío de datos. -- -- Nota: Recordar habilitar la interrupción general intcon_gie = 1 -- -- 03) definir const bit bit_par = -- -- 1 = Habilita el noveno bit para determinar la paridad. Con paridad. -- 0 = Des-habilita el noveno bit. Sin paridad. -- -- 04) Definir const serial_hard_baudrate = -- El valor, debe ser estandar para la comunicación serial rs232 -- -- 05) Incluir la librería --> include serial_hardware_sa -- -- 06) Llamar a la función serial_hard_ini() -- Iniciará todo el hardware rs232 del PIC dejándo habilitado la transmisión. -- -- 07) TRANSIMITIR CON PARIDAD PAR: -- ---------------------------- -- serial_tx(valor) -- Al cargar el valor a transmitir, la función calcula el bit de paridad par y lo carga al registro correspondiente. Luego transmite -- el valor de 8 bits (1 byte). -- valor es el dato a transmitir. valor = BYTE -- -- TRANSMITIR SIN PARIDAD: -- ----------------------- -- serial_tx(valor) -- valor es el dato a trasmitir. valor = BYTE -- -- 08) RECIBIR CON PARIDAD PAR: -- ------------------------ -- serial_rx(valor) -- Retorna con el valor recibido. El mismo devuelve true si la paridad es correcta, false en caso de un error en la recepción -- o cuando el bit de paridad no coincide con el dato recibido. La función calcula automáticamente, la paridad par del dato recibido. -- valor es el dato recibido. valor = BYTE -- -- RECIBIR SIN PARIDAD: -- -------------------- -- serial_rx(valor) -- Retorna con el valor recibido. El mismo devuelve true si se recibió correctamente, false en caso de un error en la recepción -- o cuando no se recibió ningún dato. -- valor es el dato recibido. valor = BYTE -- -- 09) Si se desea, se puede cortar y/o habilitar tanto sea la transmisión como la recepción. -- hab_usart() --> Habilita el módulo USART. -- des_usart() --> Desactiva el módulo USART. -- ------------------------------------------------------------------------------------------------------------------------------------------------- const bit asin = 0 -- Calcular velocidad -- procedure _calcular_velocidad(bit in asin) is const maximo_porcentual_error = 5 -- Máximo porcentaje de error 5% if asin == 1 then -- Modo asíncrono. if (defined(BAUDCON_BRG16) == true) then -- Uso del registro de 16 bit para baudrate. BAUDCON_BRG16 = true TXSTA_BRGH = true const usart_division = ((5 + ( ( 10 * target_clock ) / ( 4 * serial_hard_baudrate )) ) / 10 ) - 1 const baudrate_real = target_clock / 4 / ((usart_division & 0xffff) + 1) if (baudrate_real > serial_hard_baudrate) then if (100 * (baudrate_real - serial_hard_baudrate) / serial_hard_baudrate >= maximo_porcentual_error) then _error "Velocidad de transmision (baudrate) sincrono es demasiado grande." end if else if (100 * (serial_hard_baudrate - baudrate_real) / serial_hard_baudrate >= maximo_porcentual_error) then _error "Velocidad de transmision (baudrate) asincrono es demasiado grande." end if end if SPBRG = byte(usart_division) SPBRGH = byte(usart_division >> 8) else -- Uso del registroo de 8 bit para baudrate. -- SPBRG = ( Fosc / ( 4 * Baudrate ) ) -1 -- Primer intento para obtener una velocidad de transmisión más precisa -- para obtener el redondeo a la derecha (5 + 10*f(x)) /10 const usart_division = ((5 + ( ( 10 * target_clock ) / ( 16 * serial_hard_baudrate )) ) / 10 ) - 1 -- Caso especia si la división es 0, -- Testar si el error porcentual no es demasiado. if usart_division <= 0 then if (100 * (serial_hard_baudrate - (target_clock / 16) )) / serial_hard_baudrate >= maximo_porcentual_error then _error "Velocidad de transmision (baudrate) es demasiado alto." end if end if -- Si la división es suficiente pequeña, -- calculatar la división y setear alta velocidad (high-speed) const baudrate_real = target_clock / 16 / (usart_division + 1) if usart_division <= 255 then if (baudrate_real > serial_hard_baudrate) then if (100 * (baudrate_real - serial_hard_baudrate) / serial_hard_baudrate >= maximo_porcentual_error) then _error "Velocidad de transmision (baudrate) Asincrono es demasiado grande." end if else if (100 * (serial_hard_baudrate - baudrate_real) / serial_hard_baudrate >= maximo_porcentual_error) then _error "Velocidad de transmision (baudrate) es demasiado grande." end if end if if usart_division >= 0 then SPBRG = usart_division else SPBRG = 0 end if TXSTA_BRGH = true -- Probar el modo de velocidad lenta (low-speed) else const usart_division_bajo = ((((10 * target_clock) / ( 64 * serial_hard_baudrate )) + 5 ) / 10) - 1 -- Aquí nunca se dividirá por 0 -- pero el caso especia a considerar, -- si la velocidad de transmision es justo o demasiado bajo if (usart_division_bajo > 255) & (100 * ((target_clock / (64 * 256 )) - serial_hard_baudrate)) / serial_hard_baudrate < maximo_porcentual_error then SPBRG = 255 TXSTA_BRGH = false -- Ahora, calcular la division y setear el bit de alta o baja velocidad elsif usart_division_bajo <= 255 then if usart_division_bajo >= 0 then SPBRG = usart_division_bajo else SPBRG = 0 end if TXSTA_BRGH = false else _error "Velocidad de transimision (baudrate) Asincrono es demasiado bajo." end if end if end if else -- Modo Sincrono const usart_division_sync = ( target_clock / ( 4 * serial_hard_baudrate )) - 1 -- Caso especial si la división es 0 o negativo -- testear si la velocidad de transmision es demasiado alto. if usart_division_sync <= 0 then if (100 * (serial_hard_baudrate - (target_clock / 4 ) )) / serial_hard_baudrate >= maximo_porcentual_error then _error "Velocidad de transmision (baudrate) Sincrono es demasiado alto." end if end if -- Caso especia a considerar, -- si la velocidad de transmision es justo o demasiado alto if (usart_division_sync > 255) & (100 * ((target_clock / (4 * 256) ) - serial_hard_baudrate)) / serial_hard_baudrate < maximo_porcentual_error then SPBRG = 255 elsif usart_division_sync <= 255 then if SPBRG >= 0 then SPBRG = usart_division_sync else SPBRG = 0 end if else _error "Velocidad de transmision (baudrate) sincrono es demasiado bajo." end if end if end procedure -- ------------------------------------------------------------------------------------- -- Función para obtener el bit de paridad. -- --------------------------------------- function obtener_paridad(byte in valor) return bit is var byte valor_temp = valor var bit flag_paridad var byte aux ASSEMBLER local aux swapf valor_temp,W xorwf valor_temp,W movwf aux rrf aux,F rrf aux,F xorwf aux,F rrf aux,W xorwf aux,F bsf flag_paridad btfss aux,0 bcf flag_paridad nop end ASSEMBLER if flag_paridad == 1 then return true else return false end if end function procedure obtener_par(byte in valor) is if obtener_paridad(valor) == 1 then TXSTA_TX9D = 1 else TXSTA_TX9D = 0 end if end procedure -- ------------------------------------------------------------------ -- Función para obtener el bit de paridad para la recepción de datos. -- ------------------------------------------------------------------ function obtener_par_rx(byte in valor) return bit is var bit flag_paridad flag_paridad = obtener_paridad(valor) if RCSTA_RX9D == flag_paridad then return true else return false end if end function -- ------------------------------------------------------------------------------------- -- Inicializa el puerto serial, Calcula el registro para la velocidad de transmisión. -- -- ------------------------------------------------------------------------------------- procedure serial_ini() is var bit asin case modo_usart of 0x01: block asin = 1 _calcular_velocidad(asin) RCSTA_SPEN = 1 TXSTA_SYNC = 0 end block -- 0x02: block asin = 0 _calcular_velocidad(asin) txsta_sync = 1 rcsta_spen = 1 txsta_csrc = 1 end block -- 0x03: block asin = 0 _calcular_velocidad(asin) TXSTA_SYNC = 1 RCSTA_SPEN = 1 TXSTA_CSRC = 0 end block end case if bit_par == 1 then TXSTA_TX9 = 1 RCSTA_RX9 = 1 else TXSTA_TX9 = 0 RCSTA_RX9 = 0 end if TXSTA_TXEN = 1 -- Habilito TX RCSTA_CREN = 1 -- Habilito la recepción. if hab_int_rx == 1 then pie1_rcie = 1 intcon_peie = 1 else pie1_rcie = 0 end if if hab_int_tx == 1 then pie1_txie = 1 intcon_peie = 1 else pie1_txie = 0 end if end procedure -- Procedimiento para habilitar el módulo USART en forma manual. -- procedure hab_usart() is RCSTA_SPEN = 1 end procedure -- Procedimiento para desactivar el módulo USART en forma manual. -- procedure des_usart() is while !TXSTA_TRMT loop -- Esperamos la finalización de la transmisión en curso. end loop RCSTA_SPEN = false end procedure -- ------------------------------------------------------------------------------------- -- Procedimiento para enviar un dato por rs232. -- Si se utilizó la opción con bit paridad, antes de enviar el dato, obtiene el bit paridad par. -- procedure serial_tx(byte in valor) is if bit_par == 1 then obtener_par(valor) end if while ! PIR1_TXIF loop -- Esperamos que se termine la transmisión anterior. end loop TXREG = valor end procedure procedure serial_tx_word(word in valor) is var byte DX[2] at valor if bit_par == 1 then obtener_par(DX[1]) -- DX[1] end if while ! PIR1_TXIF loop -- Esperamos que se termine la transmisión anterior. end loop TXREG = DX[1] asm nop -- Para estabilizar. if bit_par == 1 then obtener_par(DX[0]) -- DX[0] end if while ! PIR1_TXIF loop -- Esperamos que se termine la transmisión anterior. end loop TXREG = DX[0] end procedure -- ------------------------------------------------------------------------------------ -- ------------------------------------------------------------------------------------ -- Función para recibir un dato por rs232. -- Si se utilizó la opción con bit paridad, antes de volver con el dato, chequea que sea correcto -- según el bit paridad. Para ello, la función devuelve 1 si el dato recibido es correcto -- y 0 cuando el dato recibido es erróneo. -- function _serial_rx(byte out valor) return bit is -- Solo uso interno. pragma inline -- test if byte available, and if so, -- get byte and transport to outer world if PIR1_RCIF == 1 then var byte valor_temp valor = RCREG valor_temp = RCREG PIR1_RCIF = false -- if bit_par == 1 then if obtener_par_rx(valor_temp) == 1 then return true else return false end if end if else return false ;result = false end if if RCSTA_OERR then RCSTA_CREN = false RCSTA_CREN = true end if return true end function -- ----------------------------------------------------------------------- -- serial_rx - Leer caracteres. -- function serial_rx(byte out valor) return bit is return _serial_rx(valor) end function -- -----------------------------------------------------------------------