├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── STM8_Lib ├── RAM_routines.c ├── RAM_routines.h ├── adc2.c ├── adc2.h ├── beeper.c ├── beeper.h ├── debug.h ├── error_codes.h ├── exint.c ├── exint.h ├── flash.c ├── flash.h ├── gpio.c ├── gpio.h ├── i2c.c ├── i2c.h ├── i2c_lcd.c ├── i2c_lcd.h ├── i2c_poti.c ├── i2c_poti.h ├── memory_access.h ├── misc.c ├── misc.h ├── putchar.c ├── putchar.h ├── spi.c ├── spi.h ├── stdint.h ├── stm8as.h ├── sw_fifo.h ├── timer1.c ├── timer1.h ├── timer2.c ├── timer2.h ├── timer3.c ├── timer3.h ├── timer4.c ├── timer4.h ├── uart1.c ├── uart1.h ├── uart3.c └── uart3.h ├── ax25.c ├── ax25.h ├── hc12pj.c ├── radio_config_Si4463.h ├── si446x.c └── si446x.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | 34 | # untested or unfinished projects (starting with 'x_') 35 | x_* 36 | 37 | # misc: documentation, ideas, scripts, etc. 38 | README.txt 39 | Ideen.txt 40 | *.command 41 | *.bat 42 | doxygen/html -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Andrew Litt 4 | Portions copyright (c) 2015 Georg Icking-Konert 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ####################### 2 | # SDCC Makefile for making a hexfile from all .C files in this directory. 3 | # Hexfile location is '.', other output files are generated in directory 'output'. 4 | ####################### 5 | # 6 | # From https://github.com/gicking/STM8_templates.git 7 | 8 | 9 | DEVICE=STM8S003 10 | 11 | OPTIMIZE = --opt-code-size 12 | 13 | # define compiler path (if not in PATH), and flags 14 | CC = sdcc 15 | CFLAGS = -mstm8 --std-sdcc99 -D$(DEVICE) -DUART1_FIFO 16 | LFLAGS = -mstm8 -lstm8 $(OPTIMIZE) --out-fmt-ihx 17 | 18 | # set target 19 | OBJDIR = $(DEVICE) 20 | TARGET = $(OBJDIR)/hc12pj.ihx 21 | 22 | # set project options (all .c files in folder) 23 | PRJ_ROOT = . 24 | PRJ_INC_DIR = $(PRJ_ROOT) 25 | PRJ_SRC_DIR = $(PRJ_ROOT) 26 | PRJ_SOURCE = $(wildcard *.c) 27 | PRJ_HEADER = $(wildcard *.h) 28 | PRJ_OBJECT = $(addprefix $(OBJDIR)/, $(PRJ_SOURCE:.c=.rel)) 29 | 30 | # set STM8_Lib options 31 | LIB_ROOT = ./STM8_Lib 32 | LIB_INC_DIR = $(LIB_ROOT) 33 | LIB_SRC_DIR = $(LIB_ROOT) 34 | LIB_SOURCE = gpio.c uart1.c putchar.c 35 | LIB_HEADER = $(patsubst %.c,%.h,$(LIB_SOURCE)) 36 | LIB_OBJECT := $(addprefix $(OBJDIR)/, $(LIB_SOURCE:.c=.rel)) 37 | 38 | # concat all include folders 39 | INCLUDE = -I$(PRJ_INC_DIR) -I$(LIB_INC_DIR) 40 | 41 | # concat all source directories 42 | VPATH=$(PRJ_SRC_DIR):$(LIB_SRC_DIR) 43 | 44 | 45 | .PHONY: clean all default objects 46 | 47 | .PRECIOUS: $(TARGET) $(PRJ_OBJECT) $(LIB_OBJECT) 48 | 49 | default: $(OBJDIR) $(TARGET) 50 | 51 | all: default 52 | 53 | # create output folder 54 | $(OBJDIR): 55 | mkdir -p $(OBJDIR) 56 | 57 | # compile all *c files 58 | $(OBJDIR)/%.rel: %.c $(PRJ_HEADER) $(LIB_HEADER) $(LIB_SOURCE) $(OBJDIR) 59 | $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ 60 | 61 | # link all object files and libaries 62 | $(TARGET): $(PRJ_OBJECT) $(LIB_OBJECT) 63 | $(CC) $(LFLAGS) $(PRJ_OBJECT) $(LIB_OBJECT) -o $@ 64 | 65 | # clean up 66 | clean: 67 | rm -fr .DS_Store 68 | rm -fr -- -p 69 | rm -fr $(OBJDIR) 70 | rm -fr *.TMP 71 | 72 | # upload via stm8flash / SWIM (https://github.com/vdudouyt/stm8flash) 73 | # stm8flash requires device in lower-case -> http://gnu-make.2324884.n4.nabble.com/how-to-achieve-conversion-to-uppercase-td14496.html 74 | swim: 75 | stm8flash -c stlinkv2 -w $(TARGET) -p stm8s003p3 76 | 77 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | HC12 Packet Junkie 2 | 3 | Alternate firmware for HC12 serial RF modem modules. 4 | 5 | The HC12 is an inexpensive 433MHz RF module found for under $5 on your 6 | Chinese import site of choice. Out of the box it communicates with other 7 | HC12 modules over long distances (reportedly 1-2km) using a proprietary 8 | radio protocol. A simple command interface over a UART on the 5 pin 9 | header is used to talk to the modem. The radio IC is a Silicon Labs Si4463 10 | which has excellent receive sensitivity, 20dBm (0.1W) transmit power, and 11 | a very configurable FSK modem and packet processor. An ST Micro STM8S003 12 | microcontroller manages the radio and the serial host interface. 13 | 14 | The anonymous designer of this PCB was kind enough to put test points on 15 | the back of the HC12 PCB for the SWIM and RST pins of the microcontroller, 16 | making it easy to replace the factory firmware with our own. 17 | 18 | *** This is a work in progress! *** 19 | 20 | Based on the STM8_template examples and library from Georg Icking-Konert 21 | available at https://github.com/gicking/STM8_templates.git . 22 | 23 | 24 | -------------------------------------------------------------------------------- /STM8_Lib/RAM_routines.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file RAM_routines.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of RAM routines for flash block w/e 9 | 10 | implementation of RAM routines which are required for flash write/erase 11 | */ 12 | 13 | /*---------------------------------------------------------- 14 | INCLUDE FILES 15 | ----------------------------------------------------------*/ 16 | #include 17 | #include "stm8as.h" 18 | #include "flash.h" 19 | #include "memory_access.h" 20 | #include "RAM_routines.h" 21 | 22 | 23 | // keyword for Cosmic compiler place code in RAM 24 | #if defined(__CSMC__) 25 | #pragma section (RAM_CODE) 26 | #endif // __CSMC__ 27 | 28 | 29 | /** 30 | \fn void RAM_erase_block(void) 31 | 32 | \brief RAM routine to erase 128B flash block 33 | 34 | erase 128B flash block which contains g_addr. 35 | Actual block erase needs to be executed from RAM. 36 | Use global address to minimize valuable RAM space 37 | */ 38 | void RAM_erase_block(MEM_POINTER_T addr) { 39 | 40 | // enable block erase 41 | FLASH.CR2.reg.ERASE = 1; 42 | FLASH.NCR2.reg.ERASE = 0; // complementary register 43 | 44 | // clear 4B word in sector 45 | write_4B(addr, 0x00000000); 46 | 47 | // wait until erase finished 48 | while (!FLASH.IAPSR.reg.EOP); 49 | 50 | } // RAM_erase_block 51 | 52 | 53 | 54 | /** 55 | \fn void RAM_write_block(MEM_POINTER_T addr, uint8_t buf[]) 56 | 57 | \brief RAM routine to copy 128B buffer to flash block 58 | 59 | \param[in] addr starting address of block to write 60 | \param[in] buf 128B buffer to write 61 | 62 | write 128B flash block starting at addr. 63 | Actual block write needs to be executed from RAM 64 | */ 65 | void RAM_write_block(MEM_POINTER_T addr, uint8_t buf[]) { 66 | 67 | uint8_t i; 68 | 69 | // enable fast block programming mode 70 | FLASH.CR2.reg.FPRG = 1; 71 | FLASH.NCR2.reg.FPRG = 0; 72 | 73 | // copy content to latch 74 | for (i=0; i<128; i++) 75 | write_1B(addr++, buf[i]); 76 | 77 | // wait until erase finished 78 | while (!FLASH.IAPSR.reg.EOP); 79 | 80 | } // RAM_write_block 81 | 82 | 83 | // end of RAM code for Cosmic compiler 84 | #if defined(__CSMC__) 85 | #pragma section () 86 | #endif // __CSMC__ 87 | 88 | /*----------------------------------------------------------------------------- 89 | END OF MODULE 90 | -----------------------------------------------------------------------------*/ 91 | -------------------------------------------------------------------------------- /STM8_Lib/RAM_routines.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file RAM_routines.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of RAM routines for flash block w/e 9 | 10 | declaration of RAM routines which are required for flash write/erase 11 | */ 12 | 13 | /*----------------------------------------------------------------------------- 14 | MODULE DEFINITION FOR MULTIPLE INCLUSION 15 | -----------------------------------------------------------------------------*/ 16 | #ifndef _RAM_ROUTINES_H_ 17 | #define _RAM_ROUTINES_H_ 18 | 19 | /*----------------------------------------------------------------------------- 20 | INCLUDE FILES 21 | -----------------------------------------------------------------------------*/ 22 | #include 23 | #include "stm8as.h" 24 | 25 | 26 | /*----------------------------------------------------------------------------- 27 | DEFINITION OF GLOBAL FUNCTIONS 28 | -----------------------------------------------------------------------------*/ 29 | 30 | /// RAM routine to erase 128B flash block 31 | void RAM_erase_block(MEM_POINTER_T addr); 32 | 33 | /// RAM routine to copy 128B buffer to flash block 34 | void RAM_write_block(MEM_POINTER_T addr, uint8_t buf[]); 35 | 36 | /*----------------------------------------------------------------------------- 37 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 38 | -----------------------------------------------------------------------------*/ 39 | #endif // _FLASH_H_ 40 | -------------------------------------------------------------------------------- /STM8_Lib/adc2.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file adc2.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of ADC2 functions/macros 9 | 10 | implementation of functions for ADC2 measurements in single-shot mode 11 | */ 12 | 13 | /*----------------------------------------------------------------------------- 14 | INCLUDE FILES 15 | -----------------------------------------------------------------------------*/ 16 | #include 17 | #include "stm8as.h" 18 | #include "adc2.h" 19 | 20 | 21 | /*---------------------------------------------------------- 22 | FUNCTIONS 23 | ----------------------------------------------------------*/ 24 | 25 | /** 26 | \fn void adc2_init(void) 27 | 28 | \brief initialize ADC 29 | 30 | initialize ADC2 for single-shot measurements. Default to channel AIN0 31 | */ 32 | void adc2_init() { 33 | 34 | // reset channel selection [3:0], no ADC interrupt [5] 35 | ADC2.CSR.byte = 0x00; 36 | 37 | // set single shot mode 38 | ADC2.CR1.reg.CONT = 0; 39 | 40 | // set ADC clock 1/12*fMaster (<2MHz) 41 | ADC2.CR1.reg.SPSEL = 6; 42 | 43 | // right alignment (read DRL, then DRH), no external trigger 44 | ADC2.CR2.reg.ALIGN = 1; 45 | 46 | // disable Schmitt trigger only for measurement channels 47 | //ADC2.TDR = 0xFFFF; 48 | 49 | // ADC module on 50 | ADC2.CR1.reg.ADON = 1; 51 | 52 | } // adc2_init 53 | 54 | 55 | 56 | /** 57 | \fn uint16_t adc2_measure(uint8_t ch) 58 | 59 | \brief measure ADC2 channel 60 | 61 | \param ch ADC2 channel to query 62 | \return ADC2 value in INC 63 | 64 | measure ADC2 channel in single shot mode. 65 | */ 66 | uint16_t adc2_measure(uint8_t ch) { 67 | 68 | uint16_t result; 69 | 70 | // switch to ADC channel if required. Else skip to avoid AMUX charge injection 71 | if (ADC2.CSR.reg.CH != ch) 72 | ADC2.CSR.reg.CH = ch; 73 | 74 | // clear conversion ready flag, start conversion, wait until conversion done 75 | ADC2.CSR.reg.EOC = 0; 76 | ADC2.CR1.reg.ADON = 1; // start conversion 77 | while(!ADC2.CSR.reg.EOC); // wait for "conversion ready" flag 78 | 79 | // get ADC result (read low byte first for right alignment!) 80 | result = (uint16_t) (ADC2.DR.byteL); 81 | result += ((uint16_t)(ADC2.DR.byteH)) << 8; 82 | 83 | // don't switch to default channel to avoid AMUX charge injection 84 | 85 | // return result 86 | return(result); 87 | 88 | } // adc2_measure 89 | 90 | 91 | /*----------------------------------------------------------------------------- 92 | END OF MODULE 93 | -----------------------------------------------------------------------------*/ 94 | -------------------------------------------------------------------------------- /STM8_Lib/adc2.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file adc2.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of ADC2 functions/macros 9 | 10 | declaration of functions for ADC2 measurements in single-shot mode 11 | */ 12 | 13 | /*----------------------------------------------------------------------------- 14 | MODULE DEFINITION FOR MULTIPLE INCLUSION 15 | -----------------------------------------------------------------------------*/ 16 | #ifndef _ADC2_H_ 17 | #define _ADC2_H_ 18 | 19 | 20 | /*----------------------------------------------------------------------------- 21 | DEFINITION OF GLOBAL MACROS/#DEFINES 22 | -----------------------------------------------------------------------------*/ 23 | 24 | // includes 25 | #include 26 | 27 | 28 | /*----------------------------------------------------------------------------- 29 | DECLARATION OF GLOBAL FUNCTIONS 30 | -----------------------------------------------------------------------------*/ 31 | 32 | /// initialize ADC2 for single shot mode 33 | void adc2_init(void); 34 | 35 | /// measure ADC2 channel 36 | uint16_t adc2_measure(uint8_t ch); 37 | 38 | 39 | /*----------------------------------------------------------------------------- 40 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 41 | -----------------------------------------------------------------------------*/ 42 | #endif // _ADC_H_ 43 | -------------------------------------------------------------------------------- /STM8_Lib/beeper.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file beeper.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of beeper control 9 | 10 | implementation of functions for beeper control 11 | */ 12 | 13 | /*----------------------------------------------------------------------------- 14 | INCLUDE FILES 15 | -----------------------------------------------------------------------------*/ 16 | #include "stm8as.h" 17 | #include "beeper.h" 18 | #include "timer3.h" // for sleep_ms() 19 | 20 | 21 | /*---------------------------------------------------------- 22 | FUNCTIONS 23 | ----------------------------------------------------------*/ 24 | 25 | /** 26 | \fn void beep(uint8_t freq, uint16_t duration) 27 | 28 | \brief beeper control 29 | 30 | \param[in] freq beeper frequency/state (1=1kHz, 2=2kHz, 4=4kHz, other=off) 31 | \param[in] duration duration [ms] of signal (0 = start beep and continue code execution) 32 | 33 | control the on-board beeper with frequency/state and duration 34 | */ 35 | void beep(uint8_t freq, uint16_t duration) { 36 | 37 | // set to default calibration 38 | BEEP.CSR.byte = 0x0B; 39 | 40 | // switch beeper frequency ond state (assume stable LS clock 128kHz) 41 | switch (freq) { 42 | 43 | case 1: 44 | BEEP.CSR.reg.BEEPSEL = 0; // set 1KHz 45 | BEEP.CSR.reg.BEEPEN = 1; // enable beeper 46 | break; 47 | 48 | case 2: 49 | BEEP.CSR.reg.BEEPSEL = 1; // set 2KHz 50 | BEEP.CSR.reg.BEEPEN = 1; // enable beeper 51 | break; 52 | 53 | case 4: 54 | BEEP.CSR.reg.BEEPSEL = 2; // set 4KHz 55 | BEEP.CSR.reg.BEEPEN = 1; // enable beeper 56 | break; 57 | 58 | default: 59 | BEEP.CSR.reg.BEEPEN = 0; // disable beeper 60 | 61 | } // switch (state) 62 | 63 | 64 | // if duration >0 -> wait time [ms], then disable beeper 65 | if (duration > 0) { 66 | sleep_ms(duration); 67 | BEEP.CSR.reg.BEEPEN = 0; // disable beeper 68 | } 69 | 70 | } // beep 71 | 72 | 73 | /*----------------------------------------------------------------------------- 74 | END OF MODULE 75 | -----------------------------------------------------------------------------*/ 76 | -------------------------------------------------------------------------------- /STM8_Lib/beeper.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file beeper.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of beeper control 9 | 10 | declaration of functions for beeper control 11 | */ 12 | 13 | /*----------------------------------------------------------------------------- 14 | MODULE DEFINITION FOR MULTIPLE INCLUSION 15 | -----------------------------------------------------------------------------*/ 16 | #ifndef _BEEPER_H_ 17 | #define _BEEPER_H_ 18 | 19 | #include 20 | 21 | 22 | /*----------------------------------------------------------------------------- 23 | DECLARATION OF GLOBAL FUNCTIONS 24 | -----------------------------------------------------------------------------*/ 25 | 26 | /// beeper control 27 | void beep(uint8_t freq, uint16_t duration); 28 | 29 | 30 | /*----------------------------------------------------------------------------- 31 | DEFINITION OF GLOBAL MACROS/#DEFINES 32 | -----------------------------------------------------------------------------*/ 33 | 34 | /*----------------------------------------------------------------------------- 35 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 36 | -----------------------------------------------------------------------------*/ 37 | #endif // _BEEPER_H_ 38 | -------------------------------------------------------------------------------- /STM8_Lib/debug.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file debug.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of global debug variables 9 | 10 | declaration of global variables with fixed addresses for debugging, 11 | e.g. via UART or STMStudio (via SWIM). 12 | For clarity all global variables start with "g_". 13 | */ 14 | 15 | /*---------------------------------------------------------- 16 | MODULE DEFINITION FOR MULTIPLE INCLUSION 17 | ----------------------------------------------------------*/ 18 | #ifndef _DEBUG_H_ 19 | #define _DEBUG_H_ 20 | 21 | #include 22 | #include "stm8as.h" 23 | 24 | 25 | /*----------------------------------------------------------------------------- 26 | DECLARATION OF GLOBAL VARIABLES 27 | -----------------------------------------------------------------------------*/ 28 | 29 | // declare global variables with fixed addresses 30 | #ifdef _MAIN_ 31 | 32 | // misc variables for debugging via STM Studio (needs fixed address) 33 | reg(0x00F0, uint8_t, g_valu8); ///< global uint8 variable for debug via STM Studio 34 | reg(0x00F1, uint16_t, g_valu16); ///< global uint16 variable for debug via STM Studio 35 | reg(0x00F3, uint32_t, g_valu32); ///< global uint32 variable for debug via STM Studio 36 | 37 | // refer to global variables 38 | #else // _MAIN_ 39 | extern uint8_t g_valu8; 40 | extern uint16_t g_valu16; 41 | extern uint32_t g_valu32; 42 | #endif // _MAIN_ 43 | 44 | 45 | /*----------------------------------------------------------------------------- 46 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 47 | -----------------------------------------------------------------------------*/ 48 | #endif // _DEBUG_H_ 49 | 50 | -------------------------------------------------------------------------------- /STM8_Lib/error_codes.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file error_codes.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of error codes 9 | 10 | declaration of codes for error tracking 11 | */ 12 | 13 | /*----------------------------------------------------------------------------- 14 | MODULE DEFINITION FOR MULTIPLE INCLUSION 15 | -----------------------------------------------------------------------------*/ 16 | #ifndef _ERROR_CODES_H_ 17 | #define _ERROR_CODES_H_ 18 | 19 | 20 | /*----------------------------------------------------------------------------- 21 | DEFINITION OF GLOBAL MACROS/#DEFINES 22 | -----------------------------------------------------------------------------*/ 23 | 24 | // error codes 25 | #define SUCCESS 0 // no error 26 | #define ERROR_TIMOUT 1 // timeout 27 | #define ERROR_COMM 2 // communication error 28 | #define ERROR_RANGE 3 // out of range error 29 | #define ERROR_ILLPRM 5 // illegal parameter error 30 | #define ERROR_CHK 6 // checksum error 31 | #define ERROR_FLASH 7 // flash error (e.g. during write) 32 | #define ERROR_MISC 255 // misc error and muCom indicator for error (precedes actual error code) 33 | 34 | 35 | /*----------------------------------------------------------------------------- 36 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 37 | -----------------------------------------------------------------------------*/ 38 | #endif // _ERROR_CODES_H_ 39 | -------------------------------------------------------------------------------- /STM8_Lib/exint.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file exint.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of functions/macros for external interrupts 9 | 10 | implementation of functions/macros for external interrupts on port pins 11 | */ 12 | 13 | /*----------------------------------------------------------------------------- 14 | INCLUDE FILES 15 | -----------------------------------------------------------------------------*/ 16 | #include 17 | #include "stm8as.h" 18 | #include "gpio.h" 19 | #include "exint.h" 20 | 21 | 22 | /*---------------------------------------------------------- 23 | FUNCTIONS 24 | ----------------------------------------------------------*/ 25 | 26 | /** 27 | \fn void exint_init(GPIO_TypeDef *addrPort, uint8_t edge) 28 | 29 | \brief edge control for EXINTs 30 | 31 | \param[in] addrPort address of port to configure, e.g. PORT_A (see gpio.h) 32 | \param[in] edge select edge for exint, e.g. EXINT_RISE_EDGE (see gpio.h) 33 | 34 | configure edge control for GPIO external interrupts. Can only be done for complete 35 | port. For pins with EXINT functionality see exint.h. 36 | 37 | Note that all interrupts have to be disabled when calling this routine! 38 | */ 39 | void exint_init(GPIO_TypeDef *addrPort, uint8_t edge) { 40 | 41 | // select correct port 42 | if (addrPort == &PORT_A) { 43 | EXTI.CR1.reg.PAIS = edge; // set edge control 44 | g_countExintA = 0; // reset counter 45 | } 46 | else if (addrPort == &PORT_B) { 47 | EXTI.CR1.reg.PBIS = edge; // set edge control 48 | g_countExintB = 0; // reset counter 49 | } 50 | else if (addrPort == &PORT_C) { 51 | EXTI.CR1.reg.PCIS = edge; // set edge control 52 | g_countExintC = 0; // reset counter 53 | } 54 | else if (addrPort == &PORT_D) { 55 | EXTI.CR1.reg.PDIS = edge; // set edge control 56 | g_countExintD = 0; // reset counter 57 | } 58 | else if (addrPort == &PORT_E) { 59 | EXTI.CR2.reg.PEIS = edge; // set edge control 60 | g_countExintE = 0; // reset counter 61 | } 62 | 63 | } // exint_init 64 | 65 | 66 | 67 | /** 68 | \fn void EXINT_PA_ISR(void) 69 | 70 | \brief ISR for external interrupt on port A 71 | 72 | interrupt service routine for external interrupt on a port A pin. 73 | Here only a global event counter is increased, see exint.h. 74 | */ 75 | #if defined(__CSMC__) 76 | @near @interrupt void EXINT_PA_ISR(void) 77 | #elif defined(__SDCC) 78 | void EXINT_PA_ISR() __interrupt(__EXTI0_VECTOR__) 79 | #endif 80 | { 81 | // increase global exint counter (port specific) 82 | g_countExintA++; 83 | 84 | return; 85 | 86 | } // EXINT_PA_ISR 87 | 88 | 89 | 90 | /** 91 | \fn void EXINT_PB_ISR(void) 92 | 93 | \brief ISR for external interrupt on port B 94 | 95 | interrupt service routine for external interrupt on a port B pin. 96 | Here only a global event counter is increased, see exint.h. 97 | */ 98 | #if defined(__CSMC__) 99 | @near @interrupt void EXINT_PB_ISR(void) 100 | #elif defined(__SDCC) 101 | void EXINT_PB_ISR() __interrupt(__EXTI1_VECTOR__) 102 | #endif 103 | { 104 | // increase global exint counter (port specific) 105 | g_countExintB++; 106 | 107 | return; 108 | 109 | } // EXINT_PB_ISR 110 | 111 | 112 | 113 | /** 114 | \fn void EXINT_PC_ISR(void) 115 | 116 | \brief ISR for external interrupt on port C 117 | 118 | interrupt service routine for external interrupt on a port C pin. 119 | Here only a global event counter is increased, see exint.h. 120 | */ 121 | #if defined(__CSMC__) 122 | @near @interrupt void EXINT_PC_ISR(void) 123 | #elif defined(__SDCC) 124 | void EXINT_PC_ISR() __interrupt(__EXTI2_VECTOR__) 125 | #endif 126 | { 127 | // increase global exint counter (port specific) 128 | g_countExintC++; 129 | 130 | return; 131 | 132 | } // EXINT_PC_ISR 133 | 134 | 135 | 136 | /** 137 | \fn void EXINT_PD_ISR(void) 138 | 139 | \brief ISR for external interrupt on port D 140 | 141 | interrupt service routine for external interrupt on a port D pin. 142 | Here only a global event counter is increased, see exint.h. 143 | */ 144 | #if defined(__CSMC__) 145 | @near @interrupt void EXINT_PD_ISR(void) 146 | #elif defined(__SDCC) 147 | void EXINT_PD_ISR() __interrupt(__EXTI3_VECTOR__) 148 | #endif 149 | { 150 | // increase global exint counter (port specific) 151 | g_countExintD++; 152 | 153 | return; 154 | 155 | } // EXINT_PD_ISR 156 | 157 | 158 | 159 | /** 160 | \fn void EXINT_PE_ISR(void) 161 | 162 | \brief ISR for external interrupt on port E 163 | 164 | interrupt service routine for external interrupt on a port E pin. 165 | Here only a global event counter is increased, see exint.h. 166 | */ 167 | #if defined(__CSMC__) 168 | @near @interrupt void EXINT_PE_ISR(void) 169 | #elif defined(__SDCC) 170 | void EXINT_PE_ISR() __interrupt(__EXTI4_VECTOR__) 171 | #endif 172 | { 173 | 174 | // increase global exint counter (port specific) 175 | g_countExintE++; 176 | 177 | return; 178 | 179 | } // EXINT_PE_ISR 180 | 181 | 182 | /*----------------------------------------------------------------------------- 183 | END OF MODULE 184 | -----------------------------------------------------------------------------*/ 185 | -------------------------------------------------------------------------------- /STM8_Lib/exint.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file exint.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of functions/macros for external interrupts 9 | 10 | declaration of functions/macros for external interrupts on port pins 11 | */ 12 | 13 | /*----------------------------------------------------------------------------- 14 | MODULE DEFINITION FOR MULTIPLE INCLUSION 15 | -----------------------------------------------------------------------------*/ 16 | #ifndef _EXINT_H_ 17 | #define _EXINT_H_ 18 | 19 | /*----------------------------------------------------------------------------- 20 | INCLUDE FILES 21 | -----------------------------------------------------------------------------*/ 22 | 23 | #include 24 | #include "stm8as.h" 25 | 26 | 27 | /*----------------------------------------------------------------------------- 28 | DECLARATION OF GLOBAL VARIABLES 29 | -----------------------------------------------------------------------------*/ 30 | 31 | // EXINT event counters (increased in respective ISR) 32 | global uint32_t g_countExintA; ///< PA external interrupts 33 | global uint32_t g_countExintB; ///< PB external interrupts 34 | global uint32_t g_countExintC; ///< PC external interrupts 35 | global uint32_t g_countExintD; ///< PD external interrupts 36 | global uint32_t g_countExintE; ///< PE external interrupts 37 | 38 | 39 | 40 | /*----------------------------------------------------------------------------- 41 | DECLARATION OF GLOBAL MACROS 42 | -----------------------------------------------------------------------------*/ 43 | 44 | // edge control for external interrupts (0=low, 1=rising, 2=falling, 3=both) 45 | #define EXINT_LOW_LEVEL 0 ///< external interrupt on low level --> careful! 46 | #define EXINT_RISE_EDGE 1 ///< external interrupt on rising edge 47 | #define EXINT_FALL_EDGE 2 ///< external interrupt on falling edge 48 | #define EXINT_BOTH_EDGE 3 ///< external interrupt on both edges 49 | 50 | 51 | /*----------------------------------------------------------------------------- 52 | DECLARATION OF GLOBAL FUNCTIONS 53 | -----------------------------------------------------------------------------*/ 54 | 55 | /// edge control for EXINTs 56 | void exint_init(GPIO_TypeDef *addrPort, uint8_t edge); 57 | 58 | /// ISR for external interrupt pins 59 | #if defined(__CSMC__) 60 | @near @interrupt void EXINT_PA_ISR(void); // port a 61 | @near @interrupt void EXINT_PB_ISR(void); // port b 62 | @near @interrupt void EXINT_PC_ISR(void); // port c 63 | @near @interrupt void EXINT_PD_ISR(void); // port d 64 | @near @interrupt void EXINT_PE_ISR(void); // port e 65 | #elif defined(__SDCC) 66 | void EXINT_PA_ISR(void) __interrupt(__EXTI0_VECTOR__); // port a 67 | void EXINT_PB_ISR(void) __interrupt(__EXTI1_VECTOR__); // port b 68 | void EXINT_PC_ISR(void) __interrupt(__EXTI2_VECTOR__); // port c 69 | void EXINT_PD_ISR(void) __interrupt(__EXTI3_VECTOR__); // port d 70 | void EXINT_PE_ISR(void) __interrupt(__EXTI4_VECTOR__); // port e 71 | #endif 72 | 73 | 74 | /*----------------------------------------------------------------------------- 75 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 76 | -----------------------------------------------------------------------------*/ 77 | #endif // _EXINT_H_ 78 | -------------------------------------------------------------------------------- /STM8_Lib/flash.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file flash.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of flash read/write routines 9 | 10 | implementation of flash read/write routines. 11 | RAM routines for block operations are in RAM_routines.c 12 | */ 13 | 14 | /*---------------------------------------------------------- 15 | INCLUDE FILES 16 | ----------------------------------------------------------*/ 17 | #include 18 | #include "stm8as.h" 19 | #include "flash.h" 20 | #include "memory_access.h" 21 | 22 | /** 23 | \fn void flash_OPT_default(void) 24 | 25 | \brief assert default option byte setting 26 | 27 | assert that all option bytes have their default setting (see below). 28 | On change trigger a reset. 29 | */ 30 | void flash_OPT_default() { 31 | 32 | uint8_t flagWD = 0; 33 | 34 | // reset alternate GPIO mapping (=OPT2/NOPT2) 35 | if ((*((uint8_t*) OPT2) != 0x00) || (*((uint8_t*) NOPT2) != 0xFF)) { 36 | flash_write_option_byte(OPT2, 0x00); 37 | flash_write_option_byte(NOPT2, 0xFF); 38 | flagWD = 1; 39 | } 40 | 41 | // deactivate watchdog (=OPT3/NOPT3) 42 | if ((*((uint8_t*) OPT3) != 0x00) || (*((uint8_t*) NOPT3) != 0xFF)) { 43 | flash_write_option_byte(OPT3, 0x00); 44 | flash_write_option_byte(NOPT3, 0xFF); 45 | flagWD = 1; 46 | } 47 | 48 | // reset clock options to default (=OPT4/NOPT4) 49 | if ((*((uint8_t*) OPT4) != 0x00) || (*((uint8_t*) NOPT4) != 0xFF)) { 50 | flash_write_option_byte(OPT4, 0x00); 51 | flash_write_option_byte(NOPT4, 0xFF); 52 | flagWD = 1; 53 | } 54 | 55 | // max. HCE clock startup time (=OPT5/NOPT5) 56 | if ((*((uint8_t*) OPT5) != 0x00) || (*((uint8_t*) NOPT5) != 0xFF)) { 57 | flash_write_option_byte(OPT5, 0x00); 58 | flash_write_option_byte(NOPT5, 0xFF); 59 | flagWD = 1; 60 | } 61 | 62 | // OPT6 is reserved/undocumented 63 | 64 | // no flash wait state (required for >16MHz) (=OPT7/NOPT7) 65 | if ((*((uint8_t*) OPT7) != 0x00) || (*((uint8_t*) NOPT7) != 0xFF)) { 66 | flash_write_option_byte(OPT7, 0x00); 67 | flash_write_option_byte(NOPT7, 0xFF); 68 | flagWD = 1; 69 | } 70 | 71 | // OPT8-16 contain temporary memory unprotection key (TMU) -> rather don't touch 72 | 73 | // activate ROM-bootloader (=OPT17/NOPT17) 74 | if ((*((uint8_t*) OPT17) != 0x55) || (*((uint8_t*) NOPT17) != 0xAA)) { 75 | flash_write_option_byte(OPT17, 0x55); 76 | flash_write_option_byte(NOPT17, 0xAA); 77 | flagWD = 1; 78 | } 79 | 80 | // if any option byte was changed trigger SW reset 81 | if (flagWD != 0) 82 | SW_RESET; 83 | 84 | } // flash_OPT_default() 85 | 86 | 87 | 88 | /** 89 | \fn uint8_t flash_write_option_byte(uint16_t addr, uint8_t byte) 90 | 91 | \brief write option byte 92 | 93 | \param[in] addr 16b address of option byte to write 94 | \param[in] byte byte to program 95 | 96 | \return byte changed (=1) or unchanged (=0) 97 | 98 | write single option byte to given address (16b sufficient for all devices). 99 | Only write if value needs to be changed. 100 | */ 101 | uint8_t flash_write_option_byte(uint16_t addr, uint8_t byte) { 102 | 103 | uint8_t countTimeout=0; // use counter rather than timer for flash timeout to avoid conflicts 104 | 105 | // check address 106 | if ((addr < 0x4800) || (addr > 0x48FF)) 107 | return(0); 108 | 109 | // skip write if value is already correct 110 | if ((*((uint8_t*) addr)) == byte) 111 | return(0); 112 | 113 | // disable interrupts 114 | DISABLE_INTERRUPTS; 115 | 116 | // unlock w/e access to EEPROM & option bytes 117 | FLASH.DUKR.byte = 0xAE; 118 | FLASH.DUKR.byte = 0x56; 119 | 120 | // additionally required for option bytes 121 | FLASH.CR2.byte |= 0x80; 122 | FLASH.NCR2.byte &= 0x7F; 123 | 124 | // wait until access granted 125 | while(!FLASH.IAPSR.reg.DUL); 126 | 127 | // write option byte to p-flash 128 | *((uint8_t*) addr) = byte; 129 | 130 | // wait until done or timeout (normal flash write measured with 0 --> 100 is more than sufficient) 131 | while ((!FLASH.IAPSR.reg.EOP) && ((++countTimeout) < 100)); 132 | 133 | // lock EEPROM again against accidental erase/write 134 | FLASH.IAPSR.reg.DUL = 0; 135 | 136 | // additional lock 137 | FLASH.CR2.byte &= 0x7F; 138 | FLASH.NCR2.byte |= 0x80; 139 | 140 | 141 | // enable interrupts 142 | ENABLE_INTERRUPTS; 143 | 144 | // option byte changed -> return 1 145 | return(1); 146 | 147 | } // flash_write_option_byte 148 | 149 | 150 | 151 | /** 152 | \fn void flash_write_byte(MEM_POINTER_T addr, uint8_t data) 153 | 154 | \brief write single byte to flash 155 | 156 | \param[in] addr address to write to 157 | \param[in] data byte to program 158 | 159 | write single byte to address in P-flash or EEPROM. 160 | For address width see file stm8as.h 161 | 162 | Warning: for simplicity no safeguard is used to protect against 163 | accidental data loss or even overwriting the application -> use with care! 164 | */ 165 | void flash_write_byte(MEM_POINTER_T addr, uint8_t data) { 166 | 167 | uint8_t countTimeout=0; // use counter for timeout to avoid timer conflicts 168 | 169 | // disable interrupts 170 | DISABLE_INTERRUPTS; 171 | 172 | // unlock w/e access to P-flash 173 | FLASH.PUKR.byte = 0x56; 174 | FLASH.PUKR.byte = 0xAE; 175 | 176 | // unlock w/e access to EEPROM 177 | FLASH.DUKR.byte = 0xAE; 178 | FLASH.DUKR.byte = 0x56; 179 | 180 | // wait until access granted 181 | while(!FLASH.IAPSR.reg.PUL); 182 | while(!FLASH.IAPSR.reg.DUL); 183 | 184 | // write byte using 16-bit or 32-bit macro/function (see flash.h) 185 | write_1B(addr, data); 186 | 187 | // wait until done or timeout (normal flash write measured with 0 --> 100 is more than sufficient) 188 | while ((!FLASH.IAPSR.reg.EOP) && ((++countTimeout) < 100)); 189 | 190 | // lock P-flash and EEPROM again against accidental erase/write 191 | FLASH.IAPSR.reg.PUL = 0; 192 | FLASH.IAPSR.reg.DUL = 0; 193 | 194 | // enable interrupts 195 | ENABLE_INTERRUPTS; 196 | 197 | } // flash_write_byte 198 | 199 | 200 | 201 | // only required for flash block write/erase operations (need to be executed from RAM!) 202 | #if defined(FLASH_BLOCK_OPS) 203 | 204 | // start RAM code section (Cosmic compiler) 205 | #if defined(__CSMC__) 206 | #pragma section (RAM_CODE) // Cosmic 207 | #endif // __CSMC__ 208 | 209 | /** 210 | \fn void flash_erase_block(MEM_POINTER_T addr) 211 | 212 | \brief erase 128B block in P-flash (must be executed from RAM) 213 | 214 | \param[in] addr address of block to erase 215 | 216 | erase 128B flash block which contains addr. 217 | Actual code for block erase needs to be executed from RAM 218 | For address width see file stm8as.h 219 | 220 | Warning: for simplicity no safeguard is used to protect against 221 | accidental data loss or even overwriting the application -> use with care! 222 | */ 223 | void flash_erase_block(MEM_POINTER_T addr) { 224 | 225 | // disable interrupts 226 | DISABLE_INTERRUPTS; 227 | 228 | // unlock w/e access to P-flash 229 | FLASH.PUKR.byte = 0x56; 230 | FLASH.PUKR.byte = 0xAE; 231 | 232 | // unlock w/e access to EEPROM 233 | FLASH.DUKR.byte = 0xAE; 234 | FLASH.DUKR.byte = 0x56; 235 | 236 | 237 | /////////////// start min. code section in RAM... ///////////////// 238 | 239 | // enable p-flash erase 240 | FLASH.CR2.reg.ERASE = 1; 241 | FLASH.NCR2.reg.ERASE = 0; // complementary register 242 | while(!FLASH.IAPSR.reg.PUL); 243 | 244 | // init erwase by writing 0x00 to 4B word inside page 245 | write_4B(addr, 0x00000000); 246 | 247 | /////////////// ...until here ///////////////// 248 | 249 | 250 | // lock P-flash and EEPROM again against accidental erase/write 251 | FLASH.IAPSR.reg.PUL = 0; 252 | FLASH.IAPSR.reg.DUL = 0; 253 | 254 | // enable interrupts 255 | ENABLE_INTERRUPTS; 256 | 257 | } // flash_erase_block 258 | 259 | 260 | 261 | /** 262 | \fn void flash_write_block(MEM_POINTER_T addr, uint8_t ch[]) 263 | 264 | \brief write 128B block to flash (must be executed from RAM) 265 | 266 | \param[in] addr address to write to (for width see stm8as.h) 267 | \param[in] buf 128B buffer to write 268 | 269 | Fast write 128B block to flash (adress must be divisable by 128) 270 | Actual code for block write needs to be executed from RAM 271 | For address width see file stm8as.h 272 | 273 | Warning: for simplicity no safeguard is used to protect against 274 | accidental data loss or even overwriting the application -> use with care! 275 | */ 276 | void flash_write_block(MEM_POINTER_T addr, uint8_t buf[]) { 277 | 278 | uint8_t i; 279 | 280 | // disable interrupts 281 | DISABLE_INTERRUPTS; 282 | 283 | // unlock w/e access to P-flash 284 | FLASH.PUKR.byte = 0x56; 285 | FLASH.PUKR.byte = 0xAE; 286 | 287 | // unlock w/e access to EEPROM 288 | FLASH.DUKR.byte = 0xAE; 289 | FLASH.DUKR.byte = 0x56; 290 | 291 | // wait until access granted 292 | while(!FLASH.IAPSR.reg.PUL); 293 | while(!FLASH.IAPSR.reg.DUL); 294 | 295 | 296 | /////////////// start min. code section in RAM... ///////////////// 297 | 298 | // set fast block programming mode 299 | FLASH.CR2.reg.FPRG = 1; 300 | FLASH.NCR2.reg.FPRG = 0; 301 | 302 | // copy 128B to latch 303 | for (i=0; i<128; i++) 304 | write_1B(addr+i, buf[i]); 305 | 306 | /////////////// ...until here ///////////////// 307 | 308 | 309 | // lock P-flash and EEPROM again against accidental erase/write 310 | FLASH.IAPSR.reg.PUL = 0; 311 | FLASH.IAPSR.reg.DUL = 0; 312 | 313 | // enable interrupts 314 | ENABLE_INTERRUPTS; 315 | 316 | } // flash_write_block 317 | 318 | 319 | // end RAM code section (Cosmic compiler) 320 | #if defined(__CSMC__) 321 | #pragma section () 322 | #endif // __CSMC__ 323 | 324 | #endif // FLASH_BLOCK_OPS 325 | 326 | /*----------------------------------------------------------------------------- 327 | END OF MODULE 328 | -----------------------------------------------------------------------------*/ 329 | -------------------------------------------------------------------------------- /STM8_Lib/flash.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file flash.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of flash read/write routines 9 | 10 | declaration of flash read/write routines and macros. 11 | RAM routines for block operations are in RAM_routines.c 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | MODULE DEFINITION FOR MULTIPLE INCLUSION 16 | -----------------------------------------------------------------------------*/ 17 | #ifndef _FLASH_H_ 18 | #define _FLASH_H_ 19 | 20 | /*----------------------------------------------------------------------------- 21 | INCLUDE FILES 22 | -----------------------------------------------------------------------------*/ 23 | #include 24 | #include "stm8as.h" 25 | #include "memory_access.h" 26 | 27 | 28 | /*----------------------------------------------------------------------------- 29 | DEFINITION OF GLOBAL MACROS/#DEFINES 30 | -----------------------------------------------------------------------------*/ 31 | 32 | ////// 33 | // addresses of option bytes 34 | ////// 35 | #define OPT0 (OPT_BaseAddress+0x00) //!< Option byte 0: Read-out protection (not accessible in IAP mode) 36 | #define OPT1 (OPT_BaseAddress+0x01) //!< Option byte 1: User boot code */ 37 | #define NOPT1 (OPT_BaseAddress+0x02) //!< Complementary Option byte 1 */ 38 | #define OPT2 (OPT_BaseAddress+0x03) //!< Option byte 2: Alternate function remapping */ 39 | #define NOPT2 (OPT_BaseAddress+0x04) //!< Complementary Option byte 2 */ 40 | #define OPT3 (OPT_BaseAddress+0x05) //!< Option byte 3: Watchdog option */ 41 | #define NOPT3 (OPT_BaseAddress+0x06) //!< Complementary Option byte 3 */ 42 | #define OPT4 (OPT_BaseAddress+0x07) //!< Option byte 4: Clock option */ 43 | #define NOPT4 (OPT_BaseAddress+0x08) //!< Complementary Option byte 4 */ 44 | #define OPT5 (OPT_BaseAddress+0x09) //!< Option byte 5: HSE clock startup */ 45 | #define NOPT5 (OPT_BaseAddress+0x0A) //!< Complementary Option byte 5 */ 46 | #define RES1 (OPT_BaseAddress+0x0B) //!< Reserved Option byte*/ 47 | #define RES2 (OPT_BaseAddress+0x0C) //!< Reserved Option byte*/ 48 | #define OPT7 (OPT_BaseAddress+0x0D) //!< Option byte 7: flash wait states */ 49 | #define NOPT7 (OPT_BaseAddress+0x0E) //!< Complementary Option byte 7 */ 50 | 51 | #define OPT8 (OPT_BaseAddress+0x10) //!< Option byte 8: TMU key 1 */ 52 | #define OPT9 (OPT_BaseAddress+0x11) //!< Option byte 9: TMU key 2 */ 53 | #define OPT10 (OPT_BaseAddress+0x12) //!< Option byte 10: TMU key 3 */ 54 | #define OPT11 (OPT_BaseAddress+0x13) //!< Option byte 11: TMU key 4 */ 55 | #define OPT12 (OPT_BaseAddress+0x14) //!< Option byte 12: TMU key 5 */ 56 | #define OPT13 (OPT_BaseAddress+0x15) //!< Option byte 13: TMU key 6 */ 57 | #define OPT14 (OPT_BaseAddress+0x16) //!< Option byte 14: TMU key 7 */ 58 | #define OPT15 (OPT_BaseAddress+0x17) //!< Option byte 15: TMU key 8 */ 59 | #define OPT16 (OPT_BaseAddress+0x18) //!< Option byte 16: TMU access failure counter */ 60 | 61 | #define OPT17 (OPT_BaseAddress+0x7E) //!< Option byte 17: BSL activation */ 62 | #define NOPT17 (OPT_BaseAddress+0x7F) //!< Complementary Option byte 17 */ 63 | 64 | 65 | /*----------------------------------------------------------------------------- 66 | DEFINITION OF GLOBAL FUNCTIONS 67 | -----------------------------------------------------------------------------*/ 68 | 69 | // flash write/erase routines 70 | void flash_OPT_default(void); 71 | uint8_t flash_write_option_byte(uint16_t addr, uint8_t data); ///< write option byte (all in 16-bit range) 72 | void flash_write_byte(MEM_POINTER_T addr, uint8_t data); ///< write 1B to P-flash or EEPROM 73 | void flash_erase_block(MEM_POINTER_T addr); ///< erase 128B block in flash (must be executed from RAM) 74 | void flash_write_block(MEM_POINTER_T addr, uint8_t buf[]); ///< write 128B block to flash (must be executed from RAM) 75 | 76 | /*----------------------------------------------------------------------------- 77 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 78 | -----------------------------------------------------------------------------*/ 79 | #endif // _FLASH_H_ 80 | -------------------------------------------------------------------------------- /STM8_Lib/gpio.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file gpio.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of functions/macros for GPIO control 9 | 10 | implementation of functions/macros for port pin control 11 | For speed use macros instead of functions where reasonable 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | INCLUDE FILES 16 | -----------------------------------------------------------------------------*/ 17 | #include 18 | #include "stm8as.h" 19 | #include "gpio.h" 20 | 21 | 22 | /*---------------------------------------------------------- 23 | FUNCTIONS 24 | ----------------------------------------------------------*/ 25 | 26 | /** 27 | \fn void gpio_init(GPIO_TypeDef *addrPort, uint8_t pins, uint8_t config) 28 | 29 | \brief configure GPIO pin 30 | 31 | \param[in] addrPort address of port to configure, e.g. PORT_A (see stm8as.h) 32 | \param[in] pins bitmask of one or more port pins to configure, e.g. PIN_1 (see gpio.h) 33 | \param[in] config select pin configuration, e.g. INPUT_PULLUP_NOEXINT (see gpio.h) 34 | 35 | configure GPIO port pins as input or output with different features activated. 36 | For supported configurations see gpio.h. Multiple port pins can be configured 37 | with same configuration 38 | */ 39 | void gpio_init(GPIO_TypeDef *addrPort, uint8_t pins, uint8_t config) { 40 | 41 | // set corresponding port config bits 42 | switch (config) { 43 | 44 | case INPUT_FLOAT_NOEXINT: 45 | addrPort->DDR.byte &= (uint8_t) (~pins); // input(=0) or output(=1) 46 | addrPort->CR1.byte &= (uint8_t) (~pins); // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 47 | addrPort->CR2.byte &= (uint8_t) (~pins); // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 48 | break; 49 | 50 | case INPUT_FLOAT_EXINT: 51 | addrPort->DDR.byte &= (uint8_t) (~pins); // input(=0) or output(=1) 52 | addrPort->CR1.byte &= (uint8_t) (~pins); // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 53 | addrPort->CR2.byte |= (uint8_t) pins; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 54 | break; 55 | 56 | case INPUT_PULLUP_NOEXINT: 57 | addrPort->DDR.byte &= (uint8_t) (~pins); // input(=0) or output(=1) 58 | addrPort->CR1.byte |= (uint8_t) pins; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 59 | addrPort->CR2.byte &= (uint8_t) (~pins); // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 60 | break; 61 | 62 | case INPUT_PULLUP_EXINT: 63 | addrPort->DDR.byte &= (uint8_t) (~pins); // input(=0) or output(=1) 64 | addrPort->CR1.byte |= (uint8_t) pins; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 65 | addrPort->CR2.byte |= (uint8_t) pins; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 66 | break; 67 | 68 | case OUTPUT_OPENDRAIN_SLOW: 69 | addrPort->DDR.byte |= (uint8_t) pins; // input(=0) or output(=1) 70 | addrPort->CR1.byte &= (uint8_t) (~pins); // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 71 | addrPort->CR2.byte &= (uint8_t) (~pins); // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 72 | break; 73 | 74 | case OUTPUT_OPENDRAIN_FAST: 75 | addrPort->DDR.byte |= (uint8_t) pins; // input(=0) or output(=1) 76 | addrPort->CR1.byte &= (uint8_t) (~pins); // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 77 | addrPort->CR2.byte |= (uint8_t) pins; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 78 | break; 79 | 80 | case OUTPUT_PUSHPULL_SLOW: 81 | addrPort->DDR.byte |= (uint8_t) pins; // input(=0) or output(=1) 82 | addrPort->CR1.byte |= (uint8_t) pins; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 83 | addrPort->CR2.byte &= (uint8_t) (~pins); // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 84 | break; 85 | 86 | case OUTPUT_PUSHPULL_FAST: 87 | addrPort->DDR.byte |= (uint8_t) pins; // input(=0) or output(=1) 88 | addrPort->CR1.byte |= (uint8_t) pins; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 89 | addrPort->CR2.byte |= (uint8_t) pins; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 90 | break; 91 | 92 | } // switch (config) 93 | 94 | } // gpio_init 95 | 96 | 97 | /*----------------------------------------------------------------------------- 98 | END OF MODULE 99 | -----------------------------------------------------------------------------*/ 100 | -------------------------------------------------------------------------------- /STM8_Lib/gpio.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file gpio.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of functions/macros for GPIO control 9 | 10 | declaration of functions/macros for port pin control 11 | For speed use macros instead of functions where reasonable 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | MODULE DEFINITION FOR MULTIPLE INCLUSION 16 | -----------------------------------------------------------------------------*/ 17 | #ifndef _GPIO_H_ 18 | #define _GPIO_H_ 19 | 20 | /*----------------------------------------------------------------------------- 21 | INCLUDE FILES 22 | -----------------------------------------------------------------------------*/ 23 | 24 | #include 25 | #include "stm8as.h" 26 | 27 | 28 | /*----------------------------------------------------------------------------- 29 | DECLARATION OF GLOBAL MACROS 30 | -----------------------------------------------------------------------------*/ 31 | 32 | // port control structs (PORT_x) see stm8as.h 33 | 34 | // bitmasks for port pins 35 | #define PIN_0 0x01 ///< bitmask for port pin 0 36 | #define PIN_1 0x02 ///< bitmask for port pin 1 37 | #define PIN_2 0x04 ///< bitmask for port pin 2 38 | #define PIN_3 0x08 ///< bitmask for port pin 3 39 | #define PIN_4 0x10 ///< bitmask for port pin 4 40 | #define PIN_5 0x20 ///< bitmask for port pin 5 41 | #define PIN_6 0x40 ///< bitmask for port pin 6 42 | #define PIN_7 0x80 ///< bitmask for port pin 7 43 | 44 | // define states for GPIOs for config_gpio() 45 | #define INPUT_FLOAT_NOEXINT 0 ///< configure pin as: input, float, no exint 46 | #define INPUT_FLOAT_EXINT 1 ///< configure pin as: input, float, with exint 47 | #define INPUT_PULLUP_NOEXINT 2 ///< configure pin as: input, pull-up, no exint 48 | #define INPUT_PULLUP_EXINT 3 ///< configure pin as: input, pull-up, with exint 49 | #define OUTPUT_OPENDRAIN_SLOW 4 ///< configure pin as: output, open-drain, slow (2MHz) 50 | #define OUTPUT_OPENDRAIN_FAST 5 ///< configure pin as: output, open-drain, fast (10MHz) 51 | #define OUTPUT_PUSHPULL_SLOW 6 ///< configure pin as: output, push-pull, slow (2MHz) 52 | #define OUTPUT_PUSHPULL_FAST 7 ///< configure pin as: output, push-pull, fast (10MHz) 53 | 54 | // useful macros for accessing port pin (alternatively access bit Py.ODR.bit.by or Py.IDR.bit.by directly, see stm8as.h) 55 | #define GPIO_HIGH(port,pins) (port.ODR.byte |= (uint8_t) pins) ///< set port pin(s) to high (alternative: Py.ODR.bit.by=1, see stm8as.h) 56 | #define GPIO_LOW(port,pins) (port.ODR.byte &= (uint8_t) (~pins)) ///< set port pin(s) to low (alternative: Py.ODR.bit.by=0, see stm8as.h) 57 | #define GPIO_TOGGLE(port,pins) (port.ODR.byte ^= pins) ///< toggle port pin(s) (alternative: Py.ODR.bit.by^=1, see stm8as.h) 58 | #define GPIO_SET(port,pins,state) ( state ? GPIO_HIGH(port,pins) : GPIO_LOW(port,pins)) ///< set port pin(s) to state (alternative: Py.ODR.bit.by=state, see stm8as.h) 59 | #define GPIO_READ(port,pins) (uint8_t)(port.IDR.byte & pins) ///< read state of port pin(s); is =1 if any masked pin is high (alternative: a=Py.IDR.bit.by, see stm8as.h) 60 | 61 | 62 | /*----------------------------------------------------------------------------- 63 | DECLARATION OF GLOBAL FUNCTIONS 64 | -----------------------------------------------------------------------------*/ 65 | 66 | /// configure GPIO pin 67 | void gpio_init(GPIO_TypeDef *addrPort, uint8_t pins, uint8_t config); 68 | 69 | 70 | /*----------------------------------------------------------------------------- 71 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 72 | -----------------------------------------------------------------------------*/ 73 | #endif // _GPIO_H_ 74 | -------------------------------------------------------------------------------- /STM8_Lib/i2c.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file i2c.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of I2C functions/macros 9 | 10 | implementation of functions for I2C bus communication 11 | For I2C bus, see http://en.wikipedia.org/wiki/I2C 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | INCLUDE FILES 16 | -----------------------------------------------------------------------------*/ 17 | #include 18 | #include "stm8as.h" 19 | #include "gpio.h" 20 | #include "i2c.h" 21 | #include "timer3.h" 22 | #include "error_codes.h" 23 | 24 | 25 | /*---------------------------------------------------------- 26 | FUNCTIONS 27 | ----------------------------------------------------------*/ 28 | 29 | /** 30 | \fn void i2c_init(void) 31 | 32 | \brief configure I2C bus 33 | 34 | configure I2C bus as master in standard mode, BR=100kHz, 7bit address, 250ns rise time 35 | 36 | */ 37 | void i2c_init() { 38 | 39 | // configure I2C pins PE1(=SCL) and PE2(=SDA) 40 | gpio_init(&PORT_E, PIN_1 | PIN_2, OUTPUT_OPENDRAIN_FAST); 41 | 42 | // init I2C bus 43 | I2C.CR1.byte = 0x00; // disable I2C, no clock stretching 44 | I2C.CR2.byte = 0x00; // reset I2C bus 45 | I2C.CR2.reg.ACK = 1; // enable ACK on address match 46 | I2C.FREQR.reg.FREQ = 16; // peripheral clock = 16MHz (f=val*1MHz) 47 | I2C.OARH.reg.ADDCONF = 1; // set 7b addressing mode 48 | I2C.OARL.reg.ADD = 0x04; // set own 7b addr to 0x04 49 | I2C.SR1.byte = 0x00; // clear status registers 50 | I2C.SR2.byte = 0x00; 51 | I2C.SR3.byte = 0x00; 52 | I2C.CCRL.reg.CCR = 0x50; // BR = 100kBaud (t_low = t_high = 80/16MHz) 53 | I2C.CCRH.byte = 0x00; // I2C standard mode 54 | I2C.TRISER.reg.TRISE = 0x04; // t_rise<250ns (<300ns for display driver) 55 | I2C.CR1.reg.PE = 1; // enable I2C module with broadcast receive disabled 56 | 57 | } // i2c_init 58 | 59 | 60 | 61 | /** 62 | \fn uint8_t i2c_start(void) 63 | 64 | \brief generate I2C start condition 65 | 66 | \return error code 67 | 68 | generate I2C start condition with 1ms timeout 69 | 70 | */ 71 | uint8_t i2c_start() { 72 | 73 | // start overall timeout 74 | start_timeout_ms(1); 75 | 76 | // generate start condition 77 | I2C.CR2.reg.START = 1; 78 | while ((!I2C.SR1.reg.SB) && (!check_timeout())); // wait for start condition generated or timeout 79 | 80 | // on I2C timeout set error flag 81 | if (check_timeout()) { 82 | stop_timeout_ms(); 83 | return(ERROR_TIMOUT); 84 | } 85 | 86 | // reset timeout timer 87 | stop_timeout_ms(); 88 | 89 | return(SUCCESS); 90 | 91 | } // i2c_start 92 | 93 | 94 | 95 | /** 96 | \fn uint8_t i2c_stop(void) 97 | 98 | \brief generate I2C stop condition 99 | 100 | \return operation successful? 101 | 102 | generate I2C stop condition with 1ms timeout 103 | 104 | */ 105 | uint8_t i2c_stop() { 106 | 107 | // start overall timeout 108 | start_timeout_ms(1); 109 | 110 | // generate stop condition 111 | I2C.CR2.reg.STOP = 1; 112 | while ((I2C.SR3.reg.MSL) && (!check_timeout())); // wait for stop condition generated or timeout 113 | 114 | // on I2C timeout set error flag 115 | if (check_timeout()) { 116 | stop_timeout_ms(); 117 | return(ERROR_TIMOUT); 118 | } 119 | 120 | // reset timeout timer 121 | stop_timeout_ms(); 122 | 123 | return(SUCCESS); 124 | 125 | } // i2c_stop 126 | 127 | 128 | 129 | /** 130 | \fn uint8_t i2c_send(uint8_t addr, uint8_t numTx, uint8_t *bufTx) 131 | 132 | \brief write data via I2C 133 | 134 | \param[in] addr 7b address [6:0] of I2C slave 135 | \param[in] numTx number of bytes to send 136 | \param[in] bufTx send buffer 137 | 138 | \return operation successful? 139 | 140 | write data via I2C with 5ms frame timeout. Note that no start or 141 | stop condition is generated. 142 | 143 | */ 144 | uint8_t i2c_send(uint8_t addr, uint8_t numTx, uint8_t *bufTx) { 145 | 146 | uint8_t i; 147 | 148 | // start overall timeout 149 | start_timeout_ms(5); 150 | 151 | // send 7b slave adress [7:1] + write flag (LSB=0) 152 | I2C.DR.byte = (uint8_t) ((addr << 1) & ~0x01); // shift left and set LSB (write=0, read=1) 153 | while ((!I2C.SR1.reg.ADDR) && (!check_timeout())); // wait until address sent or timeout 154 | while ((!I2C.SR3.reg.BUSY) && (!check_timeout())); // seems to be required...??? 155 | 156 | // send data 157 | if (!check_timeout()) { 158 | 159 | for (i=0; i 21 | #include "stm8as.h" 22 | 23 | 24 | /*----------------------------------------------------------------------------- 25 | DEFINITION OF GLOBAL MACROS/#DEFINES 26 | -----------------------------------------------------------------------------*/ 27 | 28 | /// check I2C busy flag 29 | #define I2C_BUSY (I2C.SR3.reg.BUSY) 30 | 31 | 32 | /*----------------------------------------------------------------------------- 33 | DECLARATION OF GLOBAL FUNCTIONS 34 | -----------------------------------------------------------------------------*/ 35 | 36 | /// configure I2C bus as master in standard mode 37 | void i2c_init(void); 38 | 39 | /// generate I2C start condition 40 | uint8_t i2c_start(void); 41 | 42 | /// generate I2C stop condition 43 | uint8_t i2c_stop(void); 44 | 45 | /// write data via I2C 46 | uint8_t i2c_send(uint8_t addr, uint8_t numTx, uint8_t *Tx); 47 | 48 | /// request data via I2C as master 49 | uint8_t i2c_request(uint8_t addr, uint8_t numRx, uint8_t *Rx); 50 | 51 | /// ISR for I2C bus (required for slave operation) 52 | #if defined(__CSMC__) 53 | @near @interrupt void i2c_ISR(void); 54 | #elif defined(__SDCC) 55 | void i2c_ISR(void) __interrupt(__I2C_VECTOR__); 56 | #endif 57 | 58 | 59 | /*----------------------------------------------------------------------------- 60 | DEFINITION OF GLOBAL MACROS/#DEFINES 61 | -----------------------------------------------------------------------------*/ 62 | 63 | /*----------------------------------------------------------------------------- 64 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 65 | -----------------------------------------------------------------------------*/ 66 | #endif // _I2C_H_ 67 | -------------------------------------------------------------------------------- /STM8_Lib/i2c_lcd.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file i2c_lcd.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of 2x16 LCD output functions/macros 9 | 10 | implementation of functions for printing strings via I2C to 2x16 LCD 11 | Batron BTHQ21605V-COG-FSRE-I2C (Farnell 1220409). 12 | Connect LCD I2C bus to STM8 SCL/SDA, and LCD reset pin to STM8 pin PE3 13 | */ 14 | 15 | /*----------------------------------------------------------------------------- 16 | INCLUDE FILES 17 | -----------------------------------------------------------------------------*/ 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include "stm8as.h" 24 | #include "gpio.h" 25 | #include "timer3.h" 26 | #include "i2c.h" 27 | #include "i2c_lcd.h" 28 | #include "error_codes.h" 29 | 30 | 31 | /*---------------------------------------------------------- 32 | FUNCTIONS 33 | ----------------------------------------------------------*/ 34 | 35 | /** 36 | \fn uint8_t lcd_init(void) 37 | 38 | \brief initialize LCD for output 39 | 40 | \return is an LCD attached? 41 | 42 | initialize LCD for LCD output. 43 | Also check if LCD display is attached via bus timeout 44 | */ 45 | uint8_t lcd_init() { 46 | 47 | uint8_t status; 48 | 49 | 50 | // configure PE3 as reset pin (active high) 51 | gpio_init(&PORT_E, PIN_3, OUTPUT_PUSHPULL_SLOW); 52 | 53 | // reset LCD display 54 | sleep_ms(10); 55 | GPIO_HIGH(PORT_E, PIN_3); 56 | sleep_ms(10); 57 | GPIO_LOW(PORT_E, PIN_3); 58 | sleep_ms(10); 59 | 60 | 61 | //// 62 | // check if LCD present by sending a dummy frame 63 | //// 64 | 65 | // generate start condition 66 | i2c_start(); 67 | 68 | // send dummy frame (with timeout) 69 | status = (uint8_t) (i2c_send(LCD_ADDR_I2C, 0, NULL)); 70 | 71 | // generate stop condition 72 | i2c_stop(); 73 | 74 | 75 | // if LCD is present clear it 76 | if (status == SUCCESS) 77 | lcd_clear(); 78 | 79 | // return LCD status 80 | return(status); 81 | 82 | } // lcd_init 83 | 84 | 85 | 86 | /** 87 | \fn void clear_lcd(void) 88 | 89 | \brief clear LCD display 90 | 91 | clear both lines of LCD display 92 | */ 93 | void lcd_clear() { 94 | 95 | uint8_t str[1]; 96 | 97 | // print empty lines to both lines clears LCD 98 | sprintf(str, ""); 99 | lcd_print(1, 1, str); 100 | lcd_print(2, 1, str); 101 | 102 | } // lcd_clear 103 | 104 | 105 | 106 | /** 107 | \fn void lcd_print(uint8_t line, uint8_t col, char *s) 108 | 109 | \brief print to LCD display 110 | 111 | \param line line to print to (1 or 2) 112 | \param col column to start at 113 | \param s string to print 114 | 115 | print up to 16 chars to line in LCD display 116 | 117 | */ 118 | void lcd_print(uint8_t line, uint8_t col, char *s) { 119 | 120 | uint8_t s2[21]; 121 | uint8_t len; 122 | uint8_t i, j; 123 | 124 | 125 | ////// 126 | // config display 127 | ////// 128 | i = 0; 129 | s2[i++] = 0x00; // control byte 'config mode' 130 | s2[i++] = 0x34; // command 'function set' 131 | s2[i++] = 0x0C; // command 'display on' 132 | s2[i++] = 0x06; // command 'entry mode' 133 | if (line == 1) 134 | s2[i++] = (uint8_t)(0x80 + col - 1); // DRAM adress offset for line 1 + column 135 | else 136 | s2[i++] = (uint8_t)(0xC0 + col - 1); // DRAM adress offset for line 2 137 | 138 | 139 | // wait until bus is free (with timeout) 140 | start_timeout_ms(100); 141 | while ((I2C_BUSY) && (!check_timeout())); 142 | 143 | // generate start condition 144 | i2c_start(); 145 | 146 | // send data (with timeout) 147 | i2c_send(LCD_ADDR_I2C, i, s2); 148 | 149 | 150 | ////// 151 | // send string 152 | ////// 153 | 154 | // init array to SPC 155 | for (i=0; i<20; i++) 156 | s2[i] = 0xA0; 157 | 158 | i = 0; 159 | s2[i++] = 0x40; // control byte 'write data' 160 | 161 | // copy string 162 | len = (uint8_t) strlen(s); 163 | for (j=0; (j 27 | 28 | // I2C addresses of LCD display 29 | #define LCD_ADDR_I2C 59 30 | 31 | 32 | /*----------------------------------------------------------------------------- 33 | DECLARATION OF GLOBAL FUNCTIONS 34 | -----------------------------------------------------------------------------*/ 35 | 36 | /// initialize I2C, and reset LCD 37 | uint8_t lcd_init(void); 38 | 39 | /// clear LCD 40 | void lcd_clear(void); 41 | 42 | /// print string to LCD diplay 43 | void lcd_print(uint8_t line, uint8_t col, char *s); 44 | 45 | 46 | /*----------------------------------------------------------------------------- 47 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 48 | -----------------------------------------------------------------------------*/ 49 | #endif // _I2C_LCD_H_ 50 | -------------------------------------------------------------------------------- /STM8_Lib/i2c_poti.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file i2c_poti.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of I2C digital potentiometer functions/macros 9 | 10 | implementation of functions for controlling a 20kR / 8-bit digital 11 | potentiometer AD5280BRUZ20 (Farnell 1438441). 12 | Connect poti I2C bus to STM8 SCL/SDA 13 | */ 14 | 15 | /*----------------------------------------------------------------------------- 16 | INCLUDE FILES 17 | -----------------------------------------------------------------------------*/ 18 | #include 19 | #include 20 | 21 | #include "stm8as.h" 22 | #include "timer3.h" 23 | #include "i2c.h" 24 | #include "i2c_poti.h" 25 | #include "error_codes.h" 26 | 27 | 28 | /*---------------------------------------------------------- 29 | FUNCTIONS 30 | ----------------------------------------------------------*/ 31 | 32 | /** 33 | \fn uint8_t set_dig_poti(uint8_t res) 34 | 35 | \brief set resistance of potentiometer 36 | 37 | \param[in] res new resistor value in [78.4R/INC] 38 | 39 | \return operation succeeded? 40 | 41 | set resistor btw terminal A and washer to 20k/255*res (res=[0;255]). 42 | Resistance btw terminal B and washer to 20k/255*(255-res) 43 | */ 44 | uint8_t set_dig_poti(uint8_t res) { 45 | 46 | // I2C Tx buffer 47 | uint8_t s[2]; 48 | 49 | // invert value to have right R(A;washer) behaviour 50 | res = (uint8_t)(255-res); 51 | 52 | // wait until bus is free (with timeout) 53 | start_timeout_ms(100); 54 | while ((I2C_BUSY) && (!check_timeout())); 55 | stop_timeout_ms(); 56 | 57 | // generate start condition 58 | i2c_start(); 59 | 60 | // send data (with timeout) 61 | s[0] = 0x00; 62 | s[1] = res; 63 | i2c_send(ADDR_I2C_POTI, 2, s); 64 | 65 | // generate stop condition 66 | i2c_stop(); 67 | 68 | // wait until bus is free (with timeout) 69 | start_timeout_ms(100); 70 | while ((I2C_BUSY) && (!check_timeout())); 71 | stop_timeout_ms(); 72 | 73 | // on I2C timeout return error code 74 | if (check_timeout()) 75 | return(ERROR_TIMOUT); 76 | 77 | // success 78 | return(SUCCESS); 79 | 80 | } // set_dig_poti 81 | 82 | 83 | /*----------------------------------------------------------------------------- 84 | END OF MODULE 85 | -----------------------------------------------------------------------------*/ 86 | -------------------------------------------------------------------------------- /STM8_Lib/i2c_poti.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file i2c_poti.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of I2C digital potentiometer functions/macros 9 | 10 | declaration of functions for controlling a 20kR / 8-bit digital 11 | potentiometer AD5280BRUZ20 (Farnell 1438441). 12 | Connect poti I2C bus to STM8 SCL/SDA 13 | */ 14 | 15 | /*----------------------------------------------------------------------------- 16 | MODULE DEFINITION FOR MULTIPLE INCLUSION 17 | -----------------------------------------------------------------------------*/ 18 | #ifndef _I2C_POTI_H_ 19 | #define _I2C_POTI_H_ 20 | 21 | 22 | /*----------------------------------------------------------------------------- 23 | DEFINITION OF GLOBAL MACROS/#DEFINES 24 | -----------------------------------------------------------------------------*/ 25 | 26 | #include 27 | 28 | // I2C addresses of digital potentiometer 29 | #define ADDR_I2C_POTI 46 30 | 31 | 32 | /*----------------------------------------------------------------------------- 33 | DECLARATION OF GLOBAL FUNCTIONS 34 | -----------------------------------------------------------------------------*/ 35 | 36 | /// set resistance of potentiometer 37 | uint8_t set_dig_poti(uint8_t res); 38 | 39 | 40 | /*----------------------------------------------------------------------------- 41 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 42 | -----------------------------------------------------------------------------*/ 43 | #endif // _I2C_POTI_H_ 44 | -------------------------------------------------------------------------------- /STM8_Lib/memory_access.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file memory_access.h 3 | 4 | \author G. Icking-Konert 5 | \date 2015-09-08 6 | \version 0.2 7 | 8 | \brief definition of macros and functions for memory r/w access 9 | 10 | macros and inline functions for reading and writing to memory. 11 | Is required due to lack of far pointers in SDCC. 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | MODULE DEFINITION FOR MULTIPLE INCLUSION 16 | -----------------------------------------------------------------------------*/ 17 | #ifndef _MEMORY_ACCESS_H 18 | #define _MEMORY_ACCESS_H 19 | 20 | #include 21 | #include "stm8as.h" 22 | 23 | 24 | /////// 25 | // Cosmic compiler 26 | /////// 27 | #if defined(__CSMC__) 28 | 29 | // read & write data from memory (16-bit address). For size use 16b pointers 30 | #if (ADDR_WIDTH==16) 31 | #define read_1B(addr) (*((uint8_t*) addr)) /**< read 1B from 16-bit address */ 32 | #define read_2B(addr) (*((uint16_t*) addr)) /**< read 2B from 16-bit address */ 33 | #define read_4B(addr) (*((uint32_t*) addr)) /**< read 4B from 16-bit address */ 34 | #define write_1B(addr,val) *((uint8_t*) addr) = val /**< write 1B to 16-bit address */ 35 | #define write_2B(addr,val) *((uint16_t*) addr) = val /**< write 1B to 16-bit address */ 36 | #define write_4B(addr,val) *((uint32_t*) addr) = val /**< write 1B to 16-bit address */ 37 | 38 | // read & write data from memory (24-bit address). Use 24b far pointers 39 | #else 40 | #define read_1B(addr) (*((@far uint8_t*) addr)) /**< read 1B from 24-bit address */ 41 | #define read_2B(addr) (*((@far uint16_t*) addr)) /**< read 2B from 24-bit address */ 42 | #define read_4B(addr) (*((@far uint32_t*) addr)) /**< read 4B from 24-bit address */ 43 | #define write_1B(addr,val) *((@far uint8_t*) addr) = val /**< write 1B to 24-bit address */ 44 | #define write_2B(addr,val) *((@far uint16_t*) addr) = val /**< write 1B to 24-bit address */ 45 | #define write_4B(addr,val) *((@far uint32_t*) addr) = val /**< write 1B to 24-bit address */ 46 | #endif 47 | 48 | 49 | /////// 50 | // SDCC compiler 51 | /////// 52 | #elif defined(__SDCC) 53 | 54 | // read & write data from memory (16-bit address) 55 | #if (ADDR_WIDTH==16) 56 | #define read_1B(addr) (*((uint8_t*) addr)) /**< read 1B from 16-bit address */ 57 | #define read_2B(addr) (*((uint16_t*) addr)) /**< read 2B from 16-bit address */ 58 | #define read_4B(addr) (*((uint32_t*) addr)) /**< read 4B from 16-bit address */ 59 | #define write_1B(addr,val) *((uint8_t*) addr) = val /**< write 1B to 16-bit address */ 60 | #define write_2B(addr,val) *((uint16_t*) addr) = val /**< write 1B to 16-bit address */ 61 | #define write_4B(addr,val) *((uint32_t*) addr) = val /**< write 1B to 16-bit address */ 62 | 63 | // read & write data from memory (24-bit address). SDCC doesn't support far pointers -> use inline assembly 64 | #else 65 | 66 | // global variables for interfacing with SDCC assembler 67 | global uint32_t g_addr; ///< address for interfacing to below assembler 68 | global uint8_t g_valu8; ///< 1B data for r/w via below assembler 69 | global uint32_t g_valu32; ///< 1B data for r/w via below assembler 70 | global uint32_t g_mem_addr; ///< address for interfacing to below assembler 71 | global uint32_t g_mem_val; ///< data for r/w via below assembler 72 | 73 | 74 | /** 75 | \fn uint8_t read_1B(uint32_t addr) 76 | 77 | \brief read 1 byte from memory (inline) 78 | 79 | \param[in] addr address to read from 80 | 81 | \return 1B data read from memory 82 | 83 | Inline function to read 1B from memory. 84 | Required for SDCC and >64kB due to lack of far pointers 85 | */ 86 | inline uint8_t read_1B(uint32_t addr) { 87 | 88 | // pass data between C and assembler via global variables 89 | extern volatile uint32_t g_mem_addr = addr; 90 | extern volatile uint8_t g_mem_val; // use 8b of 32b variable 91 | 92 | // use inline assembler for actual read 93 | ASM_START 94 | ldf a,[_g_mem_addr+1].e 95 | ld _g_mem_val, a 96 | ASM_END 97 | 98 | // return data 99 | return(g_mem_val); 100 | 101 | } // read_1B 102 | 103 | 104 | /** 105 | \fn uint16_t read_2B(uint32_t addr) 106 | 107 | \brief read 2 bytes from memory (inline) 108 | 109 | \param[in] addr address to read from 110 | 111 | \return 2B data read from memory 112 | 113 | Inline function to read 2B from memory. 114 | Required for SDCC and >64kB due to lack of far pointers 115 | */ 116 | inline uint16_t read_2B(uint32_t addr) { 117 | 118 | // pass data between C and assembler via global variables 119 | extern volatile uint32_t g_mem_addr = addr; 120 | extern volatile uint16_t g_mem_val; // use 16b of 32b variable 121 | 122 | // use inline assembler for actual read 123 | ASM_START 124 | ldf a,[_g_mem_addr+1].e 125 | ld _g_mem_val,a 126 | ldw x,#1 127 | ldf a,([_g_mem_addr+1].e,x) 128 | ld _g_mem_val+1,a 129 | ASM_END 130 | 131 | // return data 132 | return(g_mem_val); 133 | 134 | } // read_2B 135 | 136 | 137 | /** 138 | \fn uint16_t read_4B(uint32_t addr) 139 | 140 | \brief read 4 bytes from memory (inline) 141 | 142 | \param[in] addr address to read from 143 | 144 | \return 4B data read from memory 145 | 146 | Inline function to read 4B from memory. 147 | Required for SDCC and >64kB due to lack of far pointers 148 | */ 149 | inline uint32_t read_4B(uint32_t addr) { 150 | 151 | // pass data between C and assembler via global variables 152 | extern volatile uint32_t g_mem_addr = addr; 153 | extern volatile uint32_t g_mem_val; 154 | 155 | // use inline assembler for actual read 156 | ASM_START 157 | ldf a,[_g_mem_addr+1].e 158 | ld _g_mem_val,a 159 | ldw x,#1 160 | ldf a,([_g_mem_addr+1].e,x) 161 | ld _g_mem_val+1,a 162 | ldw x,#2 163 | ldf a,([_g_mem_addr+1].e,x) 164 | ld _g_mem_val+2,a 165 | ldw x,#3 166 | ldf a,([_g_mem_addr+1].e,x) 167 | ld _g_mem_val+3,a 168 | ASM_END 169 | 170 | // return data 171 | return(g_mem_val); 172 | 173 | } // read_4B 174 | 175 | 176 | /** 177 | \fn void write_1B(uint32_t addr, uint8_t val) 178 | 179 | \brief write 1 byte to memory (inline) 180 | 181 | \param[in] addr address to read from 182 | \param[in] val data to write 183 | 184 | Inline function to write 1B to memory. 185 | Required for SDCC and >64kB due to lack of far pointers 186 | */ 187 | inline void write_1B(uint32_t addr, uint8_t val) { 188 | 189 | // pass data between C and assembler via global variables 190 | extern volatile uint32_t g_mem_addr = addr; 191 | extern volatile uint8_t g_mem_val = val; // use 8b of 32b variable 192 | 193 | // use inline assembler for actual write 194 | ASM_START 195 | ld a,_g_mem_val 196 | ldf [_g_mem_addr+1].e,a 197 | ASM_END 198 | 199 | } // write_1B 200 | 201 | 202 | /** 203 | \fn void write_2B(uint32_t addr, uint16_t val) 204 | 205 | \brief write 2 bytes to memory (inline) 206 | 207 | \param[in] addr address to read from 208 | \param[in] val data to write 209 | 210 | Inline function to write 2B to memory. 211 | Required for SDCC and >64kB due to lack of far pointers 212 | */ 213 | inline void write_2B(uint32_t addr, uint16_t val) { 214 | 215 | // pass data between C and assembler via global variables 216 | extern volatile uint32_t g_mem_addr = addr; 217 | extern volatile uint16_t g_mem_val = val; // use 16b of 32b variable 218 | 219 | // use inline assembler for actual write 220 | ASM_START 221 | ld a,_g_mem_val 222 | ldf [_g_mem_addr+1].e,a 223 | ld a,_g_mem_val+1 224 | ldw x,#1 225 | ldf ([_g_mem_addr+1].e,x),a 226 | ASM_END 227 | 228 | } // write_2B 229 | 230 | 231 | /** 232 | \fn void write_2B(uint32_t addr, uint32_t val) 233 | 234 | \brief write 4 bytes to memory (inline) 235 | 236 | \param[in] addr address to read from 237 | \param[in] val data to write 238 | 239 | Inline function to write 4B to memory. 240 | Required for SDCC and >64kB due to lack of far pointers 241 | */ 242 | inline void write_4B(uint32_t addr, uint32_t val) { 243 | 244 | // pass data between C and assembler via global variables 245 | extern volatile uint32_t g_mem_addr = addr; 246 | extern volatile uint32_t g_mem_val = val; 247 | 248 | // use inline assembler for actual write 249 | ASM_START 250 | ld a,_g_mem_val 251 | ldf [_g_mem_addr+1].e,a 252 | ld a,_g_mem_val+1 253 | ldw x,#1 254 | ldf ([_g_mem_addr+1].e,x),a 255 | ld a,_g_mem_val+2 256 | ldw x,#2 257 | ldf ([_g_mem_addr+1].e,x),a 258 | ld a,_g_mem_val+3 259 | ldw x,#3 260 | ldf ([_g_mem_addr+1].e,x),a 261 | ASM_END 262 | 263 | } // write_4B 264 | 265 | #endif // (ADDR_WIDTH==32) 266 | 267 | // compiler unknown 268 | #else 269 | #error in 'stm8.h': compiler not supported 270 | #endif 271 | 272 | 273 | /*----------------------------------------------------------------------------- 274 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 275 | -----------------------------------------------------------------------------*/ 276 | #endif // _MEMORY_ACCESS_H 277 | -------------------------------------------------------------------------------- /STM8_Lib/misc.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file misc.c 3 | 4 | \author G. Icking-Konert 5 | \date 2008-11-02 6 | \version 0.1 7 | 8 | \brief implementation of misc macros & routines 9 | 10 | implementation of macros & routines not really fitting anywhere else 11 | 12 | */ 13 | 14 | ///////////// 15 | // headers 16 | ///////////// 17 | 18 | #include 19 | #include 20 | #include "misc.h" 21 | 22 | 23 | /** 24 | \fn uint16_t concat_u16(uint8_t *buf) 25 | 26 | \brief concatenate 2*8b buffer -> uint16_t 27 | 28 | \param[in] buf buffer containing data (Rx[0]=MSB; Rx[1]=LSB) 29 | 30 | \return resulting unsigned short value 31 | 32 | concatenate 2 unsigned chars from buffer to one value of type uint16_t 33 | */ 34 | uint16_t concat_u16(uint8_t *buf) { 35 | 36 | uint16_t value; 37 | 38 | // use endian independent (manual) conversion 39 | value = (((uint16_t) buf[0]) << 8) | ((uint16_t) buf[1]); 40 | 41 | return(value); 42 | 43 | } // concat_u16 44 | 45 | 46 | 47 | /** 48 | \fn uint16_t concat2_u16(uint8_t hb, uint8_t lb) 49 | 50 | \brief concatenate 2*8b args -> uint16_t 51 | 52 | \param[in] hb high byte 53 | \param[in] lb low byte 54 | 55 | \return resulting unsigned short value 56 | 57 | concatenate 2 unsigned chars from arguments to one value of type uint16_t 58 | */ 59 | uint16_t concat2_u16(uint8_t hb, uint8_t lb) { 60 | 61 | volatile uint16_t result; 62 | 63 | result = (((uint16_t)hb) << 8) | (((uint16_t) lb) & 0x00FF); 64 | return(result); 65 | 66 | } // concat2_u16 67 | 68 | 69 | 70 | /** 71 | \fn int16_t concat_s16(uint8_t *buf) 72 | 73 | \brief concatenate 2*8b buffer -> int16_t 74 | 75 | \param[in] buf buffer containing data (Rx[0]=MSB; Rx[1]=LSB) 76 | 77 | \return resulting signed short value 78 | 79 | concatenate 2 unsigned chars from buffer to one value of type int16_t 80 | */ 81 | int16_t concat_s16(uint8_t *buf) { 82 | 83 | int16_t value; 84 | 85 | // use endian independent (manual) conversion 86 | value = (((int16_t) buf[0]) << 8) | ((int16_t) buf[1]); 87 | 88 | return(value); 89 | 90 | } // concat_u16 91 | 92 | 93 | 94 | /** 95 | \fn uint32_t concat_u32(uint8_t *buf) 96 | 97 | \brief concatenate 4*B -> uint32_t 98 | 99 | \param[in] buf buffer containing data (Rx[0]=MSB; Rx[3]=LSB) 100 | 101 | \return resulting unsigned 32b int value 102 | 103 | concatenate 4 unsigned chars to one value of type uint32_t 104 | */ 105 | uint32_t concat_u32(uint8_t *buf) { 106 | 107 | uint32_t value; 108 | 109 | // use endian independent (manual) conversion 110 | value = (((uint32_t) buf[0]) << 24) | (((uint32_t) buf[1]) << 16) | (((uint32_t) buf[2]) << 8) | ((uint32_t) buf[3]); 111 | 112 | return(value); 113 | 114 | } // concat_u32 115 | 116 | 117 | 118 | 119 | /** 120 | \fn int32_t concat_s32(uint8_t *buf) 121 | 122 | \brief concatenate 4*B -> int32_t 123 | 124 | \param[in] buf buffer containing data (Rx[0]=MSB; Rx[3]=LSB) 125 | 126 | \return resulting signed 32b int value 127 | 128 | concatenate 4 unsigned chars to one value of type int32_t 129 | */ 130 | int32_t concat_s32(uint8_t *buf) { 131 | 132 | int32_t value; 133 | 134 | // use endian independent (manual) conversion 135 | value = (((int32_t) buf[0]) << 24) | (((int32_t) buf[1]) << 16) | (((int32_t) buf[2]) << 8) | ((int32_t) buf[3]); 136 | 137 | return(value); 138 | 139 | } // concat_s32 140 | 141 | 142 | 143 | /** 144 | \fn uint8_t get_byte_u16(uint16_t value, uint8_t idx) 145 | 146 | \brief get byte idx out of u16b argument (0->LSB) 147 | 148 | \param[in] value input value 149 | \param[in] idx index [1..size] of byte to get 150 | 151 | \return byte idx of input value 152 | 153 | get byte idx out of u16b argument (0->LSB) 154 | */ 155 | uint8_t get_byte_u16(uint16_t value, uint8_t idx) { 156 | 157 | // use endian independent (manual) conversion 158 | return((uint8_t) (value >> (8 * idx))); 159 | 160 | } // get_byte_u16 161 | 162 | 163 | 164 | /** 165 | \fn uint8_t get_byte_s16(int16_t value, uint8_t idx) 166 | 167 | \brief get byte idx out of s16b argument (0->LSB) 168 | 169 | \param[in] value input value 170 | \param[in] idx index [1..size] of byte to get 171 | 172 | \return byte idx of input value 173 | 174 | get byte idx out of s16b argument (0->LSB) 175 | */ 176 | uint8_t get_byte_s16(int16_t value, uint8_t idx) { 177 | 178 | // use endian independent (manual) conversion 179 | return((uint8_t) (value >> (8 * idx))); 180 | 181 | } // get_byte_s16 182 | 183 | 184 | 185 | /** 186 | \fn uint8_t get_byte_u32(uint32_t value, uint8_t idx) 187 | 188 | \brief get byte idx out of 32b argument (0->LSB) 189 | 190 | \param[in] value input value 191 | \param[in] idx index [1..size] of byte to get 192 | 193 | \return byte idx of input value 194 | 195 | get byte idx out of 32b argument (0->LSB) 196 | */ 197 | uint8_t get_byte_u32(uint32_t value, uint8_t idx) { 198 | 199 | // use endian independent (manual) conversion 200 | return((uint8_t) (value >> (8 * idx))); 201 | 202 | } // get_byte_u32 203 | 204 | 205 | 206 | /** 207 | \fn uint8_t get_byte_s32(int32_t value, uint8_t idx) 208 | 209 | \brief get byte idx out of 32b argument (0->LSB) 210 | 211 | \param[in] value input value 212 | \param[in] idx index [1..size] of byte to get 213 | 214 | \return byte idx of input value 215 | 216 | get byte idx out of 32b argument (0->LSB) 217 | */ 218 | uint8_t get_byte_s32(int32_t value, uint8_t idx) { 219 | 220 | // use endian independent (manual) conversion 221 | return((uint8_t) (value >> (8 * idx))); 222 | 223 | } // get_byte_s32 224 | 225 | 226 | 227 | /** 228 | \fn uint16_t log2(float arg) 229 | 230 | \brief simple calculation of log2(x) 231 | 232 | \param[in] arg log2 argument. Has to be positive. 233 | 234 | \return result of log2(arg) 235 | 236 | simple calculation of log2(x) in O(log(N)). Basically 237 | compare argument vs. increasing 1<> bit) ///< read single bit from data 30 | #define set_bit(byte, bit) byte |= (1 << bit) ///< set single bit in data to '1' 31 | #define clear_bit(byte, bit) byte &= ~(1 << bit) ///< clear single bit in data to '0' 32 | #define write_bit(byte, bit, state) (state?(byte|=(1< b) ? (a) : (b)) 41 | #define ROUND(a) ((uint32_t)((float) a + 0.5001)) 42 | #define CEIL(a) ((uint32_t)((float) a + 0.9)) 43 | 44 | 45 | /*----------------------------------------------------------------------------- 46 | GLOBAL FUNCTIONS 47 | -----------------------------------------------------------------------------*/ 48 | 49 | /// concatenate 2*8b buffer -> uint16_t (MSB first) 50 | uint16_t concat_u16(uint8_t *buf); 51 | 52 | /// concatenate 2*8b args -> uint16_t (MSB first) 53 | uint16_t concat2_u16(uint8_t hb, uint8_t lb); 54 | 55 | /// concatenate 2*8b buffer -> int16_t (MSB first) 56 | int16_t concat_s16(uint8_t *buf); 57 | 58 | /// concatenate 4*8b buffer -> uint32_t (MSB first) 59 | uint32_t concat_u32(uint8_t *buf); 60 | 61 | /// concatenate 4*8b buffer -> uint32_t (MSB first) 62 | int32_t concat_s32(uint8_t *buf); 63 | 64 | /// get byte idx out of u16b argument (0=LSB) 65 | uint8_t get_byte_u16(uint16_t value, uint8_t idx); 66 | 67 | /// get byte idx out of s16b argument (0=LSB) 68 | uint8_t get_byte_s16(int16_t value, uint8_t idx); 69 | 70 | /// get byte idx out of u32b argument (0=LSB) 71 | uint8_t get_byte_u32(uint32_t value, uint8_t idx); 72 | 73 | /// get byte idx out of s32b argument (0=LSB) 74 | uint8_t get_byte_s32(int32_t value, uint8_t idx); 75 | 76 | /// simple calculation of log2(x); missing in Cosmic & SDCC math lib 77 | uint16_t log2(float arg); 78 | 79 | 80 | /*----------------------------------------------------------------------------- 81 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 82 | -----------------------------------------------------------------------------*/ 83 | #endif // _MISC_H_ 84 | -------------------------------------------------------------------------------- /STM8_Lib/putchar.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file putchar.c 3 | 4 | \author G. Icking-Konert 5 | \date 2015-04-09 6 | \version 0.1 7 | 8 | \brief implementation of required putchar() function 9 | 10 | declaration of putchar() function required for stdio.h 11 | functions, e.g. printf(). 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | INCLUDE FILES 16 | -----------------------------------------------------------------------------*/ 17 | #include 18 | #include "uart1.h" 19 | 20 | 21 | /*---------------------------------------------------------- 22 | FUNCTIONS 23 | ----------------------------------------------------------*/ 24 | 25 | /** 26 | \fn void putchar(char byte) 27 | 28 | \brief send byte via UART1 29 | 30 | \param[in] byte data to send 31 | 32 | \return always zero (SDCC only) 33 | 34 | implementation of putchar() for printf(), using selected output channel 35 | Return type depends on used compiler (see respective stdio.h) 36 | */ 37 | #if defined(__CSMC__) 38 | char putchar(char byte) { 39 | #elif defined(__SDCC) 40 | int putchar(int byte) { 41 | #else 42 | int putchar(char byte) { 43 | #endif 44 | 45 | // use USART1 send routine 46 | if (byte=='\n') { 47 | uart1_send_byte(13); 48 | uart1_send_byte(10); 49 | } 50 | else 51 | uart1_send_byte(byte); 52 | 53 | // avoid warning message 54 | return(0); 55 | 56 | } // putchar 57 | 58 | 59 | /*----------------------------------------------------------------------------- 60 | END OF MODULE 61 | -----------------------------------------------------------------------------*/ 62 | -------------------------------------------------------------------------------- /STM8_Lib/putchar.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file putchar.h 3 | 4 | \author G. Icking-Konert 5 | \date 2015-04-09 6 | \version 0.1 7 | 8 | \brief declaration of required putchar() function 9 | 10 | declaration of putchar() function required for stdio.h 11 | functions, e.g. printf(). Is dummy (required by Makefile) 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | INCLUDE FILES 16 | -----------------------------------------------------------------------------*/ 17 | #include 18 | 19 | 20 | /*----------------------------------------------------------------------------- 21 | END OF MODULE 22 | -----------------------------------------------------------------------------*/ 23 | -------------------------------------------------------------------------------- /STM8_Lib/spi.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file spi.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of SPI functions/macros 9 | 10 | implementation of functions for SPI communication 11 | For SPI bus, see https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | INCLUDE FILES 16 | -----------------------------------------------------------------------------*/ 17 | #include 18 | #include "stm8as.h" 19 | #include "spi.h" 20 | 21 | 22 | /*---------------------------------------------------------- 23 | FUNCTIONS 24 | ----------------------------------------------------------*/ 25 | 26 | /** 27 | \fn void spi_config(uint8_t type, uint16_t timeout, uint8_t plex, uint8_t pre, uint8_t order, uint8_t phase, uint8_t idle) 28 | 29 | \brief configure SPI interface in master mode 30 | 31 | \param[in] type SPI master(=1) or slave(=0) 32 | \param[in] timeout frame timeout [ms] for slave mode (or none if 0) 33 | \param[in] plex SPI type: 4-wire full-duplex(=0) or 3-wire half duplex(=1) 34 | \param[in] pre baudrate prescaler (BR=16MHz/2^(pre+1)) 35 | \param[in] order bit order: MSB(=0) or LSB(=1) first 36 | \param[in] phase clock phase (uC samples on falling(=0) or rising(=1) edge) 37 | \param[in] idle idle clock polarity low(=0) or high(=1) 38 | 39 | configure SPI interface in master mode for 3- or 4-wire communication 40 | 41 | */ 42 | void spi_config(uint8_t type, uint16_t timeout, uint8_t plex, uint8_t pre, uint8_t order, uint8_t phase, uint8_t idle) { 43 | 44 | // disable SPI module 45 | SPI_CR1.reg.SPE = 0; 46 | 47 | // SPI master mode 48 | if (type==PRM_SPI_MASTER) { 49 | 50 | // configure SPI for master mode 51 | SPI_CR2.reg.SSM = 1; 52 | SPI_CR2.reg.SSI = 1; 53 | SPI_CR1.reg.MSTR = 1; 54 | 55 | // configure PC5(=SCK) as output 56 | PC_ODR.bit.b5 = 1; // default = high 57 | PC_DDR.bit.b5 = 1; // input(=0) or output(=1) 58 | PC_CR1.bit.b5 = 1; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 59 | PC_CR2.bit.b5 = 1; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 60 | 61 | } // SPI master 62 | 63 | // SPI slave mode 64 | else { 65 | 66 | // configure SPI for slave mode 67 | SPI_CR2.reg.SSM = 1; 68 | SPI_CR2.reg.SSI = 0; 69 | SPI_CR1.reg.MSTR = 0; 70 | 71 | // configure PC5(=SCK) as input 72 | PC_CR2.bit.b5 = 0; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 73 | PC_DDR.bit.b5 = 0; // input(=0) or output(=1) 74 | PC_CR1.bit.b5 = 1; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 75 | 76 | } // SPI slave 77 | 78 | 79 | // set SPI type: 4-wire full-duplex(=0) or 3-wire half duplex(=1) 80 | SPI_CR2.reg.BDM = plex; 81 | 82 | // for half-duplex set CR_2 of MOSI to 0 to disable interrupts in input mode (see ref. manual) 83 | if (SPI_CR2.reg.BDM == PRM_SPI_HALF_DUPLEX) 84 | PC_CR2.bit.b6 = 0; 85 | else 86 | PC_CR2.bit.b6 = 1; 87 | 88 | // set baudrate (=16MHz/2^(pre+1)) 89 | SPI_CR1.reg.BR = (uint8_t) pre; 90 | 91 | // bit ordering (0=MSB first, 1=LSB first) 92 | SPI_CR1.reg.LSBFIRST = order; 93 | 94 | // set idle SCK polarity (0=low / 1=high) 95 | SPI_CR1.reg.CPOL = idle; 96 | 97 | // uC samples on falling (phase=0) or rising (phase=1) edge 98 | // -> STM8 convention: data CAPTURE edge (0=first clock transistion / 1=second clock transition) 99 | if (idle) { // idle clock high 100 | if (phase) // rising edge 101 | SPI_CR1.reg.CPHA = 1; 102 | else // falling edge 103 | SPI_CR1.reg.CPHA = 0; 104 | } 105 | else { // idle clock low 106 | if (phase) // rising edge 107 | SPI_CR1.reg.CPHA = 0; 108 | else // falling edge 109 | SPI_CR1.reg.CPHA = 1; 110 | } 111 | 112 | // Select SPI master mode 113 | SPI_CR1.reg.MSTR = 1; 114 | 115 | // configure NSS control to SW-Mode 116 | SPI_CR2.reg.SSM = 1; 117 | SPI_CR2.reg.SSI = 1; 118 | 119 | // enable SPI module 120 | SPI_CR1.reg.SPE = 1; 121 | 122 | } // spi_config 123 | 124 | 125 | /** 126 | \fn void spi_send_receive(uint8_t csn, uint8_t numTx, uint8_t *MOSI, uint8_t numRx, uint8_t *MISO) 127 | 128 | \brief send/receive via SPI in master mode 129 | 130 | \param[in] csn GPIO used as CSN (1..16=io_x; else none) 131 | \param[in] numTx number of bytes to send per frame 132 | \param[in] MOSI array of MOSI bytes [0..MAX_WIDTH_SPI-1] 133 | \param[in] numRx number of bytes to receive per frame (=numTx for full-duplex) 134 | \param[out] MISO array of MISO bytes [0..MAX_WIDTH_SPI-1] 135 | 136 | send/receive via SPI in master mode i.e. CSN control is by muBoard 137 | 138 | */ 139 | void spi_send_receive(uint8_t csn, uint8_t numTx, uint8_t *MOSI, uint8_t numRx, uint8_t *MISO) { 140 | 141 | uint8_t i, j; 142 | 143 | // wait until not busy 144 | while (SPI_SR.reg.BSY); 145 | 146 | // disable interrupts to prevent timing issue 147 | DISABLE_INTERRUPTS; 148 | 149 | 150 | // configure PC5(=SCK) as output 151 | PC_ODR.bit.b5 = 1; // init outputs to low, except for DAC update 152 | PC_DDR.bit.b5 = 1; // input(=0) or output(=1) 153 | PC_CR1.bit.b5 = 1; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 154 | PC_CR2.bit.b5 = 1; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 155 | 156 | 157 | // full-duplex mode (= 4-wire) 158 | if (SPI_CR2.reg.BDM == PRM_SPI_FULL_DUPLEX) { 159 | 160 | // send/receive MSB first 161 | if (SPI_CR1.reg.LSBFIRST == PRM_SPI_MSB_FIRST) { 162 | 163 | // clear MISO buffer 164 | i = SPI_DR.byte; 165 | 166 | // init array indices 167 | i = j = 0; 168 | 169 | // pull CSN low 170 | if ((csn>1) && (csn<=16)) 171 | clear_gpio(csn); 172 | 173 | // loop over numTx bytes 174 | while (j != numTx) { 175 | 176 | // fill MOSI buffer when available 177 | if ((SPI_SR.reg.TXE) && (i do nothing) 191 | if ((csn>1) && (csn<=16)) 192 | set_gpio(csn); 193 | 194 | } // MSB first 195 | 196 | // send/receive LSB first 197 | else { 198 | 199 | // clear MISO buffer 200 | i = SPI_DR.byte; 201 | 202 | // init array indices 203 | i = j = numTx; 204 | 205 | // pull CSN low (255 -> do nothing) 206 | if ((csn>1) && (csn<=16)) 207 | clear_gpio(csn); 208 | 209 | // loop over numTx bytes 210 | while (j > 0) { 211 | 212 | // fill MOSI buffer when available 213 | if ((SPI_SR.reg.TXE) && (i>0)) { 214 | SPI_DR.byte = MOSI[i-1]; 215 | i--; 216 | _NOP_; 217 | } 218 | 219 | // read MISO buffer when available 220 | if (SPI_SR.reg.RxNE) { 221 | MISO[j-1] = SPI_DR.byte; 222 | j--; 223 | _NOP_; 224 | } 225 | 226 | } // send/receive loop 227 | 228 | // pull CSN high (255 -> do nothing) 229 | if ((csn>1) && (csn<=16)) 230 | set_gpio(csn); 231 | 232 | } // LSB first 233 | 234 | } // full-duplex mode 235 | 236 | 237 | // half duplex mode (= 3-wire) 238 | else { 239 | 240 | // send/receive MSB first 241 | if (SPI_CR1.reg.LSBFIRST == PRM_SPI_MSB_FIRST) { 242 | 243 | // pull CSN low (255 -> do nothing) 244 | if ((csn>1) && (csn<=16)) 245 | clear_gpio(csn); 246 | 247 | // switch MOSI to output 248 | while (SPI_SR.reg.BSY); 249 | SPI_CR2.reg.BDOE = 1; 250 | 251 | // send data 252 | for (i=0; i 0) { 259 | 260 | // after sending done, switch MOSI to input (with pull-up) 261 | while (SPI_SR.reg.BSY); 262 | PC_DDR.bit.b6 = 0; // input(=0) or output(=1) 263 | PC_CR1.bit.b6 = 1; // input: 0=float, 1=pull-up 264 | PC_CR2.bit.b6 = 0; // input: 0=no exint, 1=exint 265 | SPI_CR2.reg.BDOE = 0; // switch SPI direction of Data pin 266 | 267 | // receive data 268 | for (i=0; i 0 274 | 275 | // pull CSN high (255 -> do nothing) 276 | while (SPI_SR.reg.BSY); 277 | if ((csn>1) && (csn<=16)) 278 | clear_gpio(csn); 279 | 280 | } // half-duplex, MSB first 281 | 282 | 283 | // send/receive LSB first 284 | else { 285 | 286 | // pull CSN low (255 -> do nothing) 287 | if ((csn>1) && (csn<=16)) 288 | clear_gpio(csn); 289 | 290 | // switch MOSI to output 291 | while (SPI_SR.reg.BSY); 292 | SPI_CR2.reg.BDOE = 1; 293 | 294 | // send data 295 | for (i=numTx; i>0; i--) { 296 | while (!SPI_SR.reg.TXE); // wait for transmit buffer empty 297 | SPI_DR.byte = MOSI[i-1]; 298 | } 299 | 300 | // receive (if specified) 301 | if (numRx > 0) { 302 | 303 | // when done, switch MOSI to input (with pull-up) 304 | while (SPI_SR.reg.BSY); 305 | PC_DDR.bit.b6 = 0; // input(=0) or output(=1) 306 | PC_CR1.bit.b6 = 1; // input: 0=float, 1=pull-up 307 | PC_CR2.bit.b6 = 0; // input: 0=no exint, 1=exint 308 | SPI_CR2.reg.BDOE = 0; 309 | 310 | // wait a bit to allow for command decoding in slave 311 | //sleep_us(1); 312 | 313 | // receive data 314 | for (i=numRx; i>0; i--) { 315 | while (!SPI_SR.reg.RxNE); // wait for receive buffer not empty 316 | MISO[i-1] = SPI_DR.byte; 317 | } 318 | 319 | } // numRx > 0 320 | 321 | // pull CSN high (255 -> do nothing) 322 | if ((csn>1) && (csn<=16)) 323 | set_gpio(csn); 324 | 325 | } // half-duplex, LSB first 326 | 327 | // when done, switch MOSI back to output 328 | while (SPI_SR.reg.BSY); 329 | SPI_CR2.reg.BDOE = 1; 330 | PC_DDR.bit.b6 = 1; // input(=0) or output(=1) 331 | PC_CR1.bit.b6 = 1; // output: 0=open-drain, 1=push-pull 332 | PC_CR2.bit.b6 = 1; // output: 0=2MHz slope, 1=10MHz slope 333 | 334 | } // half duplex mode 335 | 336 | // re-enable interrupts 337 | ENABLE_INTERRUPTS; 338 | 339 | // wait until not busy 340 | while (SPI_SR.reg.BSY); 341 | 342 | // wait a bit to guarantee a min. CSN=1 time 343 | sleep_us(10); 344 | 345 | } // spi_send_receive 346 | 347 | 348 | 349 | /** 350 | \fn void spi_ISR(void) 351 | 352 | \brief ISR for SPI_ISR bus (required for slave operation) 353 | 354 | Interrupt service routine for SPI_ISR bus (required for operation as slave). 355 | 356 | */ 357 | #if defined(__CSMC__) 358 | @near @interrupt void spi_ISR(void) { 359 | #elif defined(__SDCC) 360 | void spi_ISR() __interrupt(__SPI_ISR_VECTOR__) { 361 | #endif 362 | 363 | // dummy 364 | // check different SPI status bits and react accordingly 365 | 366 | return; 367 | 368 | } // spi_ISR 369 | 370 | 371 | /*----------------------------------------------------------------------------- 372 | END OF MODULE 373 | -----------------------------------------------------------------------------*/ 374 | -------------------------------------------------------------------------------- /STM8_Lib/spi.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file spi.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of SPI functions/macros 9 | 10 | declaration of functions for SPI communication 11 | For SPI bus, see https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | MODULE DEFINITION FOR MULTIPLE INCLUSION 16 | -----------------------------------------------------------------------------*/ 17 | #ifndef _SPI_H_ 18 | #define _SPI_H_ 19 | 20 | #include 21 | 22 | 23 | /*----------------------------------------------------------------------------- 24 | DECLARATION OF GLOBAL FUNCTIONS 25 | -----------------------------------------------------------------------------*/ 26 | 27 | /// configure SPI interface in master mode 28 | void spi_config(uint8_t type, uint16_t timeout, uint8_t plex, uint8_t pre, uint8_t order, uint8_t phase, uint8_t idle); 29 | 30 | /// send and receive data via SPI (master mode) 31 | void spi_send_receive(uint8_t csn, uint8_t numTx, uint8_t *MOSI, uint8_t numRx, uint8_t *MISO); 32 | 33 | /// ISR for SPI bus (required for SPI slave operation) 34 | #if defined(__CSMC__) 35 | @near @interrupt void spi_ISR(void); 36 | #elif defined(__SDCC) 37 | void spi_ISR(void) __interrupt(__SPI_VECTOR__); 38 | #endif 39 | 40 | 41 | /*----------------------------------------------------------------------------- 42 | DEFINITION OF GLOBAL MACROS/#DEFINES 43 | -----------------------------------------------------------------------------*/ 44 | 45 | /*----------------------------------------------------------------------------- 46 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 47 | -----------------------------------------------------------------------------*/ 48 | #endif // _SPI_H_ 49 | -------------------------------------------------------------------------------- /STM8_Lib/stdint.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file stdint.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief substitute for , if missing 9 | 10 | if is missing in compiler, e.g. Cosmic, define data types 11 | with corresponding min/max values here 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | MODULE DEFINITION FOR MULTIPLE INCLUSION 16 | -----------------------------------------------------------------------------*/ 17 | #ifndef _STDINT_H_ 18 | #define _STDINT_H_ 19 | 20 | #define TRUE 1 21 | #define FALSE 0 22 | 23 | // compiler specific --> If possible, use from compiler 24 | typedef signed long int32_t; 25 | typedef signed short int16_t; 26 | typedef signed char int8_t; 27 | 28 | typedef unsigned long uint32_t; 29 | typedef unsigned short uint16_t; 30 | typedef unsigned char uint8_t; 31 | 32 | 33 | // define min/max values 34 | #define INT8_MAX 0x7f 35 | #define INT8_MIN (-INT8_MAX - 1) 36 | 37 | #define UINT8_MAX 0xFF 38 | #define UINT8_MIN 0 39 | 40 | #define INT16_MAX 0x7fff 41 | #define INT16_MIN (-INT16_MAX - 1) 42 | 43 | #define UINT16_MAX 0xFFFF 44 | #define UINT16_MIN 0 45 | 46 | #define INT32_MAX 0x7fffffffL 47 | #define INT32_MIN (-INT32_MAX - 1L) 48 | 49 | #define UINT32_MAX 0xFFFFFFFF 50 | #define UINT32_MIN 0 51 | 52 | 53 | /*----------------------------------------------------------------------------- 54 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 55 | -----------------------------------------------------------------------------*/ 56 | #endif // _STDINT_H_ 57 | -------------------------------------------------------------------------------- /STM8_Lib/sw_fifo.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file sw_fifo.h 3 | 4 | \author G. Icking-Konert 5 | \date 2015-04-06 6 | \version 0.1 7 | 8 | \brief declaration and implementation of inline functions and macros for a SW FIFO 9 | 10 | declares and implementats a generic SW FIFO buffer, e.g. for sending and receiving via UART. 11 | Most action is done insede of interrupt service routines. For speed functions are declared 12 | as inline. This FIFO is a generalized version of code by Scott Schmit available at 13 | https://eewiki.net/display/microcontroller/Software+FIFO+Buffer+for+UART+Communication 14 | 15 | \note 16 | - FIFO buffer size is configured via FIFO_BUFFER_SIZE (default=32) 17 | - for different size re-define FIFO_BUFFER_SIZE in the calling C-file 18 | - several FIFOs within the calling C-file have the same buffer size FIFO_BUFFER_SIZE 19 | - FIFO buffers in different C-files can have different sizes set via FIFO_BUFFER_SIZE 20 | - status handling can be optimized for RAM size or flash size & speed via FIFO_OPTIMIZE_RAM (default=1) 21 | - for a different setting re-define FIFO_OPTIMIZE_RAM in the calling C-file 22 | - FIFO_OPTIMIZE_RAM=1 --> status bits are stored in 1 byte --> save 2B RAM/FIFO 23 | - FIFO_OPTIMIZE_RAM=0 --> each status flag is stored in 1 byte --> save ~25B flash/FIFO and gain some speed 24 | */ 25 | 26 | /*----------------------------------------------------------------------------- 27 | MODULE DEFINITION FOR MULTIPLE INCLUSION 28 | -----------------------------------------------------------------------------*/ 29 | #ifndef _FIFO_H_ 30 | #define _FIFO_H_ 31 | 32 | /*----------------------------------------------------------------------------- 33 | INCLUDE FILES 34 | -----------------------------------------------------------------------------*/ 35 | #include 36 | 37 | 38 | /*----------------------------------------------------------------------------- 39 | DEFINITION OF GLOBAL MACROS/#DEFINES 40 | -----------------------------------------------------------------------------*/ 41 | 42 | // default FIFO size in bytes; can be overwritten by calling file 43 | #ifndef FIFO_BUFFER_SIZE 44 | #define FIFO_BUFFER_SIZE 32 45 | #endif 46 | 47 | // default handling of FIFO status. 1: save 2B RAM/FIFO; 0: save ~25B flash/FIFO and gain some speed 48 | #ifndef FIFO_OPTIMIZE_RAM 49 | #define FIFO_OPTIMIZE_RAM 1 50 | #endif 51 | 52 | // read FIFO state from 1B status byte -> optimize RAM size 53 | #if FIFO_OPTIMIZE_RAM 54 | #define FIFO_NOT_EMPTY(a) (a.flags & 0x01) 55 | #define FIFO_FULL(a) (a.flags & 0x02) 56 | #define FIFO_OVERFLOW(a) (a.flags & 0x04) 57 | 58 | // set FIFO status flags 59 | #define FIFO_SET_NOT_EMPTY(a) (a->flags |= 0x01) 60 | #define FIFO_SET_FULL(a) (a->flags |= 0x02) 61 | #define FIFO_SET_OVERFLOW(a) (a->flags |= 0x04) 62 | 63 | // clear FIFO status flags 64 | #define FIFO_CLEAR_NOT_EMPTY(a) (a->flags &= ~0x01) 65 | #define FIFO_CLEAR_FULL(a) (a->flags &= ~0x02) 66 | #define FIFO_CLEAR_OVERFLOW(a) (a->flags &= ~0x04) 67 | 68 | // read FIFO state individual status bytes -> optimize flash size & speed 69 | #else 70 | #define FIFO_NOT_EMPTY(a) (a.fifo_not_empty) 71 | #define FIFO_FULL(a) (a.fifo_full) 72 | #define FIFO_OVERFLOW(a) (a.fifo_overflow) 73 | 74 | // set FIFO status flags 75 | #define FIFO_SET_NOT_EMPTY(a) (a->fifo_not_empty = 1) 76 | #define FIFO_SET_FULL(a) (a->fifo_full = 1) 77 | #define FIFO_SET_OVERFLOW(a) (a->fifo_overflow = 1) 78 | 79 | // clear FIFO status flags 80 | #define FIFO_CLEAR_NOT_EMPTY(a) (a->fifo_not_empty = 0) 81 | #define FIFO_CLEAR_FULL(a) (a->fifo_full = 0) 82 | #define FIFO_CLEAR_OVERFLOW(a) (a->fifo_overflow = 0) 83 | 84 | #endif // FIFO_OPTIMIZE_RAM 85 | 86 | 87 | /*----------------------------------------------------------------------------- 88 | DEFINITION OF GLOBAL TYPEDEFS 89 | -----------------------------------------------------------------------------*/ 90 | 91 | /// data structure of the SW FIFO buffer. To safe RAM, encode flags in 1B and use size dependent pointer type 92 | typedef struct { 93 | uint8_t buffer[FIFO_BUFFER_SIZE]; // FIFO data buffer 94 | #if FIFO_OPTIMIZE_RAM 95 | uint8_t flags; // 1B status bits: b0=not empty, b1=full, b2=overflow 96 | #else 97 | uint8_t fifo_not_empty; // status flag for FIFO not empty 98 | uint8_t fifo_full; // status flag for FIFO full 99 | uint8_t fifo_overflow; // status flag for FIFO overflow 100 | #endif // FIFO_OPTIMIZE_RAM 101 | #if (FIFO_BUFFER_SIZE<256) // to save RAM, use 1B or 2B pointers 102 | uint8_t idxFirst; // index of oldest byte in buffer 103 | uint8_t idxLast; // index of newest byte in buffer 104 | uint8_t numBytes; // number of bytes in buffer 105 | #else 106 | uint16_t idxFirst; 107 | uint16_t idxLast; 108 | uint16_t numBytes; 109 | #endif // FIFO_BUFFER_SIZE 110 | } fifo_t; 111 | 112 | 113 | /*----------------------------------------------------------------------------- 114 | DECLARATION OF GLOBAL FUNCTIONS 115 | -----------------------------------------------------------------------------*/ 116 | 117 | /** 118 | \fn void fifo_init(fifo_t *buf) 119 | 120 | \brief init FIFO data structure 121 | 122 | \param[in] buf pointer to FIFO structure 123 | 124 | init FIFO data structure. Actions: 125 | - reset FIFO index pointers 126 | - reset status bits 127 | 128 | \note 129 | to ensure data consistency, disable connected interrupts if 130 | called from outside interrupt service routine 131 | 132 | */ 133 | #if defined(__CSMC__) 134 | @inline void fifo_init(fifo_t *buf) { 135 | #elif defined(__SDCC) 136 | inline void fifo_init(fifo_t *buf) { 137 | #endif 138 | 139 | // reset FIFO data 140 | FIFO_CLEAR_NOT_EMPTY(buf); // set "FIFO empty" status 141 | FIFO_CLEAR_FULL(buf); // reset "FIFO full" status 142 | FIFO_CLEAR_OVERFLOW(buf); // set "FIFO overflow" status 143 | buf->idxFirst = 0; // index of oldest byte in buffer 144 | buf->idxLast = 0; // index of newest byte in buffer 145 | buf->numBytes = 0; // number of bytes in buffer 146 | 147 | } // fifo_init 148 | 149 | 150 | /** 151 | \fn void fifo_enqueue(fifo_t *buf, uint8_t data) 152 | 153 | \brief add a new byte to the FIFO buffer 154 | 155 | \param[in] buf pointer to FIFO structure 156 | \param[in] data byte to add to SW FIFO buffer 157 | 158 | add a new byte to the SW FIFO buffer. Actions: 159 | - if space available in buffer, add data to it 160 | - set "not empty" bit 161 | - if buffer is full afterwards, add data and set "full" warning bit 162 | - on buffer overflows discard new data and set "overflow" error bit 163 | 164 | \note 165 | to ensure data consistency, disable connected interrupts if 166 | called from outside interrupt service routine 167 | 168 | */ 169 | #if defined(__CSMC__) 170 | @inline void fifo_enqueue(fifo_t *buf, uint8_t data) { 171 | #elif defined(__SDCC) 172 | inline void fifo_enqueue(fifo_t *buf, uint8_t data) { 173 | #endif 174 | 175 | // if the FIFO buffer is full set overflow bit and return immediately 176 | if(buf->numBytes >= FIFO_BUFFER_SIZE) { 177 | FIFO_SET_OVERFLOW(buf); 178 | return; 179 | } 180 | 181 | // store data as newest element in the buffer 182 | buf->buffer[buf->idxLast] = data; 183 | 184 | // increment index of newest element. Clip to buffer size 185 | if ((++(buf->idxLast)) >= FIFO_BUFFER_SIZE) 186 | buf->idxLast = 0; 187 | 188 | // increment the bytes counter. If FIFO is full, set warning bit 189 | if ((++(buf->numBytes)) == FIFO_BUFFER_SIZE) 190 | FIFO_SET_FULL(buf); 191 | 192 | // set "FIFO not empty" bit 193 | FIFO_SET_NOT_EMPTY(buf); 194 | 195 | } // fifo_enqueue 196 | 197 | 198 | 199 | /** 200 | \fn uint8_t fifo_dequeue(fifo_t *buf) 201 | 202 | \brief get oldest byte from the FIFO buffer 203 | 204 | \param[in] buf pointer to FIFO structure 205 | 206 | \return oldest byte from the FIFO buffer. If empty, return 255 207 | 208 | get oldest byte from the FIFO buffer. Actions: 209 | - if data in buffer, return the oldest element and remove it from buffer 210 | - if buffer empty, clear the "FIFO not empty" bit 211 | - clear "FIFO full" warning bit (no longer true) 212 | - do not change "FIFO overflow" bit to keep track of errors. Needs to be cleared by SW 213 | 214 | \note 215 | to ensure data consistency, disable connected interrupts if 216 | called from outside interrupt service routine 217 | 218 | */ 219 | #if defined(__CSMC__) 220 | @inline uint8_t fifo_dequeue(fifo_t *buf) { 221 | #elif defined(__SDCC) 222 | inline uint8_t fifo_dequeue(fifo_t *buf) { 223 | #endif 224 | 225 | uint8_t data = 255; // =FIFO empty 226 | 227 | // if FIFO is empty clear the "FIFO not empty" bit and return immediately 228 | if(buf->numBytes == 0) { 229 | FIFO_CLEAR_NOT_EMPTY(buf); 230 | return(data); 231 | } 232 | 233 | // grab the oldest element in the buffer 234 | data = buf->buffer[buf->idxFirst]; 235 | 236 | // increment index of oldest element. Clip to buffer size 237 | if ((++(buf->idxFirst)) >= FIFO_BUFFER_SIZE) 238 | buf->idxFirst = 0; 239 | 240 | // decrement the bytes counter. If FIFO is empty, clear "not empty" bit 241 | if ((--(buf->numBytes)) == 0) 242 | FIFO_CLEAR_NOT_EMPTY(buf); 243 | 244 | // clear full bit, since we just made space 245 | FIFO_CLEAR_FULL(buf); 246 | 247 | // do not clear overflow bit to keep track of error. Needs to be cleared by SW 248 | 249 | // return the read byte 250 | return(data); 251 | 252 | } // fifo_dequeue 253 | 254 | 255 | 256 | /** 257 | \fn uint8_t fifo_peek(fifo_t *buf) 258 | 259 | \brief peek oldest byte in FIFO buffer without removing it 260 | 261 | \param[in] buf pointer to FIFO structure 262 | 263 | \return oldest byte from the FIFO buffer. If empty, return 255 264 | 265 | peek oldest byte in FIFO buffer without removing it. Actions: 266 | - if data in buffer, return the oldest element but keep it in buffer 267 | - if buffer empty, clear the "FIFO not empty" bit 268 | 269 | \note 270 | to ensure data consistency, disable connected interrupts if 271 | called from outside interrupt service routine 272 | 273 | */ 274 | #if defined(__CSMC__) 275 | @inline uint8_t fifo_peek(fifo_t *buf) { 276 | #elif defined(__SDCC) 277 | inline uint8_t fifo_peek(fifo_t *buf) { 278 | #endif 279 | 280 | uint8_t data = 255; // =FIFO empty 281 | 282 | // if FIFO is empty clear the "FIFO not empty" bit and return immediately 283 | if(buf->numBytes == 0) { 284 | FIFO_CLEAR_NOT_EMPTY(buf); 285 | return(data); 286 | } 287 | 288 | // grab the oldest element in the buffer 289 | data = buf->buffer[buf->idxFirst]; 290 | 291 | // return the read byte 292 | return(data); 293 | 294 | } // fifo_peek 295 | 296 | 297 | 298 | /** 299 | \fn void fifo_print(fifo_t *buf) 300 | 301 | \brief for debugging print FIFO content to stdio (requires putchar()!) 302 | 303 | \param[in] buf pointer to FIFO structure 304 | 305 | print FIFO content for debugging using printf(). This needs putchar() to be implemented. 306 | 307 | \note 308 | to ensure data consistency, disable connected interrupts if 309 | called from outside interrupt service routine 310 | 311 | */ 312 | #if defined(__CSMC__) 313 | @inline void fifo_print(fifo_t *buf) { 314 | #elif defined(__SDCC) 315 | inline void fifo_print(fifo_t *buf) { 316 | #endif 317 | 318 | uint8_t c; 319 | 320 | printf("num=%d; ", (int) buf->numBytes); 321 | printf("first=%d; last=%d; ", (int) buf->idxFirst, (int) buf->idxLast); 322 | #if FIFO_OPTIMIZE_RAM 323 | printf("flags: 0x%02x; ", (int) buf->flags); 324 | #else 325 | printf("empty=%d full=%d overflow=%d; ", (int) buf->fifo_not_empty, (int) buf->fifo_full, (int) buf->fifo_overflow); 326 | #endif // FIFO_OPTIMIZE_RAM 327 | printf("data="); 328 | while (FIFO_NOT_EMPTY((*buf))) { 329 | c = fifo_dequeue(buf); 330 | printf("%d ", (int) c); 331 | } 332 | 333 | } // fifo_print 334 | 335 | #endif // _FIFO_H_ 336 | -------------------------------------------------------------------------------- /STM8_Lib/timer1.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file timer1.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of TIM1 functions/macros (PWM channel) 9 | 10 | implementation of timer TIM1 functions for generating and measuring 11 | PWM signals, receiving SENT protocol etc. 12 | */ 13 | 14 | /*---------------------------------------------------------- 15 | INCLUDE FILES 16 | ----------------------------------------------------------*/ 17 | #include 18 | #include 19 | #include "stm8as.h" 20 | #include "misc.h" 21 | #include "timer1.h" 22 | #include "gpio.h" 23 | #include "timer3.h" 24 | #include "error_codes.h" 25 | 26 | 27 | /*---------------------------------------------------------- 28 | FUNCTIONS 29 | ----------------------------------------------------------*/ 30 | 31 | /** 32 | \fn void tim1_init(void) 33 | 34 | \brief init timer 1 (PWM channel) 35 | 36 | init timer TIM1 to defaut values (used for PWM) 37 | */ 38 | void tim1_init(void) { 39 | 40 | // configure pin TIM1_CC1 (=PC1) as input float. Deactivate interrupt first, just in case... 41 | PORT_C.CR1.bit.b1 = 0; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 42 | PORT_C.CR2.bit.b1 = 0; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 43 | PORT_C.DDR.bit.b1 = 0; // input(=0) or output(=1) 44 | 45 | // reset all registers to reset value 46 | TIM1.CR1.byte = TIM1_CR1_RESET_VALUE; 47 | TIM1.CR2.byte = TIM1_CR2_RESET_VALUE; 48 | TIM1.SMCR.byte = TIM1_SMCR_RESET_VALUE; 49 | TIM1.ETR.byte = TIM1_ETR_RESET_VALUE; 50 | TIM1.IER.byte = TIM1_IER_RESET_VALUE; 51 | TIM1.SR1.byte = TIM1_SR1_RESET_VALUE; 52 | TIM1.SR2.byte = TIM1_SR2_RESET_VALUE; 53 | TIM1.EGR.byte = TIM1_EGR_RESET_VALUE; 54 | TIM1.CCMR1.byte = TIM1_CCMR1_RESET_VALUE; 55 | TIM1.CCMR2.byte = TIM1_CCMR2_RESET_VALUE; 56 | TIM1.CCMR3.byte = TIM1_CCMR3_RESET_VALUE; 57 | TIM1.CCMR4.byte = TIM1_CCMR4_RESET_VALUE; 58 | TIM1.CCER1.byte = TIM1_CCER1_RESET_VALUE; 59 | TIM1.CCER2.byte = TIM1_CCER2_RESET_VALUE; 60 | TIM1.CNTR.byteH = TIM1_CNTRH_RESET_VALUE; 61 | TIM1.CNTR.byteL = TIM1_CNTRL_RESET_VALUE; 62 | TIM1.PSCR.byteH = TIM1_PSCRH_RESET_VALUE; 63 | TIM1.PSCR.byteL = TIM1_PSCRL_RESET_VALUE; 64 | TIM1.ARR.byteH = TIM1_ARRH_RESET_VALUE; 65 | TIM1.ARR.byteL = TIM1_ARRL_RESET_VALUE; 66 | TIM1.RCR.byte = TIM1_RCR_RESET_VALUE; 67 | TIM1.CC1R.byteH = TIM1_CCR1H_RESET_VALUE; 68 | TIM1.CC1R.byteL = TIM1_CCR1L_RESET_VALUE; 69 | TIM1.CC2R.byteH = TIM1_CCR2H_RESET_VALUE; 70 | TIM1.CC2R.byteL = TIM1_CCR2L_RESET_VALUE; 71 | TIM1.CC3R.byteH = TIM1_CCR3H_RESET_VALUE; 72 | TIM1.CC3R.byteL = TIM1_CCR3L_RESET_VALUE; 73 | TIM1.CC4R.byteH = TIM1_CCR4H_RESET_VALUE; 74 | TIM1.CC4R.byteL = TIM1_CCR4L_RESET_VALUE; 75 | TIM1.BKR.byte = TIM1_BKR_RESET_VALUE; 76 | TIM1.DTR.byte = TIM1_DTR_RESET_VALUE; 77 | TIM1.OISR.byte = TIM1_OISR_RESET_VALUE; 78 | 79 | // force register update 80 | TIM1.EGR.reg.UG = 1; 81 | 82 | } // tim1_init 83 | 84 | 85 | 86 | /** 87 | \fn void tim1_set_pwm(uint32_t centHz, uint16_t deciPrc) 88 | 89 | \brief generate PWM signal on TIM1_CC1 (=PC1) 90 | 91 | \param centHz frequency in 0.01Hz 92 | \param deciPrc duty cycle in 0.1% (0..1000) 93 | 94 | generate a PWM signal of given frequency and duty cycle (with high polarity). 95 | A frequency of 0 deactivates the respective output. 96 | 97 | */ 98 | void tim1_set_pwm(uint32_t centHz, uint16_t deciPrc) { 99 | 100 | uint16_t pre; // 16b timer prescaler 101 | uint16_t ARR; // 16b reload value 102 | uint16_t CCR; // 16b compare value -> duty cycle 103 | 104 | 105 | ////////////// 106 | // calculate timer parameter 107 | ////////////// 108 | 109 | // calculate max. prescaler to maximize ARR resolution. Condition: centHz > 100*(16e6/((pre+1)*(2^16-1))) 110 | pre = (uint16_t) CEIL(24414.4350347143/(float)centHz - 1.0); 111 | pre = (uint16_t) MAX(pre, 0); 112 | pre = (uint16_t) MIN(pre, 65535); 113 | 114 | // set freq to spec. value (centHz = 100*fCPU/((pre+1)*(ARR+1)) 115 | ARR = (uint16_t) ROUND(16e8/((float)(pre+1)*(float)(centHz)) - 1.0); 116 | ARR = (uint16_t) MAX(ARR, 0); 117 | ARR = (uint16_t) MIN(ARR, 65535); 118 | 119 | // calculate compare value (CCR = ARR*(deciPrc/1000)) 120 | if (deciPrc >= 1000) 121 | CCR = ARR + 1; 122 | else 123 | CCR = (uint16_t) ROUND((float) ARR * (float) deciPrc / 1000.0); 124 | CCR = (uint16_t) MAX(CCR, 0); 125 | CCR = (uint16_t) MIN(CCR, 65535); 126 | 127 | 128 | ////////////// 129 | // generate PWM 130 | ////////////// 131 | 132 | // reset timer registers (just to make sure) 133 | tim1_init(); 134 | 135 | // config TIM1 pin (=TIM1CC1 =PC1) as output 136 | PORT_C.DDR.bit.b1 = 1; // input(=0) or output(=1) 137 | PORT_C.CR1.bit.b1 = 1; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 138 | PORT_C.CR2.bit.b1 = 1; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 139 | 140 | // exit on zero frequency specified. Above Init deactivates timer 1 141 | if (centHz == 0) 142 | return; 143 | 144 | 145 | // set TIM1 prescaler f = fcpu/(pre+1) with pre in [0..2^16-1] 146 | TIM1.PSCR.byteH = (uint8_t) (pre >> 8); 147 | TIM1.PSCR.byteL = (uint8_t) pre; 148 | 149 | // set reload value (fPWM = fCPU/((pre+1)*(ARR+1)) 150 | TIM1.ARR.byteH = (uint8_t) (ARR >> 8); 151 | TIM1.ARR.byteL = (uint8_t) ARR; 152 | 153 | // set capture/compare value for duty cycle (DC=CCR/ARR) 154 | TIM1.CC1R.byteH = (uint8_t) (CCR >> 8); 155 | TIM1.CC1R.byteL = (uint8_t) CCR; 156 | 157 | // set active polarity to high (=0) (1=low polarity) 158 | TIM1.CCER1.reg.CC1P = 0; 159 | 160 | // main output enable 161 | TIM1.BKR.reg.MOE = 1; 162 | 163 | // enable output 164 | TIM1.CCER1.reg.CC1E = 1; 165 | 166 | // set PWM mode 1 167 | TIM1.CCMR1.byte = 0b01101000; 168 | 169 | // request register update 170 | TIM1.EGR.reg.UG = 1; 171 | 172 | // activate timer 173 | TIM1.CR1.reg.CEN = 1; // start the timer 174 | 175 | } // tim1_set_pwm 176 | 177 | 178 | 179 | /** 180 | \fn void tim1_set_pwm_complement(uint32_t centHz, uint16_t deciPrc) 181 | 182 | \brief generate complementary PWM signal on TIM1_CC1(=PC1) and TIM1_NCC1(=PB0) 183 | 184 | \param centHz frequency in 0.01Hz 185 | \param deciPrc duty cycle in 0.1% (0..1000) 186 | 187 | generate a complementary PWM signal of given frequency and duty cycle (with high polarity). 188 | A frequency of 0 deactivates both outputs. 189 | 190 | */ 191 | void tim1_set_pwm_complement(uint32_t centHz, uint16_t deciPrc) { 192 | 193 | uint16_t pre; // 16b timer prescaler 194 | uint16_t ARR; // 16b reload value 195 | uint16_t CCR; // 16b compare value -> duty cycle 196 | 197 | 198 | ////////////// 199 | // calculate timer parameter 200 | ////////////// 201 | 202 | // calculate max. prescaler to maximize ARR resolution. Condition: centHz > 100*(16e6/((pre+1)*(2^16-1))) 203 | pre = (uint16_t) CEIL(24414.4350347143/(float)centHz - 1.0); 204 | pre = (uint16_t) MAX(pre, 0); 205 | pre = (uint16_t) MIN(pre, 65535); 206 | 207 | // set freq to spec. value (centHz = 100*fCPU/((pre+1)*(ARR+1)) 208 | ARR = (uint16_t) ROUND(16e8/((float)(pre+1)*(float)(centHz)) - 1.0); 209 | ARR = (uint16_t) MAX(ARR, 0); 210 | ARR = (uint16_t) MIN(ARR, 65535); 211 | 212 | // calculate compare value (CCR = ARR*(deciPrc/1000)) 213 | if (deciPrc >= 1000) 214 | CCR = ARR + 1; 215 | else 216 | CCR = (uint16_t) ROUND((float) ARR * (float) deciPrc / 1000.0); 217 | CCR = (uint16_t) MAX(CCR, 0); 218 | CCR = (uint16_t) MIN(CCR, 65535); 219 | 220 | 221 | ////////////// 222 | // generate PWM 223 | ////////////// 224 | 225 | // reset timer registers (just to make sure) 226 | tim1_init(); 227 | 228 | // config TIM1 pin (=TIM1CC1 =PC1) as output 229 | PORT_C.DDR.bit.b1 = 1; // input(=0) or output(=1) 230 | PORT_C.CR1.bit.b1 = 1; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 231 | PORT_C.CR2.bit.b1 = 1; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 232 | 233 | // config TIM1 pin (=TIM1NCC1 =PB0) as output 234 | PORT_B.DDR.bit.b0 = 1; // input(=0) or output(=1) 235 | PORT_B.CR1.bit.b0 = 1; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 236 | PORT_B.CR2.bit.b0 = 1; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 237 | 238 | // exit on zero frequency specified. Above Init deactivates timer 1 239 | if (centHz == 0) 240 | return; 241 | 242 | 243 | // set TIM1 prescaler f = fcpu/(pre+1) with pre in [0..2^16-1] 244 | TIM1.PSCR.byteH = (uint8_t) (pre >> 8); 245 | TIM1.PSCR.byteL = (uint8_t) pre; 246 | 247 | // set reload value (fPWM = fCPU/((pre+1)*(ARR+1)) 248 | TIM1.ARR.byteH = (uint8_t) (ARR >> 8); 249 | TIM1.ARR.byteL = (uint8_t) ARR; 250 | 251 | // set capture/compare value for duty cycle (DC=CCR/ARR) 252 | TIM1.CC1R.byteH = (uint8_t) (CCR >> 8); 253 | TIM1.CC1R.byteL = (uint8_t) CCR; 254 | 255 | // set active polarity to high (=0) (1=low polarity) 256 | TIM1.CCER1.reg.CC1NP = 1; 257 | TIM1.CCER1.reg.CC1P = 0; 258 | 259 | // main output enable 260 | TIM1.BKR.reg.MOE = 1; 261 | 262 | // enable output 263 | TIM1.CCER1.reg.CC1NE = 1; 264 | TIM1.CCER1.reg.CC1E = 1; 265 | 266 | // set PWM mode 1 267 | TIM1.CCMR1.byte = 0b01101000; 268 | 269 | // request register update 270 | TIM1.EGR.reg.UG = 1; 271 | 272 | // activate timer 273 | TIM1.CR1.reg.CEN = 1; // start the timer 274 | 275 | } // tim1_set_pwm_complement 276 | 277 | 278 | 279 | /** 280 | \fn void tim1_get_pwm(uint32_t *centHz, uint16_t *deciPrc) 281 | 282 | \brief measure PWM signal on TIM1_CC1 (=PC1) 283 | 284 | \param[out] centHz frequency in 0.01Hz (100..tbd) 285 | \param[out] decPrc duty cycle in 0.1% (0..1000) 286 | 287 | measure a PWM input signal on TIM1_CC1 (=PC1) (frequency & duty cycle). 288 | Perform automatic range adaptation (=prescaler). 289 | */ 290 | void tim1_get_pwm(uint32_t *centHz, uint16_t *deciPrc) { 291 | 292 | uint16_t pre, CC1, CC2; 293 | uint8_t flag, h=0, l=0; 294 | 295 | // reset timer registers and reset GPIO to input 296 | tim1_init(); 297 | 298 | // configure timer behaviour 299 | TIM1.CR1.reg.UDIS = 0; 300 | TIM1.CR1.reg.URS = 1; // set SR1_UIF bit only on overflow 301 | 302 | // select input and set filter=0 303 | TIM1.CCMR1.regIn.CC1S = 1; // TIM1.CC1 -> PC1 304 | TIM1.CCMR2.regIn.CC2S = 2; // TIM1.CC2 -> PC1 305 | 306 | // capture CC1 on rising (=0),and CC2 on falling (=1) edge 307 | TIM1.CCER1.reg.CC1P = 0; 308 | TIM1.CCER1.reg.CC2P = 1; 309 | 310 | // select slave mode & input trigger (reset CNTR on trigger event) 311 | TIM1.SMCR.byte = 0x54; 312 | 313 | // enable capcom for CC1 & CC2 314 | TIM1.CCER1.reg.CC1E = 1; 315 | TIM1.CCER1.reg.CC2E = 1; 316 | 317 | // adapt clock prescaler 318 | flag = 0; 319 | pre = 0; 320 | do { 321 | 322 | // set prescaler f = fcpu/(pre+1) with pre in [0..2^16-1] 323 | TIM1.PSCR.byteH = (uint8_t) (pre >> 8); // set clock prescaler 324 | TIM1.PSCR.byteL = (uint8_t) pre; 325 | 326 | // wait for 2nd rising edge or timer overflow; 1st edge automatically resets counter 327 | TIM1.SR1.byte = 0x00; // reset status registers 328 | TIM1.SR2.byte = 0x00; 329 | TIM1.EGR.reg.UG = 1; // request register update 330 | TIM1.CR1.byte = 0x05; // start the timer (keep URS=1) 331 | while (!(TIM1.SR1.byte&0x03)); // wait for 1st rising edge or timeout 332 | TIM1.CC1R.byteH = 0x00; // reset CC1IF bit 333 | TIM1.CC1R.byteL = 0x00; 334 | while ((!(TIM1.SR2.byte&0x02)) && (!(TIM1.SR1.byte&0x01))); // wait for 2nd rising edge or timeout 335 | TIM1.CR1.byte = 0x04; // stop the timer (keep URS=1) 336 | 337 | // on timer overflow reduce clock or exit with static level 338 | if (TIM1.SR1.reg.UIF) { 339 | 340 | // max prescaler for 1Hz reached --> exit 341 | if (pre >= 245) 342 | flag = 1; 343 | 344 | // half timer frequency (f = fcpu / (pre+1) with pre in [0..2^16-1]) 345 | else 346 | pre = (pre + 1) * 2 - 1; 347 | 348 | } // timer overflow 349 | 350 | // no overflow --> done 351 | else 352 | flag = 2; 353 | 354 | } while (!flag); 355 | 356 | 357 | // capture succeeded --> get final prescaler and both capture values 358 | if (flag == 2) { 359 | CC1 = (uint16_t) (TIM1.CC1R.byteH << 8); 360 | CC1 += (uint16_t) (TIM1.CC1R.byteL); 361 | CC2 = (uint16_t) (TIM1.CC2R.byteH << 8); 362 | CC2 += (uint16_t) (TIM1.CC2R.byteL); 363 | 364 | // correct for SW latency (empirically) 365 | if (pre == 0){ 366 | CC1 += 1; 367 | CC2 += 1; 368 | } 369 | 370 | // convert to frequency [0.01Hz] and duty cycle [0.1%] 371 | *centHz = (uint32_t) ROUND(16e8/(float)(pre+1) / (float) (CC1 + 1)); 372 | *deciPrc = (uint16_t) ROUND((float) CC2 / (float) CC1 * 1000.0); 373 | 374 | } // if capture succeeded 375 | 376 | // timer overflow (flag==1) --> static signal 377 | else { 378 | *centHz = 100; // set arbitrary frequency to 1Hz 379 | if (GPIO_READ(PORT_C, PIN_1)) // set duty cycle according to GPIO level 380 | *deciPrc = 1000; 381 | else 382 | *deciPrc = 0; 383 | } 384 | 385 | // just to be sure re-init timer & GPIO 386 | tim1_init(); 387 | 388 | } // tim1_get_pwm 389 | 390 | 391 | 392 | /** 393 | \fn uint8_t tim1_receive_SENT(uint8_t *status, uint16_t *data_1, uint16_t *data_2, uint16_t timeout) 394 | 395 | \brief receive data via SENT protocol on TIM1_CC1 (=PC1) 396 | 397 | \param[out] status status nibble 398 | \param[out] data_1 12b data 1 399 | \param[out] data_2 12b data 2 400 | \param[in] timeout communication timeout [ms] 401 | 402 | \return data received (=0), timeout (=1) 403 | 404 | read data via SENT protocol with a timeout of 5ms 405 | For a description of SENT, see e.g. 406 | http://www.hanser-automotive.de/fileadmin/heftarchiv/2004/28135.pdf or 407 | http://www.elektroniknet.de/automotive/technik-know-how/bauelemente/article/75190/0/Nutzung_eines_Hall-Sensors_in_Verbindung_mit_einem_8-bit-Controller/ 408 | 409 | */ 410 | uint8_t tim1_receive_SENT(uint8_t *status, uint16_t *data_1, uint16_t *data_2, uint16_t timeout) { 411 | 412 | uint8_t nibble[8]; // status, 2x nibbles 1..3, crc 413 | uint8_t checksum, i; 414 | uint8_t nibbleCount=0; // nibble counter (0..7) 415 | uint8_t durationSync=0; // duration of sync interval 416 | uint8_t duration; // duration of current interval 417 | uint8_t oldCapture = 0; // last capture value 418 | const uint8_t CRCLookup[16] = {0, 13, 7, 10, 14, 3, 9, 4, 1, 12, 6, 11, 15, 2, 8, 5}; 419 | const uint8_t TLookup[] = {0,0,0, 1,1,1, 2,2,2, 3,3,3, 4,4,4, 5,5,5, 6,6,6, 7,7,7, 8,8,8, \ 420 | 9,9,9, 10,10,10, 11,11,11, 12,12,12, 13,13,13, 14,14,14, 15,15,15}; 421 | 422 | // init data 423 | for (i=0; i<8; i++) 424 | nibble[i] = 0; 425 | *status = 0; 426 | *data_1 = 0; 427 | *data_2 = 0; 428 | 429 | // config TIM1 pin (=TIM1CC1 =PC1) as input pull-up 430 | PORT_C.DDR.bit.b1 = 0; // input(=0) or output(=1) 431 | PORT_C.CR1.bit.b1 = 1; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 432 | PORT_C.CR2.bit.b1 = 0; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 433 | 434 | // stop timer 435 | TIM1.CR1.reg.CEN = 0; 436 | 437 | // set prescaler for 1us resolution (fClk=16MHz/(n+1) -> 1us) 438 | TIM1.PSCR.byteH = 0; 439 | TIM1.PSCR.byteL = 15; 440 | 441 | // disable capcom for CC1 (reset CCE) 442 | TIM1.CCER1.reg.CC1E = 0; 443 | 444 | // select input and set filter=0 445 | TIM1.CCMR1.regIn.CC1S = 1; // TIM1.CC1 -> PC1, no filter 446 | 447 | // capture CC1 on falling (=1) edge and enable 448 | TIM1.CCER1.reg.CC1P = 1; 449 | TIM1.CCER1.reg.CC1E = 1; 450 | 451 | // request register update 452 | TIM1.EGR.reg.UG = 1; 453 | 454 | // start the timer 455 | TIM1.CR1.reg.CEN = 1; 456 | 457 | 458 | //////////// 459 | // loop until frame received or timeout 460 | //////////// 461 | start_timeout_ms(timeout); 462 | do { 463 | 464 | // wait for falling edge or timeout 465 | while ((!TIM1.SR1.reg.CC1IF) && (!check_timeout())); 466 | 467 | // get duration [us] 468 | duration = (uint8_t) (TIM1.CC1R.byteL - oldCapture); 469 | 470 | // get TIM1CC1 low byte, clears interrupt flag 471 | oldCapture = TIM1.CC1R.byteL; 472 | 473 | // if received pulse is 168us(+/-25%) -> sync nibble 474 | if ((duration > 126) && (duration < 210)) { 475 | 476 | durationSync = duration; // store sync duration 477 | nibbleCount = 0; // reset nibble counter 478 | 479 | } // sync nibble 480 | 481 | 482 | // not a sync nibble -> store 483 | else if (durationSync != 0) { 484 | 485 | // scale measured duration and subtract 35us offset 486 | duration = (uint8_t) (((uint16_t)(168*duration + (durationSync/2))) / ((uint16_t)durationSync) - 35); 487 | 488 | // valid nibble 489 | if (duration <= 47) 490 | nibble[nibbleCount++] = *(TLookup+duration); 491 | 492 | // not a valid nibble -> abort frame 493 | else { 494 | durationSync = 0; 495 | nibbleCount = 0; 496 | } 497 | 498 | } // data nibble 499 | 500 | // until frame done or timeout 501 | } while ((nibbleCount != 8) && (!check_timeout())); 502 | 503 | 504 | // on SENT timeout set error flag 505 | if (check_timeout()) { 506 | stop_timeout_ms(); 507 | return(ERROR_TIMOUT); 508 | } 509 | 510 | // reset timeout timer 511 | stop_timeout_ms(); 512 | 513 | 514 | // calculate CRC 515 | checksum = 5; 516 | for (i=0; i<7; i++) { 517 | checksum = (uint8_t) (checksum ^ nibble[i]); 518 | checksum = CRCLookup[checksum]; 519 | } 520 | 521 | // checksum error 522 | if (checksum != nibble[7]) { 523 | return(ERROR_CHK); 524 | } 525 | 526 | // extract data 527 | *status = nibble[0]; 528 | *data_1 = (((uint16_t) nibble[1]) << 8 | (uint16_t) (nibble[2] << 4 | nibble[3])); 529 | *data_2 = (((uint16_t) nibble[4]) << 8 | (uint16_t) (nibble[5] << 4 | nibble[6])); 530 | 531 | return(SUCCESS); 532 | 533 | } // receive_SENT 534 | 535 | 536 | /*----------------------------------------------------------------------------- 537 | END OF MODULE 538 | -----------------------------------------------------------------------------*/ 539 | -------------------------------------------------------------------------------- /STM8_Lib/timer1.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file timer1.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of TIM1 functions/macros (PWM channel) 9 | 10 | declaration of timer TIM1 functions for generating and measuring 11 | PWM signals, receiving SENT protocol etc. 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | MODULE DEFINITION FOR MULTIPLE INCLUSION 16 | -----------------------------------------------------------------------------*/ 17 | #ifndef _TIMER1_H_ 18 | #define _TIMER1_H_ 19 | 20 | 21 | /*----------------------------------------------------------------------------- 22 | INCLUDE FILES 23 | -----------------------------------------------------------------------------*/ 24 | 25 | #include 26 | #include "stm8as.h" 27 | 28 | 29 | /*----------------------------------------------------------------------------- 30 | DECLARATION OF GLOBAL FUNCTIONS 31 | -----------------------------------------------------------------------------*/ 32 | 33 | /// init timer 1 (PWM channel) 34 | void tim1_init(void); 35 | 36 | /// generate PWM signal on TIM1_CC1 (=PC1) 37 | void tim1_set_pwm(uint32_t centHz, uint16_t deciPrc); 38 | 39 | // generate complementary PWM signal on TIM1_CC1(=PC1) and TIM1_NCC1(=PB0) 40 | void tim1_set_pwm_complement(uint32_t centHz, uint16_t deciPrc); 41 | 42 | /// measure PWM signal on TIM1_CC1 (=PC1) 43 | void tim1_get_pwm(uint32_t *centHz, uint16_t *deciPrc); 44 | 45 | /// receive data via SENT protocol on TIM1_CC1 (=PC1) 46 | uint8_t tim1_receive_SENT(uint8_t *status, uint16_t *data_1, uint16_t *data_2, uint16_t timeout); 47 | 48 | 49 | /*----------------------------------------------------------------------------- 50 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 51 | -----------------------------------------------------------------------------*/ 52 | #endif // _TIMER1_H_ 53 | -------------------------------------------------------------------------------- /STM8_Lib/timer2.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file timer2.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of TIM2 functions/macros (PWM channel) 9 | 10 | implementation of timer TIM2 functions for generating and measuring 11 | PWM signals, receiving SENT protocol etc. 12 | */ 13 | 14 | /*---------------------------------------------------------- 15 | INCLUDE FILES 16 | ----------------------------------------------------------*/ 17 | #include 18 | #include 19 | #include "stm8as.h" 20 | #include "misc.h" 21 | #include "timer2.h" 22 | #include "gpio.h" 23 | #include "timer3.h" 24 | #include "error_codes.h" 25 | 26 | 27 | /*---------------------------------------------------------- 28 | FUNCTIONS 29 | ----------------------------------------------------------*/ 30 | 31 | /** 32 | \fn void tim2_init(void) 33 | 34 | \brief init timer 2 (PWM channel) 35 | 36 | init timer TIM2 to defaut values (used for PWM) 37 | */ 38 | void tim2_init(void) { 39 | 40 | // configure pin TIM2_CC2 (=PD3) as input float. Deactivate interrupt first, just in case... 41 | PORT_D.CR1.bit.b3 = 0; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 42 | PORT_D.CR2.bit.b3 = 0; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 43 | PORT_D.DDR.bit.b3 = 0; // input(=0) or output(=1) 44 | 45 | // reset all registers to reset value 46 | TIM2.CR1.byte = TIM2_CR1_RESET_VALUE; 47 | TIM2.IER.byte = TIM2_IER_RESET_VALUE; 48 | TIM2.SR1.byte = TIM2_SR1_RESET_VALUE; 49 | TIM2.SR2.byte = TIM2_SR2_RESET_VALUE; 50 | TIM2.EGR.byte = TIM2_EGR_RESET_VALUE; 51 | TIM2.CCMR1.byte = TIM2_CCMR1_RESET_VALUE; 52 | TIM2.CCMR2.byte = TIM2_CCMR2_RESET_VALUE; 53 | TIM2.CCMR3.byte = TIM2_CCMR3_RESET_VALUE; 54 | TIM2.CCER1.byte = TIM2_CCER1_RESET_VALUE; 55 | TIM2.CCER2.byte = TIM2_CCER2_RESET_VALUE; 56 | TIM2.CNTR.byteH = TIM2_CNTRH_RESET_VALUE; 57 | TIM2.CNTR.byteL = TIM2_CNTRL_RESET_VALUE; 58 | TIM2.PSCR.byte = TIM2_PSCR_RESET_VALUE; 59 | TIM2.ARR.byteH = TIM2_ARRH_RESET_VALUE; 60 | TIM2.ARR.byteL = TIM2_ARRL_RESET_VALUE; 61 | TIM2.CC1R.byteH = TIM2_CCR1H_RESET_VALUE; 62 | TIM2.CC1R.byteL = TIM2_CCR1L_RESET_VALUE; 63 | TIM2.CC2R.byteH = TIM2_CCR2H_RESET_VALUE; 64 | TIM2.CC2R.byteL = TIM2_CCR2L_RESET_VALUE; 65 | TIM2.CC3R.byteH = TIM2_CCR3H_RESET_VALUE; 66 | TIM2.CC3R.byteL = TIM2_CCR3L_RESET_VALUE; 67 | 68 | // force register update 69 | TIM2.EGR.reg.UG = 1; 70 | 71 | } // tim2_init 72 | 73 | 74 | 75 | /** 76 | \fn void tim2_set_pwm(uint32_t centHz, uint16_t deciPrc) 77 | 78 | \brief generate PWM signal on TIM2_CC2 (=PD3) 79 | 80 | \param centHz frequency in 0.01Hz 81 | \param deciPrc duty cycle in 0.1% (0..1000) 82 | 83 | generate a PWM signal of given frequency and duty cycle (with high polarity). 84 | A frequency of 0 deactivates the respective output. 85 | 86 | */ 87 | void tim2_set_pwm(uint32_t centHz, uint16_t deciPrc) { 88 | 89 | uint16_t pre; // 16b timer prescaler 90 | uint16_t ARR; // 16b reload value 91 | uint16_t CCR; // 16b compare value -> duty cycle 92 | 93 | 94 | ////////////// 95 | // calculate timer parameter 96 | ////////////// 97 | 98 | // calculate max. prescaler to maximize ARR resolution. Condition: centHz > 100*(16e6/(2^pre*(2^16-1))) 99 | pre = (uint8_t) log2(24414.4350347143/(float)centHz); // checked via Excel 100 | pre = (uint16_t) MAX(pre, 0); 101 | pre = (uint16_t) MIN(pre, 15); 102 | 103 | // set freq to spec. value (centHz = 100*fCPU/((pre+1)*(ARR+1)) 104 | ARR = (uint16_t) ROUND(16e8/((float)(1L<= 1000) 110 | CCR = ARR + 1; 111 | else 112 | CCR = (uint16_t) ROUND((float) ARR * (float) deciPrc / 1000.0); 113 | CCR = (uint16_t) MAX(CCR, 0); 114 | CCR = (uint16_t) MIN(CCR, 65535); 115 | 116 | 117 | ////////////// 118 | // generate PWM 119 | ////////////// 120 | 121 | // reset timer registers 122 | tim2_init(); 123 | 124 | // config TIM2 pin (=TIM2CC2 =PD3) as output 125 | PORT_D.CR1.bit.b3 = 1; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 126 | PORT_D.CR2.bit.b3 = 1; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 127 | PORT_D.DDR.bit.b3 = 1; // input(=0) or output(=1) 128 | 129 | // set TIM2 prescaler f=fCPU/2^pre with pre in [0..15] 130 | TIM2.PSCR.reg.PSC = (uint8_t) pre; 131 | 132 | // set reload value (fPWM = fCPU/(2^pre*(ARR+1))) 133 | TIM2.ARR.byteH = (uint8_t) (ARR >> 8); 134 | TIM2.ARR.byteL = (uint8_t) ARR; 135 | 136 | // set capture/compare value for duty cycle (DC=CCR/ARR) 137 | TIM2.CC2R.byteH = (uint8_t) (CCR >> 8); 138 | TIM2.CC2R.byteL = (uint8_t) CCR; 139 | 140 | // set active polarity to high (=0) (1=low polarity) 141 | TIM2.CCER1.reg.CC2P = 0; 142 | 143 | // enable output 144 | TIM2.CCER1.reg.CC2E = 1; 145 | 146 | // set PWM mode 1 147 | TIM2.CCMR2.byte = 0b01101000; 148 | 149 | // request register update 150 | TIM2.EGR.reg.UG = 1; 151 | 152 | // start the timer 153 | TIM2.CR1.reg.CEN = 1; 154 | 155 | } // tim2_set_pwm 156 | 157 | 158 | 159 | 160 | /** 161 | \fn void tim2_get_pwm(uint32_t *centHz, uint16_t *deciPrc) 162 | 163 | \brief measure PWM signal on TIM2_CC2 (=PD3) 164 | 165 | \param[out] centHz frequency in 0.01Hz (100..tbd) 166 | \param[out] decPrc duty cycle in 0.1% (0..1000) 167 | 168 | measure a PWM input signal on TIM2_CC2 (=PD3) (frequency & duty cycle). 169 | Perform automatic range adaptation (=prescaler). 170 | */ 171 | void tim2_get_pwm(uint32_t *centHz, uint16_t *deciPrc) { 172 | 173 | uint16_t pre, CC1, CC2; 174 | uint8_t flag, h=0, l=0; 175 | 176 | // reset timer registers and reset GPIO to input 177 | tim2_init(); 178 | 179 | // configure timer behaviour 180 | TIM2.CR1.reg.UDIS = 0; 181 | TIM2.CR1.reg.URS = 1; // set SR1_UIF bit only on overflow 182 | 183 | // select input and set filter=0 184 | TIM2.CCMR1.regIn.CC1S = 2; // TIM2.CC1 -> PD3 185 | TIM2.CCMR2.regIn.CC2S = 1; // TIM2.CC2 -> PD3 186 | 187 | // capture CC1 on rising (=0),and CC2 on falling (=1) edge 188 | TIM2.CCER1.reg.CC1P = 0; 189 | TIM2.CCER1.reg.CC2P = 1; 190 | 191 | // enable capcom for CC1 & CC2 192 | TIM2.CCER1.reg.CC1E = 1; 193 | TIM2.CCER1.reg.CC2E = 1; 194 | 195 | // adapt clock prescaler 196 | flag = 0; 197 | pre = 0; 198 | do { 199 | 200 | // set prescaler f = fcpu/2^pre with pre in [0..15] 201 | TIM2.PSCR.reg.PSC = (uint8_t) pre; 202 | 203 | // wait for 2nd rising edge or timer overflow. TIM2 has no slave mode and needs some more effort 204 | TIM2.SR1.byte = 0x00; // reset status registers 205 | TIM2.SR2.byte = 0x00; 206 | l = TIM2.CC1R.byteL; // clear CC1 capture flag 207 | TIM2.CNTR.byteH = 0x00; 208 | TIM2.CNTR.byteL = 0x00; 209 | TIM2.EGR.reg.UG = 1; // request register update 210 | TIM2.CR1.byte = 0x05; // start the timer (keep URS=1) 211 | while (!(TIM2.SR1.byte&0x03)); // wait for 2nd rising edge or timeout 212 | h = TIM2.CNTR.byteH; // store capture value 213 | l = TIM2.CNTR.byteL; 214 | while ((!(TIM2.SR2.byte&0x02)) && (!(TIM2.SR1.byte&0x01))); // wait for 2nd rising edge or timeout 215 | TIM2.CR1.byte = 0x04; // stop the timer (keep URS=1) 216 | 217 | // on timer overflow or if duty cycle>100% reduce clock or exit with static level 218 | if (TIM2.SR1.reg.UIF) { 219 | 220 | // max prescaler for 1Hz reached (need 2x period since counter is not reset) --> exit 221 | if (pre >= 9) 222 | flag = 1; 223 | 224 | // half timer frequency (f = fcpu/2^pre with pre in [0..15]) 225 | else 226 | pre++; 227 | 228 | } // timer overflow 229 | 230 | // no overflow --> done 231 | else 232 | flag = 2; 233 | 234 | } while (!flag); 235 | 236 | 237 | // capture succeeded --> get final prescaler and both capture values 238 | if (flag == 2) { 239 | 240 | // get capture values 241 | CC1 = (uint16_t) TIM2.CC1R.byteH << 8; 242 | CC1 += (uint16_t) TIM2.CC1R.byteL; 243 | CC1 -= (uint16_t) l + ((uint16_t) h << 8); 244 | CC2 = (uint16_t) TIM2.CC2R.byteH << 8; 245 | CC2 += (uint16_t) TIM2.CC2R.byteL; 246 | CC2 -= (uint16_t) l + ((uint16_t) h << 8); 247 | 248 | // correct for SW latency (empirically) 249 | if (pre == 0){ 250 | CC1 += 5; 251 | CC2 += 5; 252 | } 253 | if (pre == 1){ 254 | CC1 += 4; 255 | CC2 += 4; 256 | } 257 | 258 | // convert to frequency [0.01Hz] and duty cycle [0.1%] 259 | *centHz = (uint32_t) ROUND(16e8/(float)(1L< static signal 266 | else { 267 | *centHz = 100; // set arbitrary frequency to 1Hz 268 | if (GPIO_READ(PORT_D, PIN_3)) // set duty cycle according to GPIO level 269 | *deciPrc = 1000; 270 | else 271 | *deciPrc = 0; 272 | } 273 | 274 | // just to be sure re-init timer & GPIO 275 | tim2_init(); 276 | 277 | } // tim2_get_pwm 278 | 279 | 280 | 281 | /** 282 | \fn uint8_t tim2_receive_SENT(uint8_t *status, uint16_t *data_1, uint16_t *data_2, uint16_t timeout) 283 | 284 | \brief receive data via SENT protocol on TIM2_CC2 (=PD3) 285 | 286 | \param[out] status status nibble 287 | \param[out] data_1 12b data 1 288 | \param[out] data_2 12b data 2 289 | \param[in] timeout communication timeout [ms] 290 | 291 | \return data received (=0), timeout (=1) 292 | 293 | read data via SENT protocol with a timeout of 5ms 294 | For a description of SENT, see e.g. 295 | http://www.hanser-automotive.de/fileadmin/heftarchiv/2004/28135.pdf or 296 | http://www.elektroniknet.de/automotive/technik-know-how/bauelemente/article/75190/0/Nutzung_eines_Hall-Sensors_in_Verbindung_mit_einem_8-bit-Controller/ 297 | 298 | */ 299 | uint8_t tim2_receive_SENT(uint8_t *status, uint16_t *data_1, uint16_t *data_2, uint16_t timeout) { 300 | 301 | uint8_t nibble[8]; // status, 2x nibbles 1..3, crc 302 | uint8_t checksum, i; 303 | uint8_t nibbleCount=0; // nibble counter (0..7) 304 | uint8_t durationSync=0; // duration of sync interval 305 | uint8_t duration; // duration of current interval 306 | uint8_t oldCapture = 0; // last capture value 307 | const uint8_t CRCLookup[16] = {0, 13, 7, 10, 14, 3, 9, 4, 1, 12, 6, 11, 15, 2, 8, 5}; 308 | const uint8_t TLookup[] = {0,0,0, 1,1,1, 2,2,2, 3,3,3, 4,4,4, 5,5,5, 6,6,6, 7,7,7, 8,8,8, \ 309 | 9,9,9, 10,10,10, 11,11,11, 12,12,12, 13,13,13, 14,14,14, 15,15,15}; 310 | 311 | // init data 312 | for (i=0; i<8; i++) 313 | nibble[i] = 0; 314 | *status = 0; 315 | *data_1 = 0; 316 | *data_2 = 0; 317 | 318 | // config TIM2 pin (TIM2_CC2 = PD3) as input pull-up 319 | PORT_D.DDR.bit.b3 = 0; // input(=0) or output(=1) 320 | PORT_D.CR1.bit.b3 = 1; // input: 0=float, 1=pull-up; output: 0=open-drain, 1=push-pull 321 | PORT_D.CR2.bit.b3 = 0; // input: 0=no exint, 1=exint; output: 0=2MHz slope, 1=10MHz slope 322 | 323 | // stop timer 324 | TIM2.CR1.reg.CEN = 0; 325 | 326 | // set prescaler for 1us resolution 327 | TIM2.PSCR.reg.PSC = 4; // fClk=16MHz/2^n -> 1us 328 | 329 | // disable capcom for CC2 (reset CCE) 330 | TIM2.CCER1.reg.CC2E = 0; 331 | 332 | // select input and set filter=0 333 | TIM2.CCMR2.regIn.CC2S = 1; // TIM2.CC2 -> PD3, no filter 334 | 335 | // capture CC2 on falling (=1) edge and enable 336 | TIM2.CCER1.reg.CC2P = 1; 337 | TIM2.CCER1.reg.CC2E = 1; 338 | 339 | // request register update 340 | TIM2.EGR.reg.UG = 1; 341 | 342 | // start the timer 343 | TIM2.CR1.reg.CEN = 1; 344 | 345 | 346 | //////////// 347 | // loop until frame received or timeout 348 | //////////// 349 | start_timeout_ms(timeout); 350 | do { 351 | 352 | // wait for falling edge or timeout 353 | while ((!TIM2.SR1.reg.CC2IF) && (!check_timeout())); 354 | 355 | // get duration [us] 356 | duration = (uint8_t) (TIM2.CC2R.byteL - oldCapture); 357 | 358 | // get TIM2_CC2 low byte, clears interrupt flag 359 | oldCapture = TIM2.CC2R.byteL; 360 | 361 | // if received pulse is 168us(+/-25%) -> sync nibble 362 | if ((duration > 126) && (duration < 210)) { 363 | 364 | durationSync = duration; // store sync duration 365 | nibbleCount = 0; // reset nibble counter 366 | 367 | } // sync nibble 368 | 369 | 370 | // not a sync nibble -> store 371 | else if (durationSync != 0) { 372 | 373 | // scale measured duration and subtract 35us offset 374 | duration = (uint8_t) (((uint16_t)(168*duration + (durationSync/2))) / ((uint16_t)durationSync) - 35); 375 | 376 | // valid nibble 377 | if (duration <= 47) 378 | nibble[nibbleCount++] = *(TLookup+duration); 379 | 380 | // not a valid nibble -> abort frame 381 | else { 382 | durationSync = 0; 383 | nibbleCount = 0; 384 | } 385 | 386 | } // data nibble 387 | 388 | // until frame done or timeout 389 | } while ((nibbleCount != 8) && (!check_timeout())); 390 | 391 | 392 | // on SENT timeout set error flag 393 | if (check_timeout()) { 394 | stop_timeout_ms(); 395 | return(ERROR_TIMOUT); 396 | } 397 | 398 | // reset timeout timer 399 | stop_timeout_ms(); 400 | 401 | 402 | // calculate CRC 403 | checksum = 5; 404 | for (i=0; i<7; i++) { 405 | checksum = (uint8_t) (checksum ^ nibble[i]); 406 | checksum = CRCLookup[checksum]; 407 | } 408 | 409 | // checksum error 410 | if (checksum != nibble[7]) { 411 | return(ERROR_CHK); 412 | } 413 | 414 | // extract data 415 | *status = nibble[0]; 416 | *data_1 = (((uint16_t) nibble[1]) << 8 | (uint16_t) (nibble[2] << 4 | nibble[3])); 417 | *data_2 = (((uint16_t) nibble[4]) << 8 | (uint16_t) (nibble[5] << 4 | nibble[6])); 418 | 419 | return(SUCCESS); 420 | 421 | } // receive_SENT 422 | 423 | 424 | /*----------------------------------------------------------------------------- 425 | END OF MODULE 426 | -----------------------------------------------------------------------------*/ 427 | -------------------------------------------------------------------------------- /STM8_Lib/timer2.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file timer2.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of TIM2 functions/macros (PWM channel) 9 | 10 | declaration of timer TIM2 functions for generating and measuring 11 | PWM signals, receiving SENT protocol etc. 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | MODULE DEFINITION FOR MULTIPLE INCLUSION 16 | -----------------------------------------------------------------------------*/ 17 | #ifndef _TIMER2_H_ 18 | #define _TIMER2_H_ 19 | 20 | 21 | /*----------------------------------------------------------------------------- 22 | INCLUDE FILES 23 | -----------------------------------------------------------------------------*/ 24 | 25 | #include 26 | #include "stm8as.h" 27 | 28 | 29 | /*----------------------------------------------------------------------------- 30 | DECLARATION OF GLOBAL FUNCTIONS 31 | -----------------------------------------------------------------------------*/ 32 | 33 | /// init timer 2 (PWM channel) 34 | void tim2_init(void); 35 | 36 | /// generate PWM signal on TIM2_CC2 (=PD3) 37 | void tim2_set_pwm(uint32_t centHz, uint16_t deciPrc); 38 | 39 | /// measure PWM signal on TIM2_CC2 (=PD3) 40 | void tim2_get_pwm(uint32_t *centHz, uint16_t *deciPrc); 41 | 42 | /// receive data via SENT protocol on TIM2_CC2 (=PD3) 43 | uint8_t tim2_receive_SENT(uint8_t *status, uint16_t *data_1, uint16_t *data_2, uint16_t timeout); 44 | 45 | 46 | /*----------------------------------------------------------------------------- 47 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 48 | -----------------------------------------------------------------------------*/ 49 | #endif // _TIMER2_H_ 50 | -------------------------------------------------------------------------------- /STM8_Lib/timer3.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file timer3.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of timer TIM3 functions/macros for sleep_x and timeout 9 | 10 | implementation of timer TIM3 functions for sleep_x and timeout 11 | via polling 12 | */ 13 | 14 | /*---------------------------------------------------------- 15 | INCLUDE FILES 16 | ----------------------------------------------------------*/ 17 | #include 18 | #include "stm8as.h" 19 | #include "timer3.h" 20 | 21 | 22 | /*---------------------------------------------------------- 23 | FUNCTIONS 24 | ----------------------------------------------------------*/ 25 | 26 | /** 27 | \fn void tim3_init(void) 28 | 29 | \brief init timer 3 (sleep_x/timeout) 30 | 31 | init timer TIM3 (used for sleep_x and timeout). 32 | */ 33 | void tim3_init(void) { 34 | 35 | // stop the timer 36 | TIM3.CR1.reg.CEN = 0; 37 | 38 | // disable single-shot mode (causes SW stalls) 39 | TIM3.CR1.reg.OPM = 0; 40 | 41 | // set prescaler to fclk/2^4 -> 1MHz clock -> 1us resolution 42 | TIM3.PSCR.reg.PSC = 4; 43 | 44 | // init to 1ms timeout (write high byte first) 45 | TIM3.CC1R.byteH = 0x03; 46 | TIM3.CC1R.byteL = 0xE8; 47 | 48 | // clear status registers 49 | TIM3.SR1.byte = 0x00; 50 | TIM3.SR2.byte = 0x00; 51 | 52 | // reset counter register (write high byte first) 53 | TIM3.CNTR.byteH = 0; 54 | TIM3.CNTR.byteL = 0; 55 | 56 | // request register update 57 | TIM3.EGR.reg.UG = 1; 58 | 59 | // disable T3 interrupts 60 | TIM3.IER.byte = 0x00; 61 | 62 | } // tim3_init 63 | 64 | 65 | 66 | /** 67 | \fn void sleep_ns(uint16_t dt) 68 | 69 | \brief halt code execution for specified 62.5ns units 70 | 71 | \param dt halt duration in 62.5ns units 72 | 73 | code execution is halted for specified number of 62.5ns units. 74 | */ 75 | void sleep_ns(uint16_t dt) { 76 | 77 | // convert 250ns-->62.5ns 78 | dt *= 4; 79 | 80 | // stop timer 81 | TIM3.CR1.reg.CEN = 0; 82 | 83 | // set prescaler to fclk -> 16MHz clock -> 62.5ns resolution 84 | TIM3.PSCR.reg.PSC = 0; 85 | 86 | // set timout to dt*250ns (freq_Hz=fclk/(prescaler*ARR)) (write high byte first) 87 | TIM3.CC1R.byteH = (uint8_t) (dt >> 8); 88 | TIM3.CC1R.byteL = (uint8_t) dt; 89 | 90 | // reset counter register (write high byte first) 91 | TIM3.CNTR.byteH = (uint8_t) 0; 92 | TIM3.CNTR.byteL = (uint8_t) 0; 93 | 94 | // request register update 95 | TIM3.EGR.reg.UG = 1; 96 | 97 | // clear status registers 98 | TIM3.SR1.byte = 0x00; 99 | 100 | // start the timer 101 | TIM3.CR1.reg.CEN = 1; 102 | 103 | // wait for overflow (no interrupts!) 104 | while (!TIM3.SR1.reg.CC1IF); 105 | 106 | } // sleep_ns 107 | 108 | 109 | 110 | /** 111 | \fn void sleep_us(uint16_t dt) 112 | 113 | \brief halt code execution for specified microseconds 114 | 115 | \param dt halt duration in us 116 | 117 | code execution is halted for specified number of microseconds. 118 | */ 119 | void sleep_us(uint16_t dt) { 120 | 121 | // stop timer 122 | TIM3.CR1.reg.CEN = 0; 123 | 124 | // set prescaler to fclk/2^4 -> 1MHz clock -> 1us resolution 125 | TIM3.PSCR.reg.PSC = 4; 126 | 127 | // set timout to dt us (freq_Hz=fclk/(prescaler*ARR)) (write high byte first) 128 | TIM3.CC1R.byteH = (uint8_t) (dt >> 8); 129 | TIM3.CC1R.byteL = (uint8_t) dt; 130 | 131 | // reset counter register (write high byte first) 132 | TIM3.CNTR.byteH = (uint8_t) 0; 133 | TIM3.CNTR.byteL = (uint8_t) 0; 134 | 135 | // request register update 136 | TIM3.EGR.reg.UG = 1; 137 | 138 | // clear status registers 139 | TIM3.SR1.byte = 0x00; 140 | 141 | // start the timer 142 | TIM3.CR1.reg.CEN = 1; 143 | 144 | // wait for overflow (no interrupts!) 145 | while (!TIM3.SR1.reg.CC1IF); 146 | 147 | } // sleep_us 148 | 149 | 150 | 151 | /** 152 | \fn void sleep_ms(uint16_t dt) 153 | 154 | \brief halt code execution for specified milliseconds 155 | 156 | \param dt halt duration in ms 157 | 158 | code execution is halted for specified number of milliseconds. 159 | */ 160 | void sleep_ms(uint16_t dt) { 161 | 162 | // stop timer 163 | TIM3.CR1.reg.CEN = 0; 164 | 165 | // set prescaler to fclk/2^14 -> ~1kHz clock 166 | TIM3.PSCR.reg.PSC = 14; 167 | 168 | // set timout to dt ms (freq_Hz=fclk/(prescaler*ARR)) (write high byte first) 169 | TIM3.CC1R.byteH = (uint8_t) (dt >> 8); 170 | TIM3.CC1R.byteL = (uint8_t) dt; 171 | 172 | // reset counter register (write high byte first) 173 | TIM3.CNTR.byteH = (uint8_t) 0; 174 | TIM3.CNTR.byteL = (uint8_t) 0; 175 | 176 | // clear status registers 177 | TIM3.SR1.byte = 0x00; 178 | 179 | // request register update 180 | TIM3.EGR.reg.UG = 1; 181 | 182 | // start the timer 183 | TIM3.CR1.reg.CEN = 1; 184 | 185 | // wait for overflow (no interrupts!) 186 | while (!TIM3.SR1.reg.CC1IF); 187 | 188 | } // sleep_ms 189 | 190 | 191 | 192 | /** 193 | \fn void start_timeout_ms(uint16_t dt) 194 | 195 | \brief start timeout with specified number of ms 196 | 197 | \param dt timeout duration in ms (0=forever) 198 | 199 | start timeout with specified number of milliseconds. To check for 200 | timeout query respective timer overflow flag. 201 | */ 202 | void start_timeout_ms(uint16_t dt) { 203 | 204 | // stop timer 205 | TIM3.CR1.reg.CEN = 0; 206 | 207 | // prescaler to fclk/2^14 -> ~1kHz clock 208 | TIM3.PSCR.reg.PSC = 14; 209 | 210 | // set auto-reload value to n us (freq_Hz=fclk/(prescaler*ARR)) (write high byte first) 211 | TIM3.CC1R.byteH = (uint8_t) (dt >> 8); 212 | TIM3.CC1R.byteL = (uint8_t) dt; 213 | 214 | // reset counter register (write high byte first) 215 | TIM3.CNTR.byteH = 0; 216 | TIM3.CNTR.byteL = 0; 217 | 218 | // request register update 219 | TIM3.EGR.reg.UG = 1; 220 | 221 | // clear status registers 222 | TIM3.SR1.byte = 0x00; 223 | 224 | // reset timeout flag 225 | TIM3.SR1.reg.CC1IF = 0; 226 | 227 | // start the timer only if dt!=0 228 | if (dt != 0) 229 | TIM3.CR1.reg.CEN = 1; 230 | else 231 | TIM3.CR1.reg.CEN = 0; 232 | 233 | } // start_timeout_ms 234 | 235 | 236 | 237 | /** 238 | \fn void stop_timeout_ms(void) 239 | 240 | \brief top timeout timer 241 | 242 | stop the timeout timer to avoid conflicts e.g. with sleep_x. 243 | */ 244 | void stop_timeout_ms() { 245 | 246 | // stop timer 247 | TIM3.CR1.reg.CEN = 0; 248 | 249 | // set prescaler to fclk/2^4 -> 1MHz clock -> 1us resolution 250 | TIM3.PSCR.reg.PSC = 4; 251 | 252 | } // stop_timeout_ms 253 | 254 | /*----------------------------------------------------------------------------- 255 | END OF MODULE 256 | -----------------------------------------------------------------------------*/ 257 | -------------------------------------------------------------------------------- /STM8_Lib/timer3.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file timer3.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of timer TIM3 functions/macros for sleep_x and timeout 9 | 10 | declaration of timer TIM3 functions for sleep_x and timeout 11 | via polling 12 | */ 13 | 14 | /*----------------------------------------------------------------------------- 15 | MODULE DEFINITION FOR MULTIPLE INCLUSION 16 | -----------------------------------------------------------------------------*/ 17 | #ifndef _TIMER3_H_ 18 | #define _TIMER3_H_ 19 | 20 | 21 | /*----------------------------------------------------------------------------- 22 | INCLUDE FILES 23 | -----------------------------------------------------------------------------*/ 24 | 25 | #include 26 | #include "stm8as.h" 27 | 28 | 29 | /*----------------------------------------------------------------------------- 30 | DEFINITION OF GLOBAL MACROS/#DEFINES 31 | -----------------------------------------------------------------------------*/ 32 | 33 | /// check for TIM3 timeout flag (see start_timeout_ms()) 34 | #define check_timeout() (TIM3.SR1.reg.CC1IF) 35 | 36 | 37 | /*----------------------------------------------------------------------------- 38 | DECLARATION OF GLOBAL FUNCTIONS 39 | -----------------------------------------------------------------------------*/ 40 | 41 | /// init timer 3 (sleep/timeout) 42 | void tim3_init(void); 43 | 44 | /// halt code execution for dt*62.5ns 45 | void sleep_ns(uint16_t dt); 46 | 47 | /// halt code execution for dt microseconds 48 | void sleep_us(uint16_t dt); 49 | 50 | /// halt code execution for dt milliseconds 51 | void sleep_ms(uint16_t dt); 52 | 53 | /// start timeout timer 54 | void start_timeout_ms(uint16_t dt); 55 | 56 | /// start timeout timer 57 | void stop_timeout_ms(void); 58 | 59 | 60 | /*----------------------------------------------------------------------------- 61 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 62 | -----------------------------------------------------------------------------*/ 63 | #endif // _TIMER3_H_ 64 | -------------------------------------------------------------------------------- /STM8_Lib/timer4.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file timer4.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of timer TIM4 (1ms clock) functions/macros 9 | 10 | implementation of timer TIM4 functions as 1ms master clock 11 | */ 12 | 13 | /*---------------------------------------------------------- 14 | INCLUDE FILES 15 | ----------------------------------------------------------*/ 16 | #include 17 | #include "stm8as.h" 18 | #include "timer4.h" 19 | 20 | 21 | /*---------------------------------------------------------- 22 | FUNCTIONS 23 | ----------------------------------------------------------*/ 24 | 25 | /** 26 | \fn void tim4_init(void) 27 | 28 | \brief init timer 4 (master clock with 1ms) 29 | 30 | init timer TIM4 with 1ms tick. Is used for SW master clock. 31 | */ 32 | void tim4_init(void) { 33 | 34 | // initialize global TIM4 variables 35 | g_flagClock = 0; 36 | g_clock = 0; 37 | 38 | // auto-reload value buffered 39 | TIM4.CR1.reg.ARPE = 1; 40 | 41 | // clear pending events 42 | TIM4.EGR.byte = 0x00; 43 | 44 | // set clock to 16Mhz/128 -> 8us 45 | TIM4.PSCR.reg.PSC = 7; 46 | 47 | // set autoreload to 125 (125*8us = 1ms) 48 | TIM4.ARR.byte = 125; 49 | 50 | // enable timer 4 interrupt 51 | TIM4.IER.reg.UIE = 1; 52 | 53 | // start the timer 54 | TIM4.CR1.reg.CEN = 1; 55 | 56 | } // tim4_init 57 | 58 | 59 | 60 | /** 61 | \fn void tim4_ISR(void) 62 | 63 | \brief ISR for timer 4 (1ms master clock) 64 | 65 | interrupt service routine for timer TIM4. 66 | Used for 1ms master clock 67 | */ 68 | #if defined(__CSMC__) 69 | @near @interrupt void tim4_ISR(void) 70 | #elif defined(__SDCC) 71 | void tim4_ISR() __interrupt(__TIM4UPD_VECTOR__) 72 | #endif 73 | { 74 | // clear timer 4 interrupt flag 75 | TIM4.SR1.reg.UIF = 0; 76 | 77 | // set global variables 78 | g_flagClock = 1; 79 | g_clock++; 80 | 81 | // service independent timeout watchdog 82 | //IWDG.KR.byte = 0xAAu; 83 | 84 | return; 85 | 86 | } // tim4_ISR 87 | 88 | 89 | /*----------------------------------------------------------------------------- 90 | END OF MODULE 91 | -----------------------------------------------------------------------------*/ 92 | -------------------------------------------------------------------------------- /STM8_Lib/timer4.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file timer4.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of timer TIM4 (1ms clock) functions/macros 9 | 10 | declaration of timer TIM4 functions as 1ms master clock 11 | */ 12 | 13 | /*----------------------------------------------------------------------------- 14 | MODULE DEFINITION FOR MULTIPLE INCLUSION 15 | -----------------------------------------------------------------------------*/ 16 | #ifndef _TIMER4_H_ 17 | #define _TIMER4_H_ 18 | 19 | 20 | /*----------------------------------------------------------------------------- 21 | INCLUDE FILES 22 | -----------------------------------------------------------------------------*/ 23 | 24 | #include 25 | #include "stm8as.h" 26 | 27 | 28 | /*----------------------------------------------------------------------------- 29 | DECLARATION OF GLOBAL VARIABLES 30 | -----------------------------------------------------------------------------*/ 31 | 32 | global uint8_t g_flagClock; ///< flag for master clock interrupt. Set in TIM4 ISR 33 | global uint32_t g_clock; ///< counter for master clock interrupt. Increased in TIM4 ISR 34 | 35 | 36 | /*----------------------------------------------------------------------------- 37 | DECLARATION OF GLOBAL FUNCTIONS 38 | -----------------------------------------------------------------------------*/ 39 | 40 | /// init timer 4 (1ms master clock) 41 | void tim4_init(void); 42 | 43 | /// ISR for timer 4 (1ms master clock) 44 | #if defined(__CSMC__) 45 | @near @interrupt void tim4_ISR(void); 46 | #elif defined(__SDCC) 47 | void tim4_ISR(void) __interrupt(__TIM4UPD_VECTOR__); 48 | #endif 49 | 50 | 51 | /*----------------------------------------------------------------------------- 52 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 53 | -----------------------------------------------------------------------------*/ 54 | #endif // _TIMER4_H_ 55 | -------------------------------------------------------------------------------- /STM8_Lib/uart1.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file uart1.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of UART1 / USART functions/macros 9 | 10 | implementation of UART1 / USART functions and macros. 11 | Optional functionality via #define: 12 | - UART1_HALF_DUPLEX: communication via 1-wire interface, e.g. LIN -> ignore echo after send (default: 2-wire) 13 | - UART1_FIFO: send/receive is in background via interrupts and SW FIFO (default: blocking w/o FIFO) 14 | - UART1_RST: trigger SW reset on reception of "Re5eT!" 15 | */ 16 | 17 | /*----------------------------------------------------------------------------- 18 | INCLUDE FILES 19 | -----------------------------------------------------------------------------*/ 20 | #include 21 | #include 22 | #include "stm8as.h" 23 | #include "uart1.h" 24 | 25 | // configure SW FIFO (see sw_fifo.h) 26 | #ifdef UART1_FIFO 27 | #define FIFO_BUFFER_SIZE 128 ///< set buffer size for Rx and Tx (have to be same) 28 | #define FIFO_OPTIMIZE_RAM 0 ///< internal management. 1: save 2B RAM/FIFO; 0: save ~25B flash/FIFO and gain some speed 29 | #include "sw_fifo.h" // FIFO declaration and inline fuctions (for speed) 30 | #endif // UART1_FIFO 31 | 32 | 33 | /*----------------------------------------------------------------------------- 34 | MODULE VARIABLES (for clarity module internal variables start with "m_") 35 | -----------------------------------------------------------------------------*/ 36 | 37 | // if FIFO is used, reserve buffers for Rx and Tx 38 | #ifdef UART1_FIFO 39 | 40 | /// UART1 receive FIFO buffer 41 | fifo_t m_UART1_Rx_Fifo = { {0}, 0, 0, 0, 0 }; 42 | 43 | /// UART1 transmit FIFO buffer 44 | fifo_t m_UART1_Tx_Fifo = { {0}, 0, 0, 0, 0 }; 45 | 46 | #endif // UART1_FIFO 47 | 48 | 49 | // if UART reset command is used 50 | #ifdef UART1_RST 51 | 52 | /// reset command string 53 | uint8_t m_UART1_cmdReset[] = "Re5eT!"; 54 | 55 | /// length of command string 56 | uint8_t m_UART1_lenReset = 6; 57 | 58 | /// index for state machine 59 | uint8_t m_UART1_idxReset = 0; 60 | 61 | #endif // UART1_RST 62 | 63 | 64 | /*---------------------------------------------------------- 65 | FUNCTIONS 66 | ----------------------------------------------------------*/ 67 | 68 | /** 69 | \fn void uart1_init(uint32_t BR) 70 | 71 | \brief initialize UART1 for communication 72 | 73 | \param[in] BR baudrate [Baud] 74 | 75 | initialize UART1 for communication with specified baudrate. 76 | Use 1 start, 8 data and 1 stop bit; no parity or flow control. 77 | 78 | If FIFO is used, initialize Rx and Tx FIFOs and activate Rx interrupt. 79 | */ 80 | void uart1_init(uint32_t BR) { 81 | 82 | volatile uint16_t val16; 83 | 84 | // set UART1 behaviour 85 | UART1.CR1.byte = 0x00; // enable UART1, 8 data bits, no parity control 86 | UART1.CR2.byte = 0x00; // no interrupts, disable sender/receiver 87 | UART1.CR3.byte = 0x00; // no LIN support, 1 stop bit, no clock output(?) 88 | 89 | // set baudrate (note: BRR2 must be written before BRR1!) 90 | val16 = (uint16_t) (((uint32_t) 16000000L)/BR); 91 | UART1.BRR2.byte = (uint8_t) (((val16 & 0xF000) >> 8) | (val16 & 0x000F)); 92 | UART1.BRR1.byte = (uint8_t) ((val16 & 0x0FF0) >> 4); 93 | 94 | // enable transmission 95 | UART1.CR2.reg.REN = 1; // enable receiver 96 | UART1.CR2.reg.TEN = 1; // enable sender 97 | 98 | #ifdef UART1_FIFO 99 | 100 | // init FIFOs for receive and transmit 101 | fifo_init(&m_UART1_Rx_Fifo); 102 | fifo_init(&m_UART1_Tx_Fifo); 103 | 104 | // enable Rx interrupt. Tx interrupt is enabled in uart1_send() 105 | UART1.CR2.reg.RIEN = 1; 106 | 107 | #endif // UART1_FIFO 108 | 109 | } // uart1_init 110 | 111 | 112 | 113 | /** 114 | \fn void uart1_send_byte(uint8_t data) 115 | 116 | \brief send byte via UART1 117 | 118 | \param[in] byte data to send 119 | 120 | send byte via UART1. 121 | 122 | For direct UART1 access: 123 | - send byte via UART1 directly 124 | 125 | If FIFO is used: 126 | - stores data into the Tx FIFO software buffer 127 | - enable the UART1 "Tx buffer empty" interrupt 128 | - actual transmission is handled by TXE interrupt routine 129 | */ 130 | void uart1_send_byte(uint8_t data) { 131 | 132 | // send data in background using FIFO 133 | #ifdef UART1_FIFO 134 | 135 | // wait until FIFO has free space 136 | while(FIFO_FULL(m_UART1_Tx_Fifo)); 137 | 138 | // disable UART1 "Tx empty interrupt" while manipulating Tx FIFO 139 | UART1.CR2.reg.TIEN = 0; 140 | 141 | // store byte in software FIFO 142 | fifo_enqueue(&m_UART1_Tx_Fifo, data); 143 | 144 | // enable UART1 "Tx empty interrupt" to handle sending data 145 | UART1.CR2.reg.TIEN = 1; 146 | 147 | 148 | // send data blocking without FIFO 149 | #else // UART1_FIFO 150 | 151 | // wait until TX buffer is available 152 | while (!(UART1.SR.reg.TXE)); 153 | 154 | // for 1-wire I/F (e.g. LIN) disable receiver 155 | #ifdef UART1_HALF_DUPLEX 156 | UART1.CR2.reg.REN = 0; 157 | #endif // UART1_HALF_DUPLEX 158 | 159 | // send byte 160 | UART1.DR.byte = data; 161 | 162 | // for 1-wire I/F (e.g. LIN) wait until byte is sent, then re-enable receiver 163 | #ifdef UART1_HALF_DUPLEX 164 | while (!(UART1.SR.reg.TXE)); 165 | UART1.CR2.reg.REN = 1; 166 | #endif // UART1_HALF_DUPLEX 167 | 168 | #endif // UART1_FIFO 169 | 170 | } // uart1_send_byte 171 | 172 | 173 | 174 | /** 175 | \fn void uart1_send_buf(uint16_t num, uint8_t *data) 176 | 177 | \brief send arry of bytes via UART1 178 | 179 | \param[in] num buf size in bytes 180 | \param[in] data bytes to send 181 | 182 | send array of bytes via UART1. 183 | 184 | For direct UART1 access: 185 | - send bytes via UART1 directly 186 | 187 | If FIFO is used: 188 | - stores data into the Tx FIFO software buffer 189 | - enable the UART1 "Tx buffer empty" interrupt 190 | - actual transmission is handled by TXE interrupt routine 191 | */ 192 | void uart1_send_buf(uint16_t num, uint8_t *data) { 193 | 194 | uint16_t i; 195 | 196 | // send data in background using FIFO 197 | #ifdef UART1_FIFO 198 | 199 | // wait until FIFO has enough free space 200 | while((FIFO_BUFFER_SIZE - m_UART1_Tx_Fifo.numBytes) < num); 201 | 202 | // disable UART1 "Tx empty interrupt" while manipulating Tx FIFO 203 | UART1.CR2.reg.TIEN = 0; 204 | 205 | // store bytes in software FIFO 206 | for (i=0; i= m_UART1_lenReset) 329 | SW_RESET; 330 | 331 | #endif // UART1_RST 332 | 333 | #endif // UART1_FIFO 334 | 335 | // return the FIFO data 336 | return(data); 337 | 338 | } // uart1_receive 339 | 340 | 341 | 342 | /** 343 | \fn uint8_t uart1_peek(void) 344 | 345 | \brief UART1 data peek function (only with FIFO) 346 | 347 | \return oldest, not treated data 348 | 349 | Required for FIFO operation: 350 | - checks if data exists in the Rx FIFO software buffer 351 | - if data exists, return oldest FIFO element 352 | - Rx FIFO is not altered 353 | */ 354 | #ifdef UART1_FIFO 355 | uint8_t uart1_peek(void) { 356 | 357 | uint8_t data=0; 358 | 359 | // disable UART1 "Rx full interrupt" while manipulating Rx FIFO 360 | UART1.CR2.reg.RIEN = 0; 361 | 362 | // get oldest FIFO element (or -128 if FIFO is empty) 363 | data = fifo_peek(&m_UART1_Rx_Fifo); 364 | 365 | // re-enable UART1 "Rx full interrupt" 366 | UART1.CR2.reg.RIEN = 1; 367 | 368 | // return the FIFO data 369 | return(data); 370 | 371 | } // uart1_peek 372 | #endif // UART1_FIFO 373 | 374 | 375 | 376 | /** 377 | \fn void uart1_RXF_ISR(void) 378 | 379 | \brief UART1 receive interrupt service routine 380 | 381 | Only for FIFO operation: 382 | - called if data received via UART1 383 | - copy data from HW buffer to FIFO 384 | */ 385 | #ifdef UART1_FIFO 386 | #if defined(__CSMC__) 387 | @near @interrupt void uart1_RXF_ISR(void) { 388 | #elif defined(__SDCC) 389 | void uart1_RXF_ISR() __interrupt(__UART1_RX_FULL_VECTOR__) { 390 | #endif 391 | 392 | uint8_t data; 393 | 394 | // clearing of ISR flag not required for STM8 395 | //UART1.SR.reg.RXNE 396 | 397 | // read byte from UART1 buffer 398 | data = UART1.DR.byte; 399 | 400 | // add a new byte to the FIFO buffer 401 | fifo_enqueue(&m_UART1_Rx_Fifo, data); 402 | 403 | // check for optional UART reset command 404 | #ifdef UART1_RST 405 | 406 | // check next byte vs. keyword 407 | if (data == m_UART1_cmdReset[m_UART1_idxReset]) 408 | m_UART1_idxReset++; 409 | else 410 | m_UART1_idxReset = 0; 411 | 412 | // trigger SW reset if full keyword received 413 | if (m_UART1_idxReset >= m_UART1_lenReset) 414 | SW_RESET; 415 | 416 | #endif // UART1_RST 417 | 418 | return; 419 | 420 | } // uart1_RXF_ISR 421 | #endif // UART1_FIFO 422 | 423 | 424 | 425 | /** 426 | \fn void uart1_TXE_ISR(void) 427 | 428 | \brief UART1 transmit interrupt service routine 429 | 430 | Only for FIFO operation: 431 | - called if Tx HW buffer is empty 432 | - checks if FIFO constains data 433 | - if yes, move oldest element from FIFO to Tx buffer 434 | - if FIFO is empty, disable this interrupt 435 | */ 436 | #ifdef UART1_FIFO 437 | #if defined(__CSMC__) 438 | @near @interrupt void uart1_TXE_ISR(void) { 439 | #elif defined(__SDCC) 440 | void uart1_TXE_ISR() __interrupt(__UART1_TX_CMPL_VECTOR__) { 441 | #endif 442 | 443 | uint8_t data; 444 | 445 | // clearing of ISR flag not required for STM8 446 | //UART1.SR.reg.TXE 447 | 448 | // if Tx FIFO contains data, get oldest element and send it 449 | if (FIFO_NOT_EMPTY(m_UART1_Tx_Fifo)) { 450 | 451 | // get Tx byte from FIFO 452 | data = fifo_dequeue(&m_UART1_Tx_Fifo); 453 | 454 | // for 1-wire I/F (e.g. LIN) disable receiver 455 | #ifdef UART1_HALF_DUPLEX 456 | UART1.CR2.reg.REN = 0; 457 | #endif // UART1_HALF_DUPLEX 458 | 459 | // send byte 460 | UART1.DR.byte = data; 461 | 462 | // for 1-wire I/F (e.g. LIN) wait until byte is sent, then re-enable receiver 463 | #ifdef UART1_HALF_DUPLEX 464 | while (!(UART1.SR.reg.TXE)); 465 | UART1.CR2.reg.REN = 1; 466 | #endif // UART1_HALF_DUPLEX 467 | 468 | } // Tx FIFO not empty 469 | 470 | // check again. If FIFO is empty now, deactivate this interrupt 471 | if (!(FIFO_NOT_EMPTY(m_UART1_Tx_Fifo))) 472 | UART1.CR2.reg.TIEN = 0; 473 | 474 | return; 475 | 476 | } // uart1_TXE_ISR 477 | #endif // UART1_FIFO 478 | 479 | 480 | /*----------------------------------------------------------------------------- 481 | END OF MODULE 482 | -----------------------------------------------------------------------------*/ 483 | -------------------------------------------------------------------------------- /STM8_Lib/uart1.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file uart1.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of UART1 / USART functions & macros 9 | 10 | declaration of UART1 / USART functions and macros. 11 | Optional functionality via #define: 12 | - UART1_HALF_DUPLEX: communication via 1-wire interface, e.g. LIN -> ignore echo after send (default: 2-wire) 13 | - UART1_FIFO: send/receive is in background via interrupts and SW FIFO (default: blocking w/o FIFO) 14 | */ 15 | 16 | /*----------------------------------------------------------------------------- 17 | MODULE DEFINITION FOR MULTIPLE INCLUSION 18 | -----------------------------------------------------------------------------*/ 19 | #ifndef _UART1_H_ 20 | #define _UART1_H_ 21 | 22 | #include 23 | #include "stm8as.h" 24 | 25 | 26 | /*----------------------------------------------------------------------------- 27 | DECLARATION OF GLOBAL FUNCTIONS 28 | -----------------------------------------------------------------------------*/ 29 | 30 | /// init UART1 for communication 31 | void uart1_init(uint32_t BR); 32 | 33 | /// send byte via UART1 34 | void uart1_send_byte(uint8_t data); 35 | 36 | /// send arry of bytes via UART1 37 | void uart1_send_buf(uint16_t num, uint8_t *buf); 38 | 39 | /// UART1 check if data was received 40 | uint8_t uart1_check_Rx(void); 41 | 42 | /// UART1 data receive function (if FIFO is used, remove data from it) 43 | uint8_t uart1_receive(void); 44 | 45 | // SW fifo uses interrupts 46 | #ifdef UART1_FIFO 47 | 48 | /// UART1 data peek function (only with FIFO, keep data in FIFO) 49 | uint8_t uart1_peek(void); 50 | 51 | // for Cosmic compiler 52 | #if defined(__CSMC__) 53 | 54 | /// with FIFO: UART1 transmit ISR; w/o FIFO: dummy 55 | @near @interrupt void uart1_TXE_ISR(void); 56 | 57 | /// with FIFO: UART1 receive ISR; w/o FIFO: dummy 58 | @near @interrupt void uart1_RXF_ISR(void); 59 | 60 | // for SDCC compiler 61 | #elif defined(__SDCC) 62 | 63 | /// with FIFO: UART1 transmit ISR; w/o FIFO: dummy 64 | void uart1_TXE_ISR(void) __interrupt(__UART1_TX_CMPL_VECTOR__); 65 | 66 | /// with FIFO: UART1 receive ISR; w/o FIFO: dummy 67 | void uart1_RXF_ISR(void) __interrupt(__UART1_RX_FULL_VECTOR__); 68 | 69 | #endif // __CSMC__ 70 | 71 | #endif // UART1_FIFO 72 | 73 | /*----------------------------------------------------------------------------- 74 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 75 | -----------------------------------------------------------------------------*/ 76 | #endif // _UART1_H_ 77 | -------------------------------------------------------------------------------- /STM8_Lib/uart3.c: -------------------------------------------------------------------------------- 1 | /** 2 | \file uart3.c 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief implementation of UART3 / LINUART functions/macros 9 | 10 | implementation of UART3 / LINUART functions and macros. 11 | Optional functionality via #define: 12 | - UART3_HALF_DUPLEX: communication via 1-wire interface, e.g. LIN -> ignore echo after send (default: 2-wire) 13 | - UART3_FIFO: send/receive is in background via interrupts and SW FIFO (default: blocking w/o FIFO) 14 | - UART3_RST: trigger SW reset on reception of "Re5eT!" 15 | */ 16 | 17 | /*----------------------------------------------------------------------------- 18 | INCLUDE FILES 19 | -----------------------------------------------------------------------------*/ 20 | #include 21 | #include 22 | #include "stm8as.h" 23 | #include "uart3.h" 24 | 25 | // configure SW FIFO (see sw_fifo.h) 26 | #ifdef UART3_FIFO 27 | #define FIFO_BUFFER_SIZE 64 ///< set buffer size for Rx and Tx (have to be same) 28 | #define FIFO_OPTIMIZE_RAM 0 ///< internal management. 1: save 2B RAM/FIFO; 0: save ~25B flash/FIFO and gain some speed 29 | #include "sw_fifo.h" // FIFO declaration and inline fuctions (for speed) 30 | #endif // UART3_FIFO 31 | 32 | 33 | /*----------------------------------------------------------------------------- 34 | MODULE VARIABLES (for clarity module internal variables start with "m_") 35 | -----------------------------------------------------------------------------*/ 36 | 37 | // if FIFO is used, reserve buffers for Rx and Tx 38 | #ifdef UART3_FIFO 39 | 40 | /// UART3 receive FIFO buffer 41 | fifo_t m_UART3_Rx_Fifo = { {0}, 0, 0, 0, 0 }; 42 | 43 | /// UART3 transmit FIFO buffer 44 | fifo_t m_UART3_Tx_Fifo = { {0}, 0, 0, 0, 0 }; 45 | 46 | #endif // UART3_FIFO 47 | 48 | 49 | // if UART reset command is used 50 | #ifdef UART3_RST 51 | 52 | /// reset command string 53 | uint8_t m_UART3_cmdReset[] = "Re5eT!"; 54 | 55 | /// length of command string 56 | uint8_t m_UART3_lenReset = 6; 57 | 58 | /// index for state machine 59 | uint8_t m_UART3_idxReset = 0; 60 | 61 | #endif // UART3_RST 62 | 63 | 64 | /*---------------------------------------------------------- 65 | FUNCTIONS 66 | ----------------------------------------------------------*/ 67 | 68 | /** 69 | \fn void uart3_init(uint32_t BR) 70 | 71 | \brief initialize UART3 for communication 72 | 73 | \param[in] BR baudrate [Baud] 74 | 75 | initialize UART3 for communication with specified baudrate. 76 | Use 1 start, 8 data and 1 stop bit; no parity or flow control 77 | 78 | If FIFO is used, initialize Rx and Tx FIFOs and activate Rx interrupt. 79 | */ 80 | void uart3_init(uint32_t BR) { 81 | 82 | volatile uint16_t val16; 83 | 84 | // set UART3 behaviour 85 | UART3.CR1.byte = 0x00; // enable UART3, 8 data bits, no parity control 86 | UART3.CR2.byte = 0x00; // no interrupts, disable sender/receiver 87 | UART3.CR3.byte = 0x00; // no LIN support, 1 stop bit, no clock output(?) 88 | 89 | // set baudrate (note: BRR2 must be written before BRR1!) 90 | val16 = (uint16_t) (((uint32_t) 16000000L)/BR); 91 | UART3.BRR2.byte = (uint8_t) (((val16 & 0xF000) >> 8) | (val16 & 0x000F)); 92 | UART3.BRR1.byte = (uint8_t) ((val16 & 0x0FF0) >> 4); 93 | 94 | // enable transmission 95 | UART3.CR2.reg.REN = 1; // enable receiver 96 | UART3.CR2.reg.TEN = 1; // enable sender 97 | 98 | #ifdef UART3_FIFO 99 | 100 | // init FIFOs for receive and transmit 101 | fifo_init(&m_UART3_Rx_Fifo); 102 | fifo_init(&m_UART3_Tx_Fifo); 103 | 104 | // enable Rx interrupt. Tx interrupt is enabled in uart3_send() 105 | UART3.CR2.reg.RIEN = 1; 106 | 107 | #endif // UART3_FIFO 108 | 109 | } // uart3_init 110 | 111 | 112 | 113 | /** 114 | \fn void uart3_send_byte(uint8_t data) 115 | 116 | \brief send byte via UART3 117 | 118 | \param[in] byte data to send 119 | 120 | send byte via UART3. 121 | 122 | For direct UART3 access: 123 | - send byte via UART3 directly 124 | 125 | If FIFO is used: 126 | - stores data into the Tx FIFO software buffer 127 | - enable the UART3 "Tx buffer empty" interrupt 128 | - actual transmission is handled by TXE interrupt routine 129 | */ 130 | void uart3_send_byte(uint8_t data) { 131 | 132 | // send data in background using FIFO 133 | #ifdef UART3_FIFO 134 | 135 | // wait until FIFO has free space 136 | while(FIFO_FULL(m_UART3_Tx_Fifo)); 137 | 138 | // disable UART3 "Tx empty interrupt" while manipulating Tx FIFO 139 | UART3.CR2.reg.TIEN = 0; 140 | 141 | // store byte in software FIFO 142 | fifo_enqueue(&m_UART3_Tx_Fifo, data); 143 | 144 | // enable UART3 "Tx empty interrupt" to handle sending data 145 | UART3.CR2.reg.TIEN = 1; 146 | 147 | 148 | // send data blocking without FIFO 149 | #else // UART3_FIFO 150 | 151 | // wait until TX buffer is available 152 | while (!(UART3.SR.reg.TXE)); 153 | 154 | // for 1-wire I/F (e.g. LIN) disable receiver 155 | #ifdef UART3_HALF_DUPLEX 156 | UART3.CR2.reg.REN = 0; 157 | #endif // UART3_HALF_DUPLEX 158 | 159 | // send byte 160 | UART3.DR.byte = data; 161 | 162 | // for 1-wire I/F (e.g. LIN) wait until byte is sent, then re-enable receiver 163 | #ifdef UART3_HALF_DUPLEX 164 | while (!(UART3.SR.reg.TXE)); 165 | UART3.CR2.reg.REN = 1; 166 | #endif // UART3_HALF_DUPLEX 167 | 168 | #endif // UART3_FIFO 169 | 170 | } // uart3_send_byte 171 | 172 | 173 | 174 | /** 175 | \fn void uart3_send_byte(uint8_t data) 176 | 177 | \brief send byte via UART3 178 | 179 | \param[in] byte data to send 180 | 181 | send byte via UART3. 182 | 183 | For direct UART3 access: 184 | - send byte via UART3 directly 185 | 186 | If FIFO is used: 187 | - stores data into the Tx FIFO software buffer 188 | - enable the UART1 "Tx buffer empty" interrupt 189 | - actual transmission is handled by TXE interrupt routine 190 | */ 191 | void uart3_send_buf(uint16_t num, uint8_t *data) { 192 | 193 | uint16_t i; 194 | 195 | // send data in background using FIFO 196 | #ifdef UART3_FIFO 197 | 198 | // wait until FIFO has enough free space 199 | while((FIFO_BUFFER_SIZE - m_UART3_Tx_Fifo.numBytes) < num); 200 | 201 | // disable UART3 "Tx empty interrupt" while manipulating Tx FIFO 202 | UART3.CR2.reg.TIEN = 0; 203 | 204 | // store bytes in software FIFO 205 | for (i=0; i= m_UART3_lenReset) 328 | SW_RESET; 329 | 330 | #endif // UART3_RST 331 | 332 | #endif // UART3_FIFO 333 | 334 | // return the FIFO data 335 | return(data); 336 | 337 | } // uart3_receive 338 | 339 | 340 | 341 | /** 342 | \fn uint8_t uart3_peek(void) 343 | 344 | \brief UART3 data peek function (only with FIFO) 345 | 346 | \return oldest, not treated data 347 | 348 | Required for FIFO operation: 349 | - checks if data exists in the Rx FIFO software buffer 350 | - if data exists, return oldest FIFO element 351 | - Rx FIFO is not altered 352 | */ 353 | #ifdef UART3_FIFO 354 | uint8_t uart3_peek(void) { 355 | 356 | uint8_t data=0; 357 | 358 | // disable UART3 "Rx full interrupt" while manipulating Rx FIFO 359 | UART3.CR2.reg.RIEN = 0; 360 | 361 | // get oldest FIFO element (or -128 if FIFO is empty) 362 | data = fifo_peek(&m_UART3_Rx_Fifo); 363 | 364 | // re-enable UART3 "Rx full interrupt" 365 | UART3.CR2.reg.RIEN = 1; 366 | 367 | // return the FIFO data 368 | return(data); 369 | 370 | } // uart3_peek 371 | #endif // UART3_FIFO 372 | 373 | 374 | 375 | /** 376 | \fn void uart3_RXF_ISR(void) 377 | 378 | \brief UART3 receive interrupt service routine 379 | 380 | Only for FIFO operation: 381 | - called if data received via UART3 382 | - copy data from HW buffer to FIFO 383 | */ 384 | #ifdef UART3_FIFO 385 | #if defined(__CSMC__) 386 | @near @interrupt void uart3_RXF_ISR(void) { 387 | #elif defined(__SDCC) 388 | void uart3_RXF_ISR() __interrupt(__UART2_3_4_RX_FULL_VECTOR__) { 389 | #endif 390 | 391 | uint8_t data; 392 | 393 | // clearing of ISR flag not required for STM8 394 | //UART3.SR.reg.RXNE 395 | 396 | // read byte from UART3 buffer 397 | data = UART3.DR.byte; 398 | 399 | // add a new byte to the FIFO buffer 400 | fifo_enqueue(&m_UART3_Rx_Fifo, data); 401 | 402 | // check for optional UART reset command 403 | #ifdef UART3_RST 404 | 405 | // check next byte vs. keyword 406 | if (data == m_UART3_cmdReset[m_UART3_idxReset]) 407 | m_UART3_idxReset++; 408 | else 409 | m_UART3_idxReset = 0; 410 | 411 | // trigger SW reset if full keyword received 412 | if (m_UART3_idxReset >= m_UART3_lenReset) 413 | SW_RESET; 414 | 415 | #endif // UART3_RST 416 | 417 | return; 418 | 419 | } // uart3_RXF_ISR 420 | #endif // UART3_FIFO 421 | 422 | 423 | 424 | /** 425 | \fn void uart3_TXE_ISR(void) 426 | 427 | \brief UART3 transmit interrupt service routine 428 | 429 | Only for FIFO operation: 430 | - called if Tx HW buffer is empty 431 | - checks if FIFO constains data 432 | - if yes, move oldest element from FIFO to Tx buffer 433 | - if FIFO is empty, disable this interrupt 434 | */ 435 | #ifdef UART3_FIFO 436 | #if defined(__CSMC__) 437 | @near @interrupt void uart3_TXE_ISR(void) { 438 | #elif defined(__SDCC) 439 | void uart3_TXE_ISR() __interrupt(__UART2_3_4_TX_CMPL_VECTOR__) { 440 | #endif 441 | 442 | uint8_t data; 443 | 444 | // clearing of ISR flag not required for STM8 445 | //UART3.SR.reg.TXE 446 | 447 | // if Tx FIFO contains data, get oldest element and send it 448 | if (FIFO_NOT_EMPTY(m_UART3_Tx_Fifo)) { 449 | 450 | // get Tx byte from FIFO 451 | data = fifo_dequeue(&m_UART3_Tx_Fifo); 452 | 453 | // for 1-wire I/F (e.g. LIN) disable receiver 454 | #ifdef UART3_HALF_DUPLEX 455 | UART3.CR2.reg.REN = 0; 456 | #endif // UART3_HALF_DUPLEX 457 | 458 | // send byte 459 | UART3.DR.byte = data; 460 | 461 | // for 1-wire I/F (e.g. LIN) wait until byte is sent, then re-enable receiver 462 | #ifdef UART3_HALF_DUPLEX 463 | while (!(UART3.SR.reg.TXE)); 464 | UART3.CR2.reg.REN = 1; 465 | #endif // UART3_HALF_DUPLEX 466 | 467 | } // Tx FIFO not empty 468 | 469 | // check again. If FIFO is empty now, deactivate this interrupt 470 | if (!(FIFO_NOT_EMPTY(m_UART3_Tx_Fifo))) 471 | UART3.CR2.reg.TIEN = 0; 472 | 473 | return; 474 | 475 | } // uart3_TXE_ISR 476 | #endif // UART3_FIFO 477 | 478 | 479 | /*----------------------------------------------------------------------------- 480 | END OF MODULE 481 | -----------------------------------------------------------------------------*/ 482 | -------------------------------------------------------------------------------- /STM8_Lib/uart3.h: -------------------------------------------------------------------------------- 1 | /** 2 | \file uart3.h 3 | 4 | \author G. Icking-Konert 5 | \date 2013-11-22 6 | \version 0.1 7 | 8 | \brief declaration of UART3 / LINUART functions & macros 9 | 10 | declaration of UART3 / LINUART functions and macros. 11 | Optional functionality via #define: 12 | - UART3_HALF_DUPLEX: communication via 1-wire interface, e.g. LIN -> ignore echo after send (default: 2-wire) 13 | - UART3_FIFO: send/receive is in background via interrupts and SW FIFO (default: blocking w/o FIFO) 14 | */ 15 | 16 | /*----------------------------------------------------------------------------- 17 | MODULE DEFINITION FOR MULTIPLE INCLUSION 18 | -----------------------------------------------------------------------------*/ 19 | #ifndef _UART3_H_ 20 | #define _UART3_H_ 21 | 22 | #include 23 | #include "stm8as.h" 24 | 25 | 26 | /*----------------------------------------------------------------------------- 27 | DECLARATION OF GLOBAL FUNCTIONS 28 | -----------------------------------------------------------------------------*/ 29 | 30 | /// init UART3 for communication 31 | void uart3_init(uint32_t BR); 32 | 33 | /// send byte via UART3 34 | void uart3_send_byte(uint8_t data); 35 | 36 | /// send arry of bytes via UART3 37 | void uart3_send_buf(uint16_t num, uint8_t *buf); 38 | 39 | /// UART3 check if data was received 40 | uint8_t uart3_check_Rx(void); 41 | 42 | /// UART3 data receive function (if FIFO is used, remove data from it) 43 | uint8_t uart3_receive(void); 44 | 45 | 46 | // SW fifo uses interrupts 47 | #ifdef UART3_FIFO 48 | 49 | /// UART3 data peek function (only with FIFO, keep data in FIFO) 50 | uint8_t uart3_peek(void); 51 | 52 | // for Cosmic compiler 53 | #if defined(__CSMC__) 54 | 55 | /// with FIFO: UART3 transmit ISR; w/o FIFO: dummy 56 | @near @interrupt void uart3_TXE_ISR(void); 57 | 58 | /// with FIFO: UART3 receive ISR; w/o FIFO: dummy 59 | @near @interrupt void uart3_RXF_ISR(void); 60 | 61 | // for SDCC compiler 62 | #elif defined(__SDCC) 63 | 64 | /// with FIFO: UART3 transmit ISR; w/o FIFO: dummy 65 | void uart3_TXE_ISR(void) __interrupt(__UART2_3_4_TX_CMPL_VECTOR__); 66 | 67 | /// with FIFO: UART3 receive ISR; w/o FIFO: dummy 68 | void uart3_RXF_ISR(void) __interrupt(__UART2_3_4_RX_FULL_VECTOR__); 69 | 70 | #endif // __CSMC__ 71 | 72 | #endif // UART3_FIFO 73 | 74 | /*----------------------------------------------------------------------------- 75 | END OF MODULE DEFINITION FOR MULTIPLE INLUSION 76 | -----------------------------------------------------------------------------*/ 77 | #endif // _UART3_H_ 78 | -------------------------------------------------------------------------------- /ax25.c: -------------------------------------------------------------------------------- 1 | #include "ax25.h" 2 | 3 | void ax25_dummy(void) { 4 | return; 5 | } 6 | -------------------------------------------------------------------------------- /ax25.h: -------------------------------------------------------------------------------- 1 | #ifndef __AX25_H_ 2 | #define __AX25_H_ 3 | 4 | void ax25_dummy(void); 5 | 6 | #endif /* __AX25_H_ */ 7 | 8 | -------------------------------------------------------------------------------- /hc12pj.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Pin# | Function | board pin/signal 3 | * 1 | PD4 | Si4463 p1 - SDN 4 | * 2 | PD5/UART1_TX | header TXD pad via level shifter 5 | * 3 | PD6/UART1_RX | header RXD pad via level shifter 6 | * 4 | nRST | reset test point nearest header RXD pad 7 | * 11 | PB5 | header SET pad via level shifter 8 | * 12 | PB4 | Si4463 p9 - GPIO0 9 | * 13 | PC3 | Si4463 p10 - GPIO1 10 | * 14 | PC4 | Si4463 p11 - nIRQ 11 | * 15 | PC5/SPI_SCK | Si4463 p12 - SCLK 12 | * 16 | PC6/SPI_MOSI | Si4463 p14 - SDI 13 | * 17 | PC7/SPI_MISO | Si4463 p13 - SDO 14 | * 18 | PD1/SWIM | SWIM test point nearest header TXD pad 15 | * 19 | PD2 | Si4463 p15 - nSEL 16 | */ 17 | 18 | #include 19 | #include 20 | #define _MAIN_ 21 | #include "stm8as.h" 22 | #include "uart1.h" 23 | #include "gpio.h" 24 | 25 | #undef _MAIN_ 26 | 27 | #include "si446x.h" 28 | #include "ax25.h" 29 | 30 | inline void setup_platform(void) { 31 | DISABLE_INTERRUPTS; 32 | 33 | CLK.CKDIVR.byte = 0x00; 34 | gpio_init(&PORT_D, PIN_2, OUTPUT_PUSHPULL_FAST); /* nSEL */ 35 | gpio_init(&PORT_D, PIN_4, OUTPUT_PUSHPULL_FAST); /* SDN */ 36 | GPIO_SET(PORT_D, PIN_4, 0); /* SDN low by default */ 37 | 38 | gpio_init(&PORT_B, PIN_4, INPUT_PULLUP_NOEXINT); /* GPIO0 input */ 39 | gpio_init(&PORT_C, PIN_3, INPUT_PULLUP_NOEXINT); /* GPIO1 input */ 40 | 41 | gpio_init(&PORT_D, PIN_5, OUTPUT_PUSHPULL_FAST); /* UART1 TxD */ 42 | gpio_init(&PORT_D, PIN_6, INPUT_PULLUP_NOEXINT); /* UART1 RxD */ 43 | 44 | 45 | uart1_init(115200L); 46 | si446x_init(); 47 | ENABLE_INTERRUPTS; 48 | } 49 | 50 | void main (void) { 51 | 52 | setup_platform(); 53 | printf("Hello!\n"); 54 | si446x_dummy(); 55 | while(1); 56 | 57 | } 58 | -------------------------------------------------------------------------------- /si446x.c: -------------------------------------------------------------------------------- 1 | #include "si446x.h" 2 | #include 3 | #include 4 | #include "gpio.h" 5 | 6 | 7 | const uint8_t config_array[] = RADIO_CONFIGURATION_DATA_ARRAY; 8 | 9 | void si446x_busy_cs(void) { 10 | uint32_t f=5000; 11 | while(f>0) { 12 | f--; 13 | } 14 | } 15 | 16 | void si446x_spi_frame_start(void) { 17 | register uint8_t i; 18 | /* assert nSEL */ 19 | GPIO_SET(PORT_D, PIN_2, 0); 20 | si446x_busy_cs(); 21 | 22 | // disable interrupts to prevent timing issue 23 | DISABLE_INTERRUPTS; 24 | 25 | // clear MISO buffer 26 | i = SPI.DR.byte; 27 | } 28 | 29 | void si446x_spi_frame_end(void) { 30 | // re-enable interrupts 31 | ENABLE_INTERRUPTS; 32 | 33 | // wait until not busy 34 | while (SPI.SR.reg.BSY); 35 | 36 | /* deassert nSEL */ 37 | GPIO_SET(PORT_D, PIN_2, 1); 38 | si446x_busy_cs(); 39 | } 40 | 41 | uint8_t si446x_spi_shift_byte(uint8_t tx) { 42 | while(!SPI.SR.reg.TXE) { 43 | _NOP_; 44 | } 45 | 46 | SPI.DR.byte = tx; 47 | 48 | while(!SPI.SR.reg.RXNE) { 49 | _NOP_; 50 | } 51 | return SPI.DR.byte; 52 | } 53 | 54 | uint8_t si446x_spi_cmd_resp(char* buf, uint8_t size, uint16_t timeout) { 55 | 56 | uint8_t i; 57 | 58 | while(timeout > 0) { 59 | si446x_spi_frame_start(); 60 | i = 0; 61 | 62 | // loop over numTx bytes 63 | while (i < (size + 2)) { 64 | 65 | if(i == 0) { 66 | buf[0] = si446x_spi_shift_byte(0x44); 67 | } else if (i == 1) { 68 | if(si446x_spi_shift_byte(0xFF) != 0xFF) { 69 | timeout--; 70 | break; 71 | } 72 | } else { 73 | buf[i-2] = si446x_spi_shift_byte(0xFF); 74 | } 75 | i++; 76 | } // send/receive loop 77 | 78 | si446x_spi_frame_end(); 79 | if (i > 1) { 80 | return 1; /* data was read */ 81 | } 82 | } 83 | return 0; /* timed out waiting for CTS */ 84 | } 85 | 86 | void si446x_spi_cmd_send(char* buf, uint8_t size) { 87 | 88 | uint8_t i; 89 | uint8_t dummy; 90 | si446x_spi_frame_start(); 91 | i = 0; 92 | // loop over numTx bytes 93 | while (i < size) { 94 | 95 | si446x_spi_shift_byte(buf[i]); 96 | 97 | i++; 98 | } // send/receive loop 99 | 100 | si446x_spi_frame_end(); 101 | } 102 | 103 | 104 | void si446x_init(void) { 105 | printf("si446x_init()\n"); 106 | 107 | 108 | /* dirty SPI init */ 109 | SPI.CR1.byte=0x14; /* SPI off, master mode, CLK/8, CPOL=CPHA=0, MSB first */ 110 | SPI.CR2.byte=0x01; /* no NSS, full duplex, no CRC */ 111 | SPI.CR1.byte=0x54; /* enable SPI */ 112 | 113 | /* set up SPI GPIOs */ 114 | gpio_init(&PORT_C, PIN_5, OUTPUT_PUSHPULL_FAST); /* SCK */ 115 | gpio_init(&PORT_C, PIN_6, OUTPUT_PUSHPULL_FAST); /* MOSI */ 116 | gpio_init(&PORT_C, PIN_7, INPUT_PULLUP_NOEXINT); /* MISO */ 117 | 118 | GPIO_SET(PORT_D, PIN_2, 1); 119 | 120 | /* toggle shutdown to reset Si446x */ 121 | GPIO_SET(PORT_D, PIN_4, 1); 122 | si446x_busy_cs(); 123 | GPIO_SET(PORT_D, PIN_4, 0); 124 | si446x_busy_cs(); 125 | } 126 | 127 | /* stolen from https://github.com/alexander-sholohov/si4463-beacon.git */ 128 | uint8_t si446x_play_cmds(const uint8_t *cmds) { 129 | uint8_t buf[16]; 130 | uint8_t size, bufidx; 131 | 132 | while(*cmds != 0) { 133 | size = *cmds; 134 | if( size > 16) { 135 | return 0; 136 | } 137 | cmds++; 138 | for(bufidx=0; bufidx