diff --git a/lib/flash_sdcard.c b/lib/flash_sdcard.c deleted file mode 100644 index b1c394d..0000000 --- a/lib/flash_sdcard.c +++ /dev/null @@ -1,611 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to communicate with an SD card flash memory using the SPI mode (code) - * @file flash_sdcard.c - * @author King Kévin - * @date 2017 - * @note peripherals used: SPI @ref flash_sdcard_spi - * @warning all calls are blocking - * @implements SD Specifications, Part 1, Physical Layer, Simplified Specification, Version 6.00, 10 April 10 2017 - * @todo use SPI unidirectional mode, use DMA, force/wait going to idle state when initializing, filter out reserved values, check sector against size - */ - -/* standard libraries */ -#include // standard integer types -#include // general utilities - -/* STM32 (including CM3) libraries */ -#include // Cortex M3 utilities -#include // real-time control clock library -#include // general purpose input output library -#include // SPI library - -#include "global.h" // global utilities -#include "flash_sdcard.h" // SD card header and definitions - -/** @defgroup flash_sdcard_spi SPI used to communication with SD card - * @{ - */ -#define FLASH_SDCARD_SPI 1 /**< SPI peripheral */ -/** @} */ - -/** if the card has been initialized successfully */ -static bool initialized = false; -/** maximum N_AC value (in 8-clock cycles) (time between the response token R1 and data block when reading data (see section 7.5.4) - * @note this is set to N_CR until we can read CSD (see section 7.2.6) - */ -static uint32_t n_ac = 8; -/** is it a Standard Capacity SD card (true), or High Capacity SD cards (false) - * @note this is indicated in the Card Capacity Status bit or OCR (set for high capacity) - * @note this is important for addressing: for standard capacity cards the address is the byte number, for high capacity cards it is the 512-byte block number - */ -static bool sdsc = false; -/** size of card in bytes */ -static uint64_t sdcard_size = 0; -/** size of an erase block bytes */ -static uint32_t erase_size = 0; - -/** table for CRC-7 calculation for the command messages (see section 4.5) - * @note faster than calculating the CRC and doesn't cost a lot of space - * @note generated using pycrc --width=7 --poly=0x09 --reflect-in=false --reflect-out=false --xor-in=0x00 --xor-out=0x00 --generate=table - */ -static const uint8_t crc7_table[] = { - 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f, 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77, - 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26, 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e, - 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d, 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45, - 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14, 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c, - 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b, 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13, - 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42, 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a, - 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69, 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21, - 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70, 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38, - 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e, 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36, - 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67, 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f, - 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c, 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04, - 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55, 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d, - 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a, 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52, - 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03, 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b, - 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28, 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60, - 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31, 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79 -}; - -/** wait one SPI round (one SPI word) - */ -static void flash_sdcard_spi_wait(void) -{ - spi_send(SPI(FLASH_SDCARD_SPI), 0xffff); // send not command token (i.e. starting with 1) -} - -/** read one SPI word - * @return SPI word read - */ -static uint16_t flash_sdcard_spi_read(void) -{ - spi_send(SPI(FLASH_SDCARD_SPI), 0xffff); // send not command token (i.e. starting with 1) - (void)SPI_DR(SPI(FLASH_SDCARD_SPI)); // clear RXNE flag (by reading previously received data (not done by spi_read or spi_xref) - while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until Tx buffer is empty before clearing the (previous) RXNE flag - while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_RXNE)); // wait for next data to be available - return SPI_DR(SPI(FLASH_SDCARD_SPI)); // return received adat -} - -/** test if card is present - * @return if card has been detected - * @note this use the SD card detection mechanism (CD/CS is high card is inserted due to the internal 50 kOhm resistor) - */ -static bool flash_sdcard_card_detect(void) -{ - rcc_periph_clock_enable(RCC_SPI_NSS_PORT(FLASH_SDCARD_SPI)); // enable clock for NSS pin port peripheral for SD card CD signal - gpio_set_mode(SPI_NSS_PORT(FLASH_SDCARD_SPI), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, SPI_NSS_PIN(FLASH_SDCARD_SPI)); // set NSS pin as input to read CD signal - gpio_clear(SPI_NSS_PORT(FLASH_SDCARD_SPI), SPI_NSS_PIN(FLASH_SDCARD_SPI)); // pull pin low to avoid false positive when card in not inserted - return (0!=gpio_get(SPI_NSS_PORT(FLASH_SDCARD_SPI), SPI_NSS_PIN(FLASH_SDCARD_SPI))); // read CD signal: is card is present the internal 50 kOhm pull-up resistor will override our 1 MOhm pull-down resistor and set the signal high (see section 6.2) -} - -/** transmit command token - * @param[in] index command index - * @param[in] argument command argument - */ -static void flash_sdcard_send_command(uint8_t index, uint32_t argument) -{ - uint8_t command[5] = { 0x40+(index&0x3f), argument>>24, argument>>16, argument>>8, argument>>0 }; // commands are 5 bytes long, plus 1 bytes of CRC (see section 7.3.1.1) - uint8_t crc7 = 0x00; // CRC-7 checksum for command message - // send command - for (uint8_t i=0; i>8); // save byte - data[i*2+1] = (word>>0); // save byte - } - flash_sdcard_spi_read(); // read CRC (the CRC after the data block should clear the computed CRC) - if (SPI_CRC_RXR(FLASH_SDCARD_SPI)) { // CRC is wrong - token = 0xff; - } else { // no error occurred - token = 0; - } - // switch back to 8-bit SPI frames - while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until the end of any transmission - while (SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_BSY); // wait until not busy before disabling - spi_disable(SPI(FLASH_SDCARD_SPI)); // disable SPI to change format - spi_disable_crc(SPI(FLASH_SDCARD_SPI)); // disable CRC since we don't use it anymore (and this allows us to clear the CRC next time we use it) - spi_set_dff_8bit(SPI(FLASH_SDCARD_SPI)); // set SPI frame to 8 bits - spi_enable(SPI(FLASH_SDCARD_SPI)); // enable SPI back - } else { // start block token not received - token = 0xff; - } - - return token; -} - -/** write a data block - * @param[in] data data block to write - * @param[in] size size of response to read (a multiple of 2) - * @return data response token (0xff for other errors) - */ -static uint8_t flash_sdcard_write_block(uint8_t* data, size_t size) -{ - if (size%2 || 0==size || NULL==data) { // can't (and shouldn't) read odd number of bytes - return 0xff; - } - - spi_send(SPI(FLASH_SDCARD_SPI), 0xfe); // send start block token (see section 7.3.3.2) - - // switch to 16-bits SPI data frame so we can use use built-in CRC-16 - while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until the end of any transmission - while (SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_BSY); // wait until not busy before disabling - spi_disable(SPI(FLASH_SDCARD_SPI)); // disable SPI to change format - spi_set_dff_16bit(SPI(FLASH_SDCARD_SPI)); // set SPI frame to 16 bits - SPI_CRC_PR(FLASH_SDCARD_SPI) = 0x1021; // set CRC-16-CCITT polynomial for data blocks (x^16+x^12+x^5+1) (see section 7.2.3) - spi_enable_crc(SPI(FLASH_SDCARD_SPI)); // enable and clear CRC - spi_enable(SPI(FLASH_SDCARD_SPI)); // enable SPI back - // send block data (ideally use DMA, but switching makes it more complex and this part doesn't take too much time) - for (size_t i=0; i>6)) || (!sdsc && 0==(csd[0]>>6))) { - return false; - } - // calculate N_AC value (we use our set minimum frequency 16 MHz to calculate time) - if (sdsc) { // calculate N_AC using TAAC and NSAC - static const float TAAC_UNITS[] = {1E-9, 10E-9, 100E-9, 1E-6, 10E-6, 100E-6, 1E-3, 10E-3}; // (see table 5-5) - static const float TAAC_VALUES[] = {10.0, 1.0, 1.2, 1.3, 1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 7.0, 8.0}; // (see table 5-5) - double taac = TAAC_VALUES[(csd[1]>>2)&0xf]*TAAC_UNITS[csd[1]&0x7]; // time in ns - n_ac=100*((taac*16E6)+(csd[2]*100))/8; // (see section 7.5.4) - } else { // value is fixed to 100 ms - n_ac=100E-3*16E6/8; - } - // calculate card size - if (sdsc) { // see section 5.3.2 - uint16_t c_size = (((uint16_t)csd[6]&0x03)<<10)+((uint16_t)csd[7]<<2)+(csd[8]>>6); - uint8_t c_size_mutl = ((csd[9]&0x03)<<1)+((csd[10]&0x80)>>7); - uint8_t read_bl_len = (csd[5]&0x0f); - sdcard_size = ((c_size+1)*(1UL<<(c_size_mutl+2)))*(1UL<>7)+1)<<(((csd[12]&0x03)<<2)+(csd[13]>>6)); - } else { - uint8_t status[64] = {0}; // SD status (see section 4.10.2) - uint16_t r2 = flash_sdcard_status(status); // get status (see table 7-4) - if (r2) { // error occurred - return false; - } - erase_size = (8192UL<<(status[10]>>4)); // calculate erase size (see table 4-44, section 4.10.2.4) - } - - // ensure block length is 512 bytes for SDSC (should be per default) to we match SDHC/SDXC block size - if (sdsc) { - r1 = flash_sdcard_command_response(16, 512, NULL, 0); // set block size using CMD16 (SET_BLOCKLEN) (see table 7-3) - if (r1) { // error occurred - return false; - } - } - - // try to switch to high speed mode (see section 7.2.14/4.3.10) - if (csd[4]&0x40) { // ensure CMD6 is supported by checking if command class 10 is set - uint32_t n_ac_back = n_ac; // backup N_AC - n_ac = 100E-3*16E6/8; // temporarily set timeout to 100 ms (see section 4.3.10.1) - // query access mode (group function 1) to check if high speed is supported (fPP=50MHz at 3.3V, we can be faster) - uint8_t fnc[64] = {0}; // function status response (see table 4-12) - r1 = flash_sdcard_data_read(6, 0x00fffff1, fnc, sizeof(fnc)); // check high speed function using CMD6 (SWITCH_FUNC) to check (mode 0) access mode (function group 1) (see table 7-3/4-30) - if (r1) { // error occurred - return false; - } - if (0x1==(fnc[16]&0x0f)) { // we can to access mode function 1 (see table 4-12) - r1 = flash_sdcard_data_read(6, 0x80fffff1, fnc, sizeof(fnc)); // switch to high speed function using CMD6 (SWITCH_FUNC) to switch (mode 1) access mode (function group 1) (see table 7-3/4-30) - if (r1) { // error occurred - return false; - } - if (0x1!=(fnc[16]&0x0f)) { // could not switch to high speed - return false; - } - // we can switch clock frequency to fPP (max. 50 MHz) (see section 6.6.7) - while (!(SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_TXE)); // wait until the end of any transmission - while (SPI_SR(SPI(FLASH_SDCARD_SPI))&SPI_SR_BSY); // wait until not busy before disabling - spi_disable(SPI(FLASH_SDCARD_SPI)); // disable SPI to change clock speed - spi_set_baudrate_prescaler(SPI(FLASH_SDCARD_SPI), SPI_CR1_BR_FPCLK_DIV_2); // set clock speed to 36 MHz (72/2=36 < 50 MHz) - spi_enable(SPI(FLASH_SDCARD_SPI)); // enable SPI back - n_ac_back /= 2; // since we go twice faster the N_AC timeout has to be halved - } - n_ac = n_ac_back; // restore N_AC - } - - initialized = true; - - return initialized; -} - -uint64_t flash_sdcard_size(void) -{ - return sdcard_size; -} - -uint32_t flash_sdcard_erase_size(void) -{ - return erase_size; -} - -bool flash_sdcard_read_data(uint32_t block, uint8_t* data) -{ - if (NULL==data) { - return false; - } - if (sdsc) { // the address for standard capacity cards must be provided in bytes - if (block>UINT32_MAX/512) { // check for integer overflow - return false; - } else { - block *= 512; // calculate byte address from block address - } - } - return (0==flash_sdcard_data_read(17, block, data, 512)); // read single data block using CMD17 (READ_SINGLE_BLOCK) (see table 7-3) -} - -bool flash_sdcard_write_data(uint32_t block, uint8_t* data) -{ - if (NULL==data) { - return false; - } - - if (sdsc) { // the address for standard capacity cards must be provided in bytes - if (block>UINT32_MAX/512) { // check for integer overflow - return false; - } else { - block *= 512; // calculate byte address from block address - } - } - - uint8_t drt = flash_sdcard_data_write(24, block, data, 512); // write single data block using CMD24 (WRITE_SINGLE_BLOCK) (see table 7-3) - if (0x05!=(drt&0x1f)) { // write block failed - return false; - } - - // get status to check if programming succeeded - uint8_t r2[1] = {0}; // to store response token R2 (see section 7.3.2.3) - uint8_t r1 = flash_sdcard_command_response(13, 0, r2, sizeof(r2)); // get SD status using CMD13 (SEND_STATUS) (see table 7-3) - if (0x00!=r1) { // error occurred - return false; - } else if (r2[0]) { // programming error - return false; - } - - return true; // programming succeeded -} diff --git a/lib/flash_sdcard.h b/lib/flash_sdcard.h deleted file mode 100644 index 7b3c718..0000000 --- a/lib/flash_sdcard.h +++ /dev/null @@ -1,47 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to communicate with an SD card flash memory using the SPI mode (API) - * @file flash_sdcard.h - * @author King Kévin - * @date 2017 - * @note peripherals used: SPI @ref flash_sdcard_spi - * @warning all calls are blocking - */ -#pragma once - -/** setup communication with SD card - * @return if card has been initialized correctly - */ -bool flash_sdcard_setup(void); -/** get size of SD card flash memory - * @return size of SD card flash memory (in bytes) - */ -uint64_t flash_sdcard_size(void); -/** get size of a erase block - * @return size of a erase block (in bytes) - */ -uint32_t flash_sdcard_erase_size(void); -/** read data on flash of SD card - * @param[in] block address of data to read (in block in 512 bytes unit) - * @param[out] data data block to read (with a size of 512 bytes) - * @return if read succeeded - */ -bool flash_sdcard_read_data(uint32_t block, uint8_t* data); -/** write data on flash of SD card - * @param[in] block address of data to write (in block in 512 bytes unit) - * @param[in] data data block to write (with a size of 512 bytes) - * @return if write succeeded - */ -bool flash_sdcard_write_data(uint32_t block, uint8_t* data); diff --git a/lib/i2c_master.c b/lib/i2c_master.c deleted file mode 100644 index fc392da..0000000 --- a/lib/i2c_master.c +++ /dev/null @@ -1,633 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to communicate using I2C as master (code) - * @file i2c_master.c - * @author King Kévin - * @date 2017-2018 - * @note peripherals used: I2C - */ - -/* standard libraries */ -#include // standard integer types -#include // general utilities - -/* STM32 (including CM3) libraries */ -#include // real-time control clock library -#include // general purpose input output library -#include // I2C library - -/* own libraries */ -#include "global.h" // global utilities -#include "i2c_master.h" // I2C header and definitions - -/** get RCC for I2C based on I2C identifier - * @param[in] i2c I2C base address - * @return RCC address for I2C peripheral - */ -static uint32_t RCC_I2C(uint32_t i2c) -{ - switch (i2c) { - case I2C1: - return RCC_I2C1; - break; - case I2C2: - return RCC_I2C2; - break; - default: - while (true); - } -} - -/** get RCC for GPIO port for SCL pin based on I2C identifier - * @param[in] i2c I2C base address - * @return RCC GPIO address - */ -static uint32_t RCC_GPIO_PORT_SCL(uint32_t i2c) -{ - switch (i2c) { - case I2C1: - case I2C2: - return RCC_GPIOB; - break; - default: - while (true); - } -} - -/** get RCC for GPIO port for SDA pin based on I2C identifier - * @param[in] i2c I2C base address - * @return RCC GPIO address - */ -static uint32_t RCC_GPIO_PORT_SDA(uint32_t i2c) -{ - switch (i2c) { - case I2C1: - case I2C2: - return RCC_GPIOB; - break; - default: - while (true); - } -} - -/** get GPIO port for SCL pin based on I2C identifier - * @param[in] i2c I2C base address - * @return GPIO address - */ -static uint32_t GPIO_PORT_SCL(uint32_t i2c) -{ - switch (i2c) { - case I2C1: - if (AFIO_MAPR & AFIO_MAPR_I2C1_REMAP) { - return GPIO_BANK_I2C1_RE_SCL; - } else { - return GPIO_BANK_I2C1_SCL; - } - break; - case I2C2: - return GPIO_BANK_I2C2_SCL; - break; - default: - while (true); - } -} - -/** get GPIO port for SDA pin based on I2C identifier - * @param[in] i2c I2C base address - * @return GPIO address - */ -static uint32_t GPIO_PORT_SDA(uint32_t i2c) -{ - switch (i2c) { - case I2C1: - if (AFIO_MAPR & AFIO_MAPR_I2C1_REMAP) { - return GPIO_BANK_I2C1_RE_SDA; - } else { - return GPIO_BANK_I2C1_SDA; - } - break; - case I2C2: - return GPIO_BANK_I2C2_SDA; - break; - default: - while (true); - } -} - -/** get GPIO pin for SCL pin based on I2C identifier - * @param[in] i2c I2C base address - * @return GPIO address - */ -static uint32_t GPIO_PIN_SCL(uint32_t i2c) -{ - switch (i2c) { - case I2C1: - if (AFIO_MAPR & AFIO_MAPR_I2C1_REMAP) { - return GPIO_I2C1_RE_SCL; - } else { - return GPIO_I2C1_SCL; - } - break; - case I2C2: - return GPIO_I2C2_SCL; - break; - default: - while (true); - } -} - -/** get GPIO pin for SDA pin based on I2C identifier - * @param[in] i2c I2C base address - * @return GPIO address - */ -static uint32_t GPIO_PIN_SDA(uint32_t i2c) -{ - switch (i2c) { - case I2C1: - if (AFIO_MAPR & AFIO_MAPR_I2C1_REMAP) { - return GPIO_I2C1_RE_SDA; - } else { - return GPIO_I2C1_SDA; - } - break; - case I2C2: - return GPIO_I2C2_SDA; - break; - default: - while (true); - } -} - -void i2c_master_setup(uint32_t i2c, uint16_t frequency) -{ - // check I2C peripheral - if (I2C1!=i2c && I2C2!=i2c) { - while (true); - } - - // configure I2C peripheral - rcc_periph_clock_enable(RCC_GPIO_PORT_SCL(i2c)); // enable clock for I2C I/O peripheral - gpio_set(GPIO_PORT_SCL(i2c), GPIO_PIN_SCL(i2c)); // already put signal high to avoid small pulse - gpio_set_mode(GPIO_PORT_SCL(i2c), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_PIN_SCL(i2c)); // setup I2C I/O pins - rcc_periph_clock_enable(RCC_GPIO_PORT_SDA(i2c)); // enable clock for I2C I/O peripheral - gpio_set(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c)); // already put signal high to avoid small pulse - gpio_set_mode(GPIO_PORT_SDA(i2c), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_PIN_SDA(i2c)); // setup I2C I/O pins - rcc_periph_clock_enable(RCC_AFIO); // enable clock for alternate function - rcc_periph_clock_enable(RCC_I2C(i2c)); // enable clock for I2C peripheral - i2c_reset(i2c); // reset peripheral domain - i2c_peripheral_disable(i2c); // I2C needs to be disable to be configured - I2C_CR1(i2c) |= I2C_CR1_SWRST; // reset peripheral - I2C_CR1(i2c) &= ~I2C_CR1_SWRST; // clear peripheral reset - if (0==frequency) { // don't allow null frequency - frequency = 1; - } else if (frequency>400) { // limit frequency to 400 kHz - frequency = 400; - } - i2c_set_clock_frequency(i2c, rcc_apb1_frequency/1000000); // configure the peripheral clock to the APB1 freq (where it is connected to) - if (frequency>100) { // use fast mode for frequencies over 100 kHz - i2c_set_fast_mode(i2c); // set fast mode (Fm) - i2c_set_ccr(i2c, rcc_apb1_frequency/(frequency*1000*2)); // set Thigh/Tlow to generate frequency (fast duty not used) - i2c_set_trise(i2c, (300/(1000/(rcc_apb1_frequency/1000000)))+1); // max rise time for Fm mode (< 400) kHz is 300 ns - } else { // use fast mode for frequencies below 100 kHz - i2c_set_standard_mode(i2c); // set standard mode (Sm) - i2c_set_ccr(i2c, rcc_apb1_frequency/(frequency*1000*2)); // set Thigh/Tlow to generate frequency of 100 kHz - i2c_set_trise(i2c, (1000/(1000/(rcc_apb1_frequency/1000000)))+1); // max rise time for Sm mode (< 100 kHz) is 1000 ns (~1 MHz) - } - i2c_peripheral_enable(i2c); // enable I2C after configuration completed -} - -void i2c_master_release(uint32_t i2c) -{ - // check I2C peripheral - if (I2C1!=i2c && I2C2!=i2c) { - while (true); - } - - i2c_reset(i2c); // reset I2C peripheral configuration - i2c_peripheral_disable(i2c); // disable I2C peripheral - rcc_periph_clock_disable(RCC_I2C(i2c)); // disable clock for I2C peripheral - gpio_set_mode(GPIO_PORT_SCL(i2c), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN_SCL(i2c)); // put I2C I/O pins back to floating - gpio_set_mode(GPIO_PORT_SDA(i2c), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO_PIN_SDA(i2c)); // put I2C I/O pins back to floating -} - -bool i2c_master_check_signals(uint32_t i2c) -{ - // check I2C peripheral - if (I2C1!=i2c && I2C2!=i2c) { - while (true); - } - - // pull SDA and SDC low to check if there are pull-up resistors - uint32_t sda_crl = GPIO_CRL(GPIO_PORT_SDA(i2c)); // backup port configuration - uint32_t sda_crh = GPIO_CRH(GPIO_PORT_SDA(i2c)); // backup port configuration - uint32_t sda_bsrr = GPIO_BSRR(GPIO_PORT_SDA(i2c)); // backup port configuration - uint32_t scl_crl = GPIO_CRL(GPIO_PORT_SCL(i2c)); // backup port configuration - uint32_t scl_crh = GPIO_CRH(GPIO_PORT_SCL(i2c)); // backup port configuration - uint32_t scl_bsrr = GPIO_BSRR(GPIO_PORT_SCL(i2c)); // backup port configuration - gpio_set_mode(GPIO_PORT_SDA(i2c), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_PIN_SDA(i2c)); // configure signal as pull down - gpio_set_mode(GPIO_PORT_SCL(i2c), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, GPIO_PIN_SCL(i2c)); // configure signal as pull down - gpio_clear(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c)); // pull down - gpio_clear(GPIO_PORT_SCL(i2c), GPIO_PIN_SCL(i2c)); // pull down - bool to_return = (0!=gpio_get(GPIO_PORT_SCL(i2c), GPIO_PIN_SCL(i2c)) && 0!=gpio_get(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c))); // check if the signals are still pulled high by external stronger pull-up resistors - GPIO_CRL(GPIO_PORT_SDA(i2c)) = sda_crl; // restore port configuration - GPIO_CRH(GPIO_PORT_SDA(i2c)) = sda_crh; // restore port configuration - GPIO_BSRR(GPIO_PORT_SDA(i2c)) = sda_bsrr; // restore port configuration - GPIO_CRL(GPIO_PORT_SCL(i2c)) = scl_crl; // restore port configuration - GPIO_CRH(GPIO_PORT_SCL(i2c)) = scl_crh; // restore port configuration - GPIO_BSRR(GPIO_PORT_SCL(i2c)) = scl_bsrr; // restore port configuration - - return to_return; -} - -void i2c_master_reset(uint32_t i2c) -{ - // check I2C peripheral - if (I2C1!=i2c && I2C2!=i2c) { - while (true); - } - - // follow procedure described in STM32F10xxC/D/E Errata sheet, Section 2.14.7 - i2c_peripheral_disable(i2c); // disable i2c peripheral - gpio_set_mode(GPIO_PORT_SCL(i2c), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN_SCL(i2c)); // put I2C I/O pins to general output - gpio_set(GPIO_PORT_SCL(i2c), GPIO_PIN_SCL(i2c)); // set high - while (!gpio_get(GPIO_PORT_SCL(i2c), GPIO_PIN_SCL(i2c))); // ensure it is high - gpio_set_mode(GPIO_PORT_SDA(i2c), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO_PIN_SDA(i2c)); // put I2C I/O pins to general output - gpio_set(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c)); // set high - while (!gpio_get(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c))); // ensure it is high - gpio_clear(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c)); // set low (try first transition) - while (gpio_get(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c))); // ensure it is low - gpio_clear(GPIO_PORT_SCL(i2c), GPIO_PIN_SCL(i2c)); // set low (try first transition) - while (gpio_get(GPIO_PORT_SCL(i2c), GPIO_PIN_SCL(i2c))); // ensure it is low - gpio_set(GPIO_PORT_SCL(i2c), GPIO_PIN_SCL(i2c)); // set high (try second transition) - while (!gpio_get(GPIO_PORT_SCL(i2c), GPIO_PIN_SCL(i2c))); // ensure it is high - gpio_set(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c)); // set high (try second transition) - while (!gpio_get(GPIO_PORT_SDA(i2c), GPIO_PIN_SDA(i2c))); // ensure it is high - gpio_set_mode(GPIO_PORT_SCL(i2c), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_PIN_SCL(i2c)); // set I2C I/O pins back - gpio_set_mode(GPIO_PORT_SDA(i2c), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, GPIO_PIN_SDA(i2c)); // set I2C I/O pins back - I2C_CR1(i2c) |= I2C_CR1_SWRST; // reset device - I2C_CR1(i2c) &= ~I2C_CR1_SWRST; // reset device - i2c_peripheral_enable(i2c); // re-enable device -} - -enum i2c_master_rc i2c_master_start(uint32_t i2c) -{ - // check I2C peripheral - if (I2C1!=i2c && I2C2!=i2c) { - while (true); - } - - // send (re-)start condition - if (I2C_CR1(i2c) & (I2C_CR1_START|I2C_CR1_STOP)) { // ensure start or stop operations are not in progress - return I2C_MASTER_RC_START_STOP_IN_PROGESS; - } - i2c_send_start(i2c); // send start condition to start transaction - while ((I2C_CR1(i2c) & I2C_CR1_START) && !(I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO))); // wait until start condition has been accepted and cleared - if (I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO)) { - return I2C_MASTER_RC_BUS_ERROR; - } - while (!(I2C_SR1(i2c) & I2C_SR1_SB) && !(I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO))); // wait until start condition is transmitted - if (I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO)) { - return I2C_MASTER_RC_BUS_ERROR; - } - if (!(I2C_SR2(i2c) & I2C_SR2_MSL)) { // verify if in master mode - return I2C_MASTER_RC_NOT_MASTER; - } - - return I2C_MASTER_RC_NONE; -} - -enum i2c_master_rc i2c_master_select_slave(uint32_t i2c, uint16_t slave, bool address_10bit, bool write) -{ - // check I2C peripheral - if (I2C1!=i2c && I2C2!=i2c) { - while (true); - } - - enum i2c_master_rc rc = I2C_MASTER_RC_NONE; // to store I2C return codes - if (!(I2C_SR1(i2c) & I2C_SR1_SB)) { // start condition has not been sent - rc = i2c_master_start(i2c); // send start condition - if (I2C_MASTER_RC_NONE!=rc) { - return rc; - } - } - if (!(I2C_SR2(i2c) & I2C_SR2_MSL)) { // I2C device is not in master mode - return I2C_MASTER_RC_NOT_MASTER; - } - - // select slave - if (!address_10bit) { // 7-bit address - I2C_SR1(i2c) &= ~(I2C_SR1_AF); // clear acknowledgement failure - i2c_send_7bit_address(i2c, slave, write ? I2C_WRITE : I2C_READ); // select slave, with read/write flag - while (!(I2C_SR1(i2c) & (I2C_SR1_ADDR|I2C_SR1_AF)) && !(I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO))); // wait until address is transmitted - if (I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO)) { - return I2C_MASTER_RC_BUS_ERROR; - } - if (I2C_SR1(i2c) & I2C_SR1_AF) { // address has not been acknowledged - return I2C_MASTER_RC_NAK; - } - } else { // 10-bit address - // send first part of address - I2C_SR1(i2c) &= ~(I2C_SR1_AF); // clear acknowledgement failure - I2C_DR(i2c) = 11110000 | (((slave>>8)&0x3)<<1); // send first header (11110xx0, where xx are 2 MSb of slave address) - while (!(I2C_SR1(i2c) & (I2C_SR1_ADD10|I2C_SR1_AF)) && !(I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO))); // wait until first part of address is transmitted - if (I2C_SR1(i2c) & I2C_SR1_AF) { // address has not been acknowledged - return I2C_MASTER_RC_NAK; - } - // send second part of address - I2C_SR1(i2c) &= ~(I2C_SR1_AF); // clear acknowledgement failure - I2C_DR(i2c) = (slave&0xff); // send remaining of address - while (!(I2C_SR1(i2c) & (I2C_SR1_ADDR|I2C_SR1_AF)) && !(I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO))); // wait until remaining part of address is transmitted - if (I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO)) { - return I2C_MASTER_RC_BUS_ERROR; - } - if (I2C_SR1(i2c) & I2C_SR1_AF) { // address has not been acknowledged - return I2C_MASTER_RC_NAK; - } - // go into receive mode if necessary - if (!write) { - rc = i2c_master_start(i2c); // send start condition - if (I2C_MASTER_RC_NONE!=rc) { - return rc; - } - // send first part of address with receive flag - I2C_SR1(i2c) &= ~(I2C_SR1_AF); // clear acknowledgement failure - I2C_DR(i2c) = 11110001 | (((slave>>8)&0x3)<<1); // send header (11110xx1, where xx are 2 MSb of slave address) - while (!(I2C_SR1(i2c) & (I2C_SR1_ADDR|I2C_SR1_AF)) && !(I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO))); // wait until remaining part of address is transmitted - if (I2C_SR1(i2c) & (I2C_SR1_BERR|I2C_SR1_ARLO)) { - return I2C_MASTER_RC_BUS_ERROR; - } - if (I2C_SR1(i2c) & I2C_SR1_AF) { // address has not been acknowledged - return I2C_MASTER_RC_NAK; - } - } - } - if (write) { - if (!((I2C_SR2(i2c) & I2C_SR2_TRA))) { // verify we are in transmit mode (and read SR2 to clear ADDR) - return I2C_MASTER_RC_NOT_TRANSMIT; - } - } else { - if ((I2C_SR2(i2c) & I2C_SR2_TRA)) { // verify we are in read mode (and read SR2 to clear ADDR) - return I2C_MASTER_RC_NOT_RECEIVE; - } - } - - return I2C_MASTER_RC_NONE; -} - -enum i2c_master_rc i2c_master_read(uint32_t i2c, uint8_t* data, size_t data_size) -{ - // check I2C peripheral - if (I2C1!=i2c && I2C2!=i2c) { - while (true); - } - - // sanity check - if (data==NULL || data_size==0) { // no data to read - return I2C_MASTER_RC_NONE; - } - if (!(I2C_SR2(i2c) & I2C_SR2_MSL)) { // I2C device is not master - return I2C_MASTER_RC_NOT_MASTER; - } - if ((I2C_SR2(i2c) & I2C_SR2_TRA)) { // I2C device not in receiver mode - return I2C_MASTER_RC_NOT_RECEIVE; - } - if (I2C_SR1(i2c) & I2C_SR1_AF) { // check if the previous transaction went well - return I2C_MASTER_RC_NOT_READY; - } - - // read data - for (size_t i=0; i0) { // only read data if needed - rc = i2c_master_read(i2c, data, data_size); - if (I2C_MASTER_RC_NONE!=rc) { - goto error; - } - } - - rc = I2C_MASTER_RC_NONE; // all went well -error: - i2c_master_stop(i2c); // sent stop condition - return rc; -} - -enum i2c_master_rc i2c_master_slave_write(uint32_t i2c, uint16_t slave, bool address_10bit, const uint8_t* data, size_t data_size) -{ - // check I2C peripheral - if (I2C1!=i2c && I2C2!=i2c) { - while (true); - } - - enum i2c_master_rc rc = I2C_MASTER_RC_NONE; // to store I2C return codes - rc = i2c_master_start(i2c); // send (re-)start condition - if (I2C_MASTER_RC_NONE!=rc) { - return rc; - } - rc = i2c_master_select_slave(i2c, slave, address_10bit, true); // select slave to write - if (I2C_MASTER_RC_NONE!=rc) { - goto error; - } - if (NULL!=data && data_size>0) { // write data only is some is available - rc = i2c_master_write(i2c, data, data_size); // write data - if (I2C_MASTER_RC_NONE!=rc) { - goto error; - } - } - - rc = I2C_MASTER_RC_NONE; // all went well -error: - i2c_master_stop(i2c); // sent stop condition - return rc; -} - -enum i2c_master_rc i2c_master_address_read(uint32_t i2c, uint16_t slave, bool address_10bit, const uint8_t* address, size_t address_size, uint8_t* data, size_t data_size) -{ - // check I2C peripheral - if (I2C1!=i2c && I2C2!=i2c) { - while (true); - } - - enum i2c_master_rc rc = I2C_MASTER_RC_NONE; // to store I2C return codes - rc = i2c_master_start(i2c); // send (re-)start condition - if (I2C_MASTER_RC_NONE!=rc) { - return rc; - } - rc = i2c_master_select_slave(i2c, slave, address_10bit, true); // select slave to write - if (I2C_MASTER_RC_NONE!=rc) { - goto error; - } - - // write address - if (NULL!=address && address_size>0) { - rc = i2c_master_write(i2c, address, address_size); // send memory address - if (I2C_MASTER_RC_NONE!=rc) { - goto error; - } - } - // read data - if (NULL!=data && data_size>0) { - rc = i2c_master_start(i2c); // send re-start condition - if (I2C_MASTER_RC_NONE!=rc) { - return rc; - } - rc = i2c_master_select_slave(i2c, slave, address_10bit, false); // select slave to read - if (I2C_MASTER_RC_NONE!=rc) { - goto error; - } - rc = i2c_master_read(i2c, data, data_size); // read memory - if (I2C_MASTER_RC_NONE!=rc) { - goto error; - } - } - - rc = I2C_MASTER_RC_NONE; // all went well -error: - i2c_master_stop(i2c); // sent stop condition - return rc; -} - -enum i2c_master_rc i2c_master_address_write(uint32_t i2c, uint16_t slave, bool address_10bit, const uint8_t* address, size_t address_size, const uint8_t* data, size_t data_size) -{ - // check I2C peripheral - if (I2C1!=i2c && I2C2!=i2c) { - while (true); - } - - enum i2c_master_rc rc = I2C_MASTER_RC_NONE; // to store I2C return codes - rc = i2c_master_start(i2c); // send (re-)start condition - if (I2C_MASTER_RC_NONE!=rc) { - return rc; - } - rc = i2c_master_select_slave(i2c, slave, address_10bit, true); // select slave to write - if (I2C_MASTER_RC_NONE!=rc) { - goto error; - } - - // write address - if (NULL!=address && address_size>0) { - rc = i2c_master_write(i2c, address, address_size); // send memory address - if (I2C_MASTER_RC_NONE!=rc) { - goto error; - } - } - // read data - if (NULL!=data && data_size>0) { - rc = i2c_master_write(i2c, data, data_size); // write memory - if (I2C_MASTER_RC_NONE!=rc) { - goto error; - } - } - - rc = I2C_MASTER_RC_NONE; // all went well -error: - i2c_master_stop(i2c); // sent stop condition - return rc; -} diff --git a/lib/i2c_master.h b/lib/i2c_master.h deleted file mode 100644 index f4e4933..0000000 --- a/lib/i2c_master.h +++ /dev/null @@ -1,132 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to communicate using I2C as master (API) - * @file i2c_master.h - * @author King Kévin - * @date 2017-2018 - * @note peripherals used: I2C - */ -#pragma once - -/** I2C return codes */ -enum i2c_master_rc { - I2C_MASTER_RC_NONE = 0, /**< no error */ - I2C_MASTER_RC_START_STOP_IN_PROGESS, /**< a start or stop condition is already in progress */ - I2C_MASTER_RC_NOT_MASTER, /**< not in master mode */ - I2C_MASTER_RC_NOT_TRANSMIT, /**< not in transmit mode */ - I2C_MASTER_RC_NOT_RECEIVE, /**< not in receive mode */ - I2C_MASTER_RC_NOT_READY, /**< slave is not read (previous operations has been nacked) */ - I2C_MASTER_RC_NAK, /**< not acknowledge received */ - I2C_MASTER_RC_BUS_ERROR, /**< an error on the I2C bus occurred */ -}; - -/** setup I2C peripheral - * @param[in] i2c I2C base address - * @param[in] frequency frequency to use in kHz (1-400) - * @note Standard mode (Sm) is used for frequencies up to 100 kHz, and Fast mode (Fm) is used for frequencies up to 400 kHz - */ -void i2c_master_setup(uint32_t i2c, uint16_t frequency); -/** release I2C peripheral - * @param[in] i2c I2C base address - */ -void i2c_master_release(uint32_t i2c); -/** reset I2C peripheral, fixing any locked state - * @warning the I2C peripheral needs to be re-setup - * @note to be used after failed start or stop, and bus error - * @param[in] i2c I2C base address - */ -void i2c_master_reset(uint32_t i2c); -/** check if SDA and SCL signals are high - * @param[in] i2c I2C base address - * @return SDA and SCL signals are high - */ -bool i2c_master_check_signals(uint32_t i2c); -/** send start condition - * @param[in] i2c I2C base address - * @return I2C return code - */ -enum i2c_master_rc i2c_master_start(uint32_t i2c); -/** select I2C slave device - * @warning a start condition should be sent before this operation - * @param[in] i2c I2C base address - * @param[in] slave I2C address of slave device to select - * @param[in] address_10bit if the I2C slave address is 10 bits wide - * @param[in] write this transaction will be followed by a read (false) or write (true) operation - * @return I2C return code - */ -enum i2c_master_rc i2c_master_select_slave(uint32_t i2c, uint16_t slave, bool address_10bit, bool write); -/** read data over I2C - * @warning the slave device must be selected before this operation - * @param[in] i2c I2C base address - * @param[out] data array to store bytes read - * @param[in] data_size number of bytes to read - * @return I2C return code - */ -enum i2c_master_rc i2c_master_read(uint32_t i2c, uint8_t* data, size_t data_size); -/** write data over I2C - * @warning the slave device must be selected before this operation - * @param[in] i2c I2C base address - * @param[in] data array of byte to write to slave - * @param[in] data_size number of bytes to write - * @return I2C return code - */ -enum i2c_master_rc i2c_master_write(uint32_t i2c, const uint8_t* data, size_t data_size); -/** sent stop condition - * @param[in] i2c I2C base address - * @return I2C return code - */ -enum i2c_master_rc i2c_master_stop(uint32_t i2c); -/** read data from slave device - * @warning the slave device must be selected before this operation - * @param[in] i2c I2C base address - * @param[in] slave I2C address of slave device to select - * @param[in] address_10bit if the I2C slave address is 10 bits wide - * @param[out] data array to store bytes read - * @param[in] data_size number of bytes to read - * @return I2C return code - */ -enum i2c_master_rc i2c_master_slave_read(uint32_t i2c, uint16_t slave, bool address_10bit, uint8_t* data, size_t data_size); -/** write data to slave device - * @warning the slave device must be selected before this operation - * @param[in] i2c I2C base address - * @param[in] slave I2C address of slave device to select - * @param[in] address_10bit if the I2C slave address is 10 bits wide - * @param[in] data array of byte to write to slave - * @param[in] data_size number of bytes to write - * @return I2C return code - */ -enum i2c_master_rc i2c_master_slave_write(uint32_t i2c, uint16_t slave, bool address_10bit, const uint8_t* data, size_t data_size); -/** read data at specific address from an I2C memory slave - * @param[in] i2c I2C base address - * @param[in] slave I2C address of slave device to select - * @param[in] address_10bit if the I2C slave address is 10 bits wide - * @param[in] address memory address of slave to read from - * @param[in] address_size address size in bytes - * @param[out] data array to store bytes read - * @param[in] data_size number of bytes to read - * @return I2C return code - */ -enum i2c_master_rc i2c_master_address_read(uint32_t i2c, uint16_t slave, bool address_10bit, const uint8_t* address, size_t address_size, uint8_t* data, size_t data_size); -/** write data at specific address on an I2C memory slave - * @param[in] i2c I2C base address - * @param[in] slave I2C address of slave device to select - * @param[in] address_10bit if the I2C slave address is 10 bits wide - * @param[in] address memory address of slave to write to - * @param[in] address_size address size in bytes - * @param[in] data array of byte to write to slave - * @param[in] data_size number of bytes to write - * @return I2C return code - */ -enum i2c_master_rc i2c_master_address_write(uint32_t i2c, uint16_t slave, bool address_10bit, const uint8_t* address, size_t address_size, const uint8_t* data, size_t data_size); diff --git a/lib/led_max7219.c b/lib/led_max7219.c deleted file mode 100644 index 44f378f..0000000 --- a/lib/led_max7219.c +++ /dev/null @@ -1,267 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to communicate with a Titan Micro MAX7219 IC attached to a 4-digit 7-segment (code) - * @file led_max7219.c - * @author King Kévin - * @date 2017 - * @note peripherals used: GPIO @ref led_max7219_gpio, timer @ref led_tm1637_timer - * @warning all calls are blocking - * - * bit vs segment: 0bpabcdefg - * +a+ - * f b - * +g+ - * e c p - * +d+ - */ - -/* standard libraries */ -#include // standard integer types -#include // general utilities -#include // string utilities - -/* STM32 (including CM3) libraries */ -#include // Cortex M3 utilities -#include // real-time control clock library -#include // general purpose input output library -#include // SPI library -#include // timer library - -#include "global.h" // global utilities -#include "led_max7219.h" // MAX7219 header and definitions - -/** @defgroup led_max7219_gpio GPIO used to control MAX7219 IC load line - * @{ - */ -#define LED_MAX7219_LOAD_PORT B /**< port for load line */ -#define LED_MAX7219_LOAD_PIN 12 /**< pin for load line */ -/** @} */ - -/** @defgroup led_max7219_spi SPI used to communication with MAX7219 IC - * @{ - */ -#define LED_MAX7219_SPI 2 /**< SPI to send data */ -/** @} */ - -/** ASCII characters encoded for the 7 segments digit block - * @note starts with space - */ -static const uint8_t ascii_7segments[] = { - 0x00, // space - 0x06, // ! (I) - 0x22, // " - 0x1d, // # (o) - 0x5b, // $ (s) - 0x25, // % (/) - 0x5f, // & (6) - 0x02, // ' - 0x4e, // ( ([) - 0x78, // ) - 0x07, // * - 0x31, // + - 0x04, // , - 0x01, // - - 0x04, // . (,) - 0x25, // / - 0x7e, // 0 - 0x30, // 1 - 0x6d, // 2 - 0x79, // 3 - 0x33, // 4 - 0x5b, // 5 - 0x5f, // 6 - 0x70, // 7 - 0x7f, // 8 - 0x7b, // 9 - 0x09, // : (=) - 0x09, // ; (=) - 0x0d, // < - 0x09, // = - 0x19, // > - 0x65, // ? - 0x6f, // @ - 0x77, // A - 0x7f, // B - 0x4e, // C - 0x3d, // D - 0x4f, // E - 0x47, // F - 0x5e, // G - 0x37, // H - 0x06, // I - 0x3c, // J - 0x37, // K - 0x0e, // L - 0x76, // M - 0x76, // N - 0x7e, // O - 0x67, // P - 0x6b, // Q - 0x66, // R - 0x5b, // S - 0x0f, // T - 0x3e, // U - 0x3e, // V (U) - 0x3e, // W (U) - 0x37, // X (H) - 0x3b, // Y - 0x6d, // Z - 0x4e, // [ - 0x13, // '\' - 0x78, // / - 0x62, // ^ - 0x08, // _ - 0x20, // ` - 0x7d, // a - 0x1f, // b - 0x0d, // c - 0x3d, // d - 0x6f, // e - 0x47, // f - 0x7b, // g - 0x17, // h - 0x04, // i - 0x18, // j - 0x37, // k - 0x06, // l - 0x15, // m - 0x15, // n - 0x1d, // o - 0x67, // p - 0x73, // q - 0x05, // r - 0x5b, // s - 0x0f, // t - 0x1c, // u - 0x1c, // v (u) - 0x1c, // w (u) - 0x37, // x - 0x3b, // y - 0x6d, // z - 0x4e, // { ([) - 0x06, // | - 0x78, // } ([) - 0x01, // ~ -}; - -/** number of display in the chain */ -uint8_t lex_max7219_displays = 0; - -/** write data on SPI bus and handle load signal - * @param[in] data bytes to write - * @param[in] display display number in chain (0xff for all) - */ -static void led_max7219_write(uint16_t data, uint8_t display) -{ - if (lex_max7219_displays<=display && 0xff!=display) { // display no in chain - return; - } - - gpio_clear(GPIO(LED_MAX7219_LOAD_PORT), GPIO(LED_MAX7219_LOAD_PIN)); // ensure load pin is low (data is put in MAX7219 register on rising edge) - for (uint8_t i=lex_max7219_displays; i>0; i--) { // go though all displays - while (SPI_SR(SPI(LED_MAX7219_SPI))&SPI_SR_BSY); // wait until not busy - if (0xff==display || i==(display+1)) { // right display or broadcast message - spi_send(SPI(LED_MAX7219_SPI), data); // send data - } else { - spi_send(SPI(LED_MAX7219_SPI), 0x0000); // send no-op command to shift command to correct display or out - } - while (!(SPI_SR(SPI(LED_MAX7219_SPI))&SPI_SR_TXE)); // wait until Tx is empty (reference manual says BSY should also cover this, but it doesn't) - while (SPI_SR(SPI(LED_MAX7219_SPI))&SPI_SR_BSY); // wait until not busy (= transmission completed) - } - gpio_set(GPIO(LED_MAX7219_LOAD_PORT), GPIO(LED_MAX7219_LOAD_PIN)); // create rising edge on load pin for data to be set in MAX7219 register -} - -void led_max7219_setup(uint8_t displays) -{ - // saved number of displays - lex_max7219_displays = displays; - - // configure GPIO for load line - rcc_periph_clock_enable(RCC_GPIO(LED_MAX7219_LOAD_PORT)); // enable clock for GPIO peripheral - gpio_clear(GPIO(LED_MAX7219_LOAD_PORT), GPIO(LED_MAX7219_LOAD_PIN)); // idle low (load on rising edge) - gpio_set_mode(GPIO(LED_MAX7219_LOAD_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_MAX7219_LOAD_PIN)); // set as output - - // configure SPI peripheral - rcc_periph_clock_enable(RCC_SPI_SCK_PORT(LED_MAX7219_SPI)); // enable clock for GPIO peripheral for clock signal - gpio_set_mode(SPI_SCK_PORT(LED_MAX7219_SPI), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_SCK_PIN(LED_MAX7219_SPI)); // set as output (max clock speed for MAX7219 is 10 MHz) - rcc_periph_clock_enable(RCC_SPI_MOSI_PORT(LED_MAX7219_SPI)); // enable clock for GPIO peripheral for MOSI signal - gpio_set_mode(SPI_MOSI_PORT(LED_MAX7219_SPI), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, SPI_MOSI_PIN(LED_MAX7219_SPI)); // set as output - rcc_periph_clock_enable(RCC_AFIO); // enable clock for SPI alternate function - rcc_periph_clock_enable(RCC_SPI(LED_MAX7219_SPI)); // enable clock for SPI peripheral - spi_reset(SPI(LED_MAX7219_SPI)); // clear SPI values to default - spi_init_master(SPI(LED_MAX7219_SPI), SPI_CR1_BAUDRATE_FPCLK_DIV_8, SPI_CR1_CPOL_CLK_TO_0_WHEN_IDLE, SPI_CR1_CPHA_CLK_TRANSITION_1, SPI_CR1_DFF_16BIT, SPI_CR1_MSBFIRST); // initialise SPI as master, divide clock by 8 since max MAX7219 clock is 10 MHz and max SPI PCLK clock is 72 Mhz, depending on which SPI is used, set clock polarity to idle low (as in the datasheet of the MAX7219, but not that important), set clock phase to go high when bit is set (depends on polarity) as data is stored on MAX7219 on rising edge), use 16 bits frames (as used by MAX7219), use MSB first - spi_set_unidirectional_mode(SPI(LED_MAX7219_SPI)); // we only need to transmit data - spi_enable(SPI(LED_MAX7219_SPI)); // enable SPI -} - -void led_max7219_on(uint8_t display) -{ - led_max7219_write(0x0C01, display); // put in normal operation more (registers remain as set) -} - -void led_max7219_off(uint8_t display) -{ - led_max7219_write(0x0C00, display); // put in shutdown mode (registers remain as set) -} - -void led_max7219_test(bool test, uint8_t display) -{ - if (test) { - led_max7219_write(0x0F01, display); // go into display test mode - } else { - led_max7219_write(0x0F00, display); // go into normal operation mode - } -} - -void led_max7219_intensity(uint8_t intensity, uint8_t digits, uint8_t display) -{ - if (intensity>15) { // intensity must be 0-15 (corresponds to (2*brightness+1)/32) - return; - } - if (digits<1 || digits>8) { // scan limit must bit 0-7 - return; - } - led_max7219_write(0x0A00+intensity, display); // set brightness - led_max7219_write(0x0B00+digits-1, display); // set scan limit to display digits -} - -bool led_max7219_text(char* text, uint8_t display) -{ - for (uint8_t i=0; i<8; i++) { // input text should only contain printable character (8th bit is used for dots) - if ((text[i]&0x7f)<' ' || (text[i]&0x7f)>=' '+LENGTH(ascii_7segments)) { - return false; - } - } - led_max7219_write(0x0900, display); // disable BCD decoding on all 7 digits - for (uint8_t i=0; i<8; i++) { // display text - led_max7219_write(((i+1)<<8)+(ascii_7segments[(text[7-i]&0x7f)-' '])+(text[7-i]&0x80), display); // send digit (in reverse order) - } - return true; -} - -void led_max7219_number(uint32_t number, uint8_t dots, uint8_t display) -{ - led_max7219_write(0x09FF, display); // enable BCD decoding on all 7 digits - for (uint8_t digit=0; digit<8; digit++) { // go through digits - if (0==digit) { // display 0 on 0 only to first digit - led_max7219_write(((digit+1)<<8) + (number%10) + (((dots>>digit)&0x01)<<7), display); // display digit - } else if (0==number) { // display blank on other digits - led_max7219_write(((digit+1)<<8) + 0x0F + (((dots>>digit)&0x01)<<7), display); // display blank - } else { - led_max7219_write(((digit+1)<<8) + (number%10) + (((dots>>digit)&0x01)<<7), display); // display digit - } - number /= 10; // get next digit - } -} diff --git a/lib/led_max7219.h b/lib/led_max7219.h deleted file mode 100644 index 8a1207e..0000000 --- a/lib/led_max7219.h +++ /dev/null @@ -1,64 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to communicate with a Maxim MAX7219 IC attached to a 8-digit 7-segment (API) - * @file led_max7219.h - * @author King Kévin - * @date 2017 - * @note peripherals used: GPIO @ref led_max7219_gpio, SPI @ref led_max7219_spi - * @warning all calls are blocking - */ -#pragma once - -/** setup communication with MAX7219 IC - * @param[in] displays number of displays in the chain - */ -void led_max7219_setup(uint8_t displays); -/** do nothing (no operation) - * @param[in] display display number in chain (0xff for all) - * @note send it to the last display in the chain to clear the previous command from the chain - */ -void led_max7219_nop(uint8_t display); -/** switch display on - * @param[in] display display number in chain (0xff for all) - */ -void led_max7219_on(uint8_t display); -/** switch display off - * @param[in] display display number in chain (0xff for all) - */ -void led_max7219_off(uint8_t display); -/** switch display in test or normal operation mode - * @param[in] test switch in test mode (else normal operation) - * @param[in] display display number in chain (0xff for all) - */ -void led_max7219_test(bool test, uint8_t display); -/** set display intensity - * @param[in] intensity level to set (0-15) - * @param[in] digits number of digits to display (1-8) - * @param[in] display display number in chain (0xff for all) - */ -void led_max7219_intensity(uint8_t intensity, uint8_t digits, uint8_t display); -/** display text - * @param[in] text text to display (8 characters) - * @param[in] display display number in chain (0xff for all) - * @note use first bit of each character to enable dot - * @return false if string has unsupported characters - */ -bool led_max7219_text(char* text, uint8_t display); -/** display number - * @param[in] number number to display (8 digits max) - * @param[in] dots set bit if dot on corresponding digit should be displayed - * @param[in] display display number in chain (0xff for all) - */ -void led_max7219_number(uint32_t number, uint8_t dots, uint8_t display); diff --git a/lib/led_tm1637.c b/lib/led_tm1637.c deleted file mode 100644 index 2d11b11..0000000 --- a/lib/led_tm1637.c +++ /dev/null @@ -1,327 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to communicate with a Titan Micro TM1637 IC attached to a 4-digit 7-segment (code) - * @file led_tm1637.c - * @author King Kévin - * @date 2017 - * @note peripherals used: GPIO @ref led_tm1637_gpio, timer @ref led_tm1637_timer - * @note the protocol is very similar to I2C but incompatible for the following reasons: the capacitance is too large for open-drain type output with weak pull-up resistors (push-pull needs to be used, preventing to get ACKs since no indication of the ACK timing is provided); the devices doesn't use addresses; the STM32 I2C will switch to receiver mode when the first sent byte (the I2C address) has last bit set to 1 (such as for address commands with B7=1 where B7 is transmitted last), preventing to send further bytes (the data byte after the address) - * @warning all calls are blocking - * - * bit vs segment: 0bpgfedcba - * +a+ - * f b p - * +g+ - * e c p - * +d+ - */ - -/* standard libraries */ -#include // standard integer types -#include // general utilities -#include // string utilities - -/* STM32 (including CM3) libraries */ -#include // Cortex M3 utilities -#include // real-time control clock library -#include // general purpose input output library -#include // timer library - -#include "global.h" // global utilities -#include "led_tm1637.h" // TM1637 header and definitions - -/** @defgroup led_tm1637_gpio GPIO used to communication with TM1637 IC - * @{ - */ -#define LED_TM1637_CLK_PORT B /**< port for CLK signal */ -#define LED_TM1637_CLK_PIN 6 /**< pin for CLK signal */ -#define LED_TM1637_DIO_PORT B /**< port for DIO signal */ -#define LED_TM1637_DIO_PIN 7 /**< pin for DIO signal */ -/** @} */ - -/** @defgroup led_tm1637_timer timer used to communication with TM1637 IC - * @{ - */ -#define LED_TM1637_TIMER 3 /**< timer to create signal */ -/** @} */ - -/** display brightness */ -static enum led_tm1637_brightness_t display_brightness = LED_TM1637_14DIV16; -/** if display is on */ -static bool display_on = false; - -/** ASCII characters encoded for the 7 segments digit block - * @note starts with space - */ -static const uint8_t ascii_7segments[] = { - 0x00, // 0b00000000 space - 0x30, // 0b00110000 ! (I) - 0x22, // 0b00100010 " - 0x5c, // 0b01011100 # (o) - 0x6d, // 0b01101101 $ (s) - 0x52, // 0b01010010 % (/) - 0x7d, // 0b01111101 & (6) - 0x20, // 0b00100000 ' - 0x39, // 0b00111001 ( ([) - 0x0f, // 0b00001111 ) - 0x70, // 0b01110000 * - 0x46, // 0b01000110 + - 0x10, // 0b00010000 , - 0x40, // 0b01000000 - - 0x10, // 0b00010000 . (,) - 0x52, // 0b01010010 / - 0x3f, // 0b00111111 0 - 0x06, // 0b00000110 1 - 0x5b, // 0b01011011 2 - 0x4f, // 0b01001111 3 - 0x66, // 0b01100110 4 - 0x6d, // 0b01101101 5 - 0x7d, // 0b01111101 6 - 0x07, // 0b00000111 7 - 0x7f, // 0b01111111 8 - 0x6f, // 0b01101111 9 - 0x48, // 0b01001000 : (=) - 0x48, // 0b01001000 ; (=) - 0x58, // 0b01011000 < - 0x48, // 0b01001000 = - 0x4c, // 0b01001100 > - 0x53, // 0b01010011 ? - 0x7b, // 0b01111011 @ - 0x77, // 0b01110111 A - 0x7f, // 0b01111111 B - 0x39, // 0b00111001 C - 0x5e, // 0b01011110 D - 0x79, // 0b01111001 E - 0x71, // 0b01110001 F - 0x3d, // 0b00111101 G - 0x76, // 0b01110110 H - 0x30, // 0b00110000 I - 0x1e, // 0b00011110 J - 0x76, // 0b01110110 K - 0x38, // 0b00111000 L - 0x37, // 0b00110111 M - 0x37, // 0b00110111 N - 0x3f, // 0b00111111 O - 0x73, // 0b01110011 P - 0x6b, // 0b01101011 Q - 0x33, // 0b00110011 R - 0x6d, // 0b01101101 S - 0x78, // 0b01111000 T - 0x3e, // 0b00111110 U - 0x3e, // 0b00111110 V (U) - 0x3e, // 0b00111110 W (U) - 0x76, // 0b01110110 X (H) - 0x6e, // 0b01101110 Y - 0x5b, // 0b01011011 Z - 0x39, // 0b00111001 [ - 0x64, // 0b01100100 '\' - 0x0f, // 0b00001111 / - 0x23, // 0b00100011 ^ - 0x08, // 0b00001000 _ - 0x02, // 0b00000010 ` - 0x5f, // 0b01011111 a - 0x7c, // 0b01111100 b - 0x58, // 0b01011000 c - 0x5e, // 0b01011110 d - 0x7b, // 0b01111011 e - 0x71, // 0b01110001 f - 0x6f, // 0b01101111 g - 0x74, // 0b01110100 h - 0x10, // 0b00010000 i - 0x0c, // 0b00001100 j - 0x76, // 0b01110110 k - 0x30, // 0b00110000 l - 0x54, // 0b01010100 m - 0x54, // 0b01010100 n - 0x5c, // 0b01011100 o - 0x73, // 0b01110011 p - 0x67, // 0b01100111 q - 0x50, // 0b01010000 r - 0x6d, // 0b01101101 s - 0x78, // 0b01111000 t - 0x1c, // 0b00011100 u - 0x1c, // 0b00011100 v (u) - 0x1c, // 0b00011100 w (u) - 0x76, // 0b01110110 x - 0x6e, // 0b01101110 y - 0x5b, // 0b01011011 z - 0x39, // 0b00111001 { ([) - 0x30, // 0b00110000 | - 0x0f, // 0b00001111 } ([) - 0x40, // 0b01000000 ~ -}; - -void led_tm1637_setup(void) -{ - // configure GPIO for CLK and DIO signals - rcc_periph_clock_enable(RCC_GPIO(LED_TM1637_CLK_PORT)); // enable clock for GPIO peripheral - gpio_set(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // idle high - gpio_set_mode(GPIO(LED_TM1637_CLK_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_TM1637_CLK_PIN)); // master start the communication (capacitance is to large for open drain), only switch to input for ack from slave - rcc_periph_clock_enable(RCC_GPIO(LED_TM1637_DIO_PORT)); // enable clock for GPIO peripheral - gpio_set(GPIO(LED_TM1637_DIO_PORT), GPIO(LED_TM1637_DIO_PIN)); // idle high - gpio_set_mode(GPIO(LED_TM1637_DIO_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_TM1637_DIO_PIN)); // master start the communication (capacitance is to large for open drain), only switch to input for ack from slave - // first clock then data high also stands for stop condition - - // setup timer to create signal timing (each tick is used for a single GPIO transition) - rcc_periph_clock_enable(RCC_TIM(LED_TM1637_TIMER)); // enable clock for timer block - timer_reset(TIM(LED_TM1637_TIMER)); // reset timer state - timer_set_mode(TIM(LED_TM1637_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up - timer_set_prescaler(TIM(LED_TM1637_TIMER), 0); // don't prescale to get most precise timing ( 1/(72E6/1/(2**16))=0.91 ms > 0.5 us ) - timer_set_period(TIM(LED_TM1637_TIMER), 500); // set the clock frequency (emprical value until the signal starts to look bad) - timer_clear_flag(TIM(LED_TM1637_TIMER), TIM_SR_UIF); // clear flag - timer_update_on_overflow(TIM(LED_TM1637_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout) -} - -/** wait until clock tick (timer overflow) occurred - */ -static inline void led_tm1637_tick(void) -{ - while (!timer_get_flag(TIM(LED_TM1637_TIMER), TIM_SR_UIF)); // wait until counter overflow update event happens - timer_clear_flag(TIM(LED_TM1637_TIMER), TIM_SR_UIF); // clear event flag -} - -/** write data on bus - * @param[in] data bytes to write - * @param[in] length number of bytes to write - * @return if write succeeded - * @note includes start and stop conditions - */ -static bool led_tm1637_write(const uint8_t* data, size_t length) -{ - bool to_return = true; // return if write succeeded - if (data==NULL || length==0) { // verify there it data to be read - return false; - } - - // enable timer for signal generation - timer_set_counter(TIM(LED_TM1637_TIMER), 0); // reset timer counter - timer_enable_counter(TIM(LED_TM1637_TIMER)); // enable timer to generate timing - led_tm1637_tick(); // wait to enforce minimum time since last write - - // send start condition (DIO then CLK low) - gpio_clear(GPIO(LED_TM1637_DIO_PORT), GPIO(LED_TM1637_DIO_PIN)); // put DIO low - led_tm1637_tick(); // wait for next tick - gpio_clear(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // put CLK low - - // send data bytes (MSb first) - for (size_t i=0; i>= 1; // shift data - led_tm1637_tick(); // wait for next tick - gpio_set(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // put CLK high - led_tm1637_tick(); // wait for next tick (no DIO transition when CLK is high) - gpio_clear(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // put CLK low - } - gpio_set_mode(GPIO(LED_TM1637_DIO_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(LED_TM1637_DIO_PIN)); // switch DIO as input to read ACK - led_tm1637_tick(); // wait for next tick (when the slave should ACK) - gpio_set(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // put CLK high - if (gpio_get(GPIO(LED_TM1637_DIO_PORT), GPIO(LED_TM1637_DIO_PIN))) { // no ACK received - to_return = false; // remember there was an error - break; // stop sending bytes - } - led_tm1637_tick(); // wait for next tick - gpio_clear(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // put CLK low - gpio_set_mode(GPIO(LED_TM1637_DIO_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_TM1637_DIO_PIN)); // switch DIO back to output to send next byte - } - - // send stop condition - gpio_set_mode(GPIO(LED_TM1637_DIO_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(LED_TM1637_DIO_PIN)); // ensure DIO is output (in case no ACK as been received - led_tm1637_tick(); // wait for next tick - gpio_set(GPIO(LED_TM1637_CLK_PORT), GPIO(LED_TM1637_CLK_PIN)); // put CLK high - led_tm1637_tick(); // wait for next tick - gpio_set(GPIO(LED_TM1637_DIO_PORT), GPIO(LED_TM1637_DIO_PIN)); // put DIO high - timer_disable_counter(TIM(LED_TM1637_TIMER)); // stop timer since it's not used anymore - - return to_return; -} - -bool led_tm1637_on(void) -{ - uint8_t data[] = { 0x88+display_brightness }; // command to turn display on (use set brightness) - bool to_return = false; // result to return - if (led_tm1637_write(data,LENGTH(data))) { // send command - display_on = true; // remember display is on - to_return = true; // command succeeded - } - return to_return; // return result -} - -bool led_tm1637_off(void) -{ - uint8_t data[] = { 0x80+display_brightness }; // command to turn display off (use set brightness) - if (led_tm1637_write(data,LENGTH(data))) { // send command - display_on = false; // remember display is off - return true; // command succeeded - } - return false; // return result -} - -bool led_tm1637_brightness(enum led_tm1637_brightness_t brightness) -{ - display_brightness = brightness; // save brightness - if (display_on) { // adjust brightness if display is on - return led_tm1637_on(); // adjust brightness - } else { - return true; // command succeeded - } - return false; -} - -bool led_tm1637_number(uint16_t number) -{ - uint8_t write_data[] = { 0x40 }; // command: write data, automatic address adding, normal - uint8_t data[] = { 0xc0, ascii_7segments[((number/1000)%10)+'0'-' '], ascii_7segments[((number/100)%10)+'0'-' '], ascii_7segments[((number/10)%10)+'0'-' '], ascii_7segments[((number/1)%10)+'0'-' '] }; // set address C0H and add data - - if (led_tm1637_write(write_data,LENGTH(write_data)) && led_tm1637_write(data,LENGTH(data))) { // send commands - return true; - } - return false; -} - -bool led_tm1637_time(uint8_t hours, uint8_t minutes) -{ - uint8_t write_data[] = { 0x40 }; // command: write data, automatic address adding, normal - uint8_t data[] = { 0xc0, ascii_7segments[((hours/10)%10)+'0'-' '], ascii_7segments[((hours/1)%10)+'0'-' ']|0x80, ascii_7segments[((minutes/10)%10)+'0'-' '], ascii_7segments[((minutes/1)%10)+'0'-' '] }; // set address C0H and add data - - if (led_tm1637_write(write_data,LENGTH(write_data)) && led_tm1637_write(data,LENGTH(data))) { // send commands - return true; - } - return false; -} - -bool led_tm1637_text(char* text) -{ - if (strlen(text)!=4) { // input text should have exactly 4 characters - return false; - } - for (uint8_t i=0; i<4; i++) { // input text should only contain printable character (8th bit is used for dots) - if ((text[i]&0x7f)<' ' || (text[i]&0x7f)>=' '+LENGTH(ascii_7segments)) { - return false; - } - } - uint8_t write_data[] = { 0x40 }; // command: write data, automatic address adding, normal - uint8_t data[] = { 0xc0, ascii_7segments[(text[0]&0x7f)-' ']|(text[0]&0x80), ascii_7segments[(text[1]&0x7f)-' ']|(text[1]&0x80), ascii_7segments[(text[2]&0x7f)-' ']|(text[2]&0x80), ascii_7segments[(text[3]&0x7f)-' ']|(text[3]&0x80) }; // set address C0H and add data - - if (led_tm1637_write(write_data,LENGTH(write_data)) && led_tm1637_write(data,LENGTH(data))) { // send commands - return true; - } - return false; -} diff --git a/lib/led_tm1637.h b/lib/led_tm1637.h deleted file mode 100644 index 7bd0109..0000000 --- a/lib/led_tm1637.h +++ /dev/null @@ -1,71 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to communicate with a Titan Micro TM1637 IC attached to a 4-digit 7-segment (API) - * @file led_tm1637.h - * @author King Kévin - * @date 2017 - * @note peripherals used: GPIO @ref led_tm1637_gpio, timer @ref led_tm1637_timer - * @warning all calls are blocking - */ -#pragma once - -/** display brightness levels - */ -enum led_tm1637_brightness_t { - LED_TM1637_1DIV16 = 0, - LED_TM1637_2DIV16 = 1, - LED_TM1637_4DIV16 = 2, - LED_TM1637_10DIV16 = 3, - LED_TM1637_11DIV16 = 4, - LED_TM1637_12DIV16 = 5, - LED_TM1637_13DIV16 = 6, - LED_TM1637_14DIV16 = 7, -}; - -/** setup communication with TM1637 IC - */ -void led_tm1637_setup(void); -/** switch display on - * @return if transmission succeeded - */ -bool led_tm1637_on(void); -/** switch display off - * @return if transmission succeeded - */ -bool led_tm1637_off(void); -/** set display brightness - * @param[in] brightness brightness level to set - * @return if transmission succeeded - */ -bool led_tm1637_brightness(enum led_tm1637_brightness_t brightness); -/** display number - * @param[in] number number to display (0-9999) - * @return if transmission succeeded - */ -bool led_tm1637_number(uint16_t number); -/** display time - * @param[in] hours hours to display (0-99) - * @param[in] minutes minutes to display (0-99) - * @note display separator between hours and minutes - * @return if transmission succeeded - */ -bool led_tm1637_time(uint8_t hours, uint8_t minutes); -/** display text - * @param[in] text text to display (4 characters) - * @note use first bit of each character to enable dot - * @return if transmission succeeded - */ -bool led_tm1637_text(char* text); - diff --git a/lib/led_ws2812b.c b/lib/led_ws2812b.c deleted file mode 100644 index 6df0e9b..0000000 --- a/lib/led_ws2812b.c +++ /dev/null @@ -1,181 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to drive a WS2812B LED chain (code) - * @file led_ws2812b.c - * @author King Kévin - * @date 2016-2017 - * @note peripherals used: SPI @ref led_ws2812b_spi, timer @ref led_ws2812b_timer, DMA (for SPI MISO) - */ - -/* standard libraries */ -#include // standard integer types -#include // general utilities - -/* STM32 (including CM3) libraries */ -#include // Cortex M3 utilities -#include // real-time control clock library -#include // general purpose input output library -#include // SPI library -#include // timer library -#include // DMA library -#include // interrupt handler - -#include "led_ws2812b.h" // LED WS2812B library API -#include "global.h" // common methods - -/** peripheral configuration */ -/** @defgroup led_ws2812b_spi SPI peripheral used to control the WS2812B LEDs - * @{ - */ -#define LED_WS2812B_SPI 1 /**< SPI peripheral */ -/** @} */ - -/** @defgroup led_ws2812b_timer timer peripheral used to generate SPI clock - * @{ - */ -#define LED_WS2812B_TIMER 3 /**< timer peripheral */ -#define LED_WS2812B_CLK_CH 3 /**< timer channel to output PWM (PB0), connect to SPI clock input */ -#define LED_WS2812B_TIMER_OC TIM_OC3 /**< timer output compare used to set PWM frequency */ -/** @} */ - -/** bit template to encode one byte to be shifted out by SPI to the WS2812B LEDs - * @details For each WS2812B bit which needs to be transfered we require to transfer 3 SPI bits. - * The first SPI bit is the high start of the WS2812B bit frame. - * The second SPI bit determines if the WS2812B bit is a 0 or 1. - * The third SPI bit is the last part of the WS2812B bit frame, which is always low. - * The binary pattern is 0b100100100100100100100100 - */ -#define LED_WS2812B_SPI_TEMPLATE 0x924924 - -uint8_t led_ws2812b_data[LED_WS2812B_LEDS*3*3+40*3/8+1] = {0}; /**< data encoded to be shifted out by SPI for the WS2812B, plus the 50us reset (~40 data bits) */ -static volatile bool transmit_flag = false; /**< flag set in software when transmission started, clear by interrupt when transmission completed */ - -void led_ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue) -{ - // verify the led exists - if (led>=LED_WS2812B_LEDS) { - return; - } - // wait for transmission to complete before changing the color - while (transmit_flag) { - __WFI(); - } - - const uint8_t colors[] = {green, red, blue}; // color order for the WS2812B - const uint8_t pattern_bit[] = {0x02, 0x10, 0x80, 0x04, 0x20, 0x01, 0x08, 0x40}; // which bit to change in the pattern - const uint8_t pattern_byte[] = {2,2,2,1,1,0,0,0}; // in which byte in the pattern to write the pattern bit - for (uint8_t color=0; color>16); - led_ws2812b_data[i*3+1] = (uint8_t)(LED_WS2812B_SPI_TEMPLATE>>8); - led_ws2812b_data[i*3+2] = (uint8_t)(LED_WS2812B_SPI_TEMPLATE>>0); - } - // fill remaining with with 0 to encode the reset code - for (uint16_t i=LED_WS2812B_LEDS*3*3; i. - * - */ -/** library to drive a WS2812B LED chain (API) - * @file led_ws2812b.h - * @author King Kévin - * @date 2016-2017 - * @note peripherals used: SPI @ref led_ws2812b_spi, timer @ref led_ws2812b_timer, DMA (for SPI MISO) - */ -#pragma once - -/** number of LEDs on the WS2812B strip */ -#define LED_WS2812B_LEDS 48 - -/** setup WS2812B LED driver */ -void led_ws2812b_setup(void); -/** set color of a single LED - * @param[in] led the LED number to set the color - * @param[in] red the red color value to set on the LED - * @param[in] green the green color value to set on the LED - * @param[in] blue the blue color value to set on the LED - * @note transmission needs to be done separately - */ -void led_ws2812b_set_rgb(uint16_t led, uint8_t red, uint8_t green, uint8_t blue); -/** transmit color values to WS2812B LEDs - * @return true if transmission started, false if another transmission is already ongoing - */ -bool led_ws2812b_transmit(void); diff --git a/lib/microwire_master.c b/lib/microwire_master.c deleted file mode 100644 index bff991d..0000000 --- a/lib/microwire_master.c +++ /dev/null @@ -1,315 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to communicate using microwore as master (code) - * @file microwire_master.c - * @author King Kévin - * @date 2017 - * @note peripherals used: GPIO @ref microwire_master_gpio, timer @ref microwire_master_timer - * microwire is a 3-Wire half-duplex synchronous bus. It is very similar to SPI without fixed length messages (bit-wised). - * @note the user has to handle the slave select pin (high during operations) so to be able to handle multiple slaves. - * @warning this library implements the M93Cx8 EEPROM operation codes. Other microwire-based ICs might use different ones. - */ - -/* standard libraries */ -#include // standard integer types -#include // general utilities - -/* STM32 (including CM3) libraries */ -#include // Cortex M3 utilities -#include // real-time control clock library -#include // general purpose input output library -#include // timer utilities - -#include "global.h" // global utilities -#include "microwire_master.h" // microwire header and definitions - -/** @defgroup microwire_master_gpio GPIO peripheral used to communicate - * @{ - */ -#define MICROWIRE_MASTER_SDO_PORT A /**< SDO output signal port (to be connected on D slave signal) */ -#define MICROWIRE_MASTER_SDO_PIN 0 /**< SDO output signal pin (to be connected on D slave signal) */ -#define MICROWIRE_MASTER_SDI_PORT A /**< SDO input signal port (to be connected on Q slave signal) */ -#define MICROWIRE_MASTER_SDI_PIN 2 /**< SDO input signal pin (to be connected on Q slave signal) */ -#define MICROWIRE_MASTER_SCK_PORT A /**< SCK output signal port (to be connected on C slave signal) */ -#define MICROWIRE_MASTER_SCK_PIN 4 /**< SCK output signal pin (to be connected on C slave signal) */ -/** @} */ - -/** @defgroup microwire_master_timer timer peripheral used to generate timing for the signal - * @{ - */ -#define MICROWIRE_MASTER_TIMER 4 /**< timer peripheral */ -/** @} */ - -/** address size used in operations (slave specific) */ -uint8_t mirowire_master_address_size = 0; -/** organization used (true=x16, false=x8) */ -bool mirowire_master_organization_x16 = true; - -void microwire_master_setup(uint32_t frequency, bool organization_x16, uint8_t address_size) -{ - // sanity checks - if (0==frequency || 0==address_size) { - return; - } - mirowire_master_address_size = address_size; // save address size - mirowire_master_organization_x16 = organization_x16; // save organisation - - // setup GPIO - rcc_periph_clock_enable(RCC_GPIO(MICROWIRE_MASTER_SDO_PORT)); // enable clock for GPIO domain for SDO signal - gpio_set_mode(GPIO(MICROWIRE_MASTER_SDO_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(MICROWIRE_MASTER_SDO_PIN)); // set SDO signal as output (controlled by the master) - gpio_clear(GPIO(MICROWIRE_MASTER_SDO_PORT), GPIO(MICROWIRE_MASTER_SDO_PIN)); // SDO is idle low - rcc_periph_clock_enable(RCC_GPIO(MICROWIRE_MASTER_SDI_PORT)); // enable clock for GPIO domain for SDI signal - gpio_set_mode(GPIO(MICROWIRE_MASTER_SDI_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(MICROWIRE_MASTER_SDI_PIN)); // set SDI signal as output (controlled by the slave) - rcc_periph_clock_enable(RCC_GPIO(MICROWIRE_MASTER_SCK_PORT)); // enable clock for GPIO domain for SCK signal - gpio_set_mode(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO_MODE_OUTPUT_10_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(MICROWIRE_MASTER_SCK_PIN)); // set SCK signal as output (controlled by the master) - gpio_clear(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // SCK is idle low - - // setup timer to generate timing for the signal - rcc_periph_clock_enable(RCC_TIM(MICROWIRE_MASTER_TIMER)); // enable clock for timer domain - timer_reset(TIM(MICROWIRE_MASTER_TIMER)); // reset timer state - timer_set_mode(TIM(MICROWIRE_MASTER_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up - uint16_t prescaler = rcc_ahb_frequency/(frequency*2)/(uint32_t)(1<<16)+1; // calculate prescaler for most accurate timing for this speed - timer_set_prescaler(TIM(MICROWIRE_MASTER_TIMER), prescaler-1); // set calculated prescaler - uint16_t period = (rcc_ahb_frequency/prescaler)/(frequency*2); // calculate period to get most accurate timing based on the calculated prescaler - timer_set_period(TIM(MICROWIRE_MASTER_TIMER), period-1); // set calculated period - timer_update_on_overflow(TIM(MICROWIRE_MASTER_TIMER)); // only use counter overflow as UEV source (use overflow as timeout) - SCB_SCR |= SCB_SCR_SEVEONPEND; // enable wake up on event (instead of using ISR) - timer_enable_irq(TIM(MICROWIRE_MASTER_TIMER), TIM_DIER_UIE); // enable update interrupt for timer -} - -/** wait for clock tick used to synchronise communication */ -static void microwire_master_wait_clock(void) -{ - while ( !timer_get_flag(TIM(MICROWIRE_MASTER_TIMER), TIM_SR_UIF)) { // wait for timer overflow event for clock change - __asm__("wfe"); // go to sleep and wait for event - } - timer_clear_flag(TIM(MICROWIRE_MASTER_TIMER), TIM_SR_UIF); // clear timer flag - nvic_clear_pending_irq(NVIC_TIM_IRQ(MICROWIRE_MASTER_TIMER)); // clear IRQ flag (else event doesn't wake up) - -} - -/** send bit over microwire - * @param[in] bit bit to send (true = '1', false = '0') - */ -static void microwire_master_send_bit(bool bit) -{ - if (bit) { - gpio_set(GPIO(MICROWIRE_MASTER_SDO_PORT), GPIO(MICROWIRE_MASTER_SDO_PIN)); // set '1' on output - } else { - gpio_clear(GPIO(MICROWIRE_MASTER_SDO_PORT), GPIO(MICROWIRE_MASTER_SDO_PIN)); // set '0' on output - } - microwire_master_wait_clock(); // wait for clock timing - gpio_set(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // make rising edge for slave to sample - microwire_master_wait_clock(); // keep output signal stable while clock is high - gpio_clear(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // put clock back to idle -} - -/** initialize microwire communication and send header (with leading start bit '1') - * @param[in] operation operation code to send (2 bits) - * @param[in] address slave memory address to select - */ -static void microwire_master_start(uint8_t operation, uint32_t address) -{ - // to sanity checks - if (0==mirowire_master_address_size) { // can't send address - return; - } - - // initial setup - gpio_clear(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // ensure clock is low (to sample on rising edge) - timer_set_counter(TIM(MICROWIRE_MASTER_TIMER),0); // reset timer counter - timer_clear_flag(TIM(MICROWIRE_MASTER_TIMER), TIM_SR_UIF); // clear timer flag - nvic_clear_pending_irq(NVIC_TIM_IRQ(MICROWIRE_MASTER_TIMER)); // clear IRQ flag (else event doesn't wake up) - timer_enable_counter(TIM(MICROWIRE_MASTER_TIMER)); // start timer to generate timing - - // send '1' start bit - microwire_master_send_bit(true); // send start bit - // send two bits operation code - if (operation&0x2) { // send first bit (MSb first) - microwire_master_send_bit(true); // send '1' - } else { - microwire_master_send_bit(false); // send '2' - } - if (operation&0x1) { // send second bit (LSb last) - microwire_master_send_bit(true); // send '1' - } else { - microwire_master_send_bit(false); // send '2' - } - - // send address - for (uint8_t bit = mirowire_master_address_size; bit > 0; bit--) { - if ((address>>(bit-1))&0x01) { - microwire_master_send_bit(true); // send '1' address bit - } else { - microwire_master_send_bit(false); // send '0' address bit - } - } - gpio_clear(GPIO(MICROWIRE_MASTER_SDO_PORT), GPIO(MICROWIRE_MASTER_SDO_PIN)); // ensure output is idle low (could be floating) -} - -/** stop microwire communication and end all activities */ -static void microwire_master_stop(void) -{ - timer_disable_counter(TIM(MICROWIRE_MASTER_TIMER)); // disable timer - timer_set_counter(TIM(MICROWIRE_MASTER_TIMER),0); // reset timer counter - timer_clear_flag(TIM(MICROWIRE_MASTER_TIMER), TIM_SR_UIF); // clear timer flag - nvic_clear_pending_irq(NVIC_TIM_IRQ(MICROWIRE_MASTER_TIMER)); // clear IRQ flag - gpio_clear(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // ensure clock is idle low - gpio_clear(GPIO(MICROWIRE_MASTER_SDO_PORT), GPIO(MICROWIRE_MASTER_SDO_PIN)); // ensure output is idle low -} - - -/** read bit from microwire communication - * @return bit value (true = '1', false = '0') - */ -static bool microwire_master_read_bit(void) -{ - microwire_master_wait_clock(); // wait for clock timing - gpio_set(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // make rising edge for slave to output data - microwire_master_wait_clock(); // wait for signal to be stable - gpio_clear(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // set clock low again - return 0!=gpio_get(GPIO(MICROWIRE_MASTER_SDI_PORT), GPIO(MICROWIRE_MASTER_SDI_PIN)); // read input signal -} - -void microwire_master_read(uint32_t address, uint16_t* data, size_t length) -{ - // to sanity checks - if (NULL==data || 0==length || 0==mirowire_master_address_size) { // can't save data - return; - } - - microwire_master_start(0x02, address); // send '10' READ instruction and memory address - - // there should already be a '0' dummy bit - if (0!=gpio_get(GPIO(MICROWIRE_MASTER_SDI_PORT), GPIO(MICROWIRE_MASTER_SDI_PIN))) { // the dummy bit wasn't '0' - goto clean; - } - - // read data - for (size_t i=0; i0; b--) { - if (microwire_master_read_bit()) { // read bit, MSb first - data[i] |= (1<<(b-1)); // set bit - } else { - data[i] &= ~(1<<(b-1)); // clear bit - } - } - } - -clean: - microwire_master_stop(); // stop communication and clean up -} - -void microwire_master_write_enable(void) -{ - // to sanity checks - if (mirowire_master_address_size<2) { // can't send '11...' address - return; - } - - microwire_master_start(0x0, 0x3<<(mirowire_master_address_size-2)); // send '00' WEN operation code and '11...' address - microwire_master_stop(); // clean up -} - -void microwire_master_write_disable(void) -{ - // to sanity checks - if (mirowire_master_address_size<2) { // can't send '00...' address - return; - } - - microwire_master_start(0x0, 0); // send '00' WDS operation code and '00...' address - microwire_master_stop(); // clean up -} - -void microwire_master_write(uint32_t address, uint16_t data) -{ - // to sanity checks - if (0==mirowire_master_address_size) { // can't send address - return; - } - - microwire_master_start(0x01, address); // send '01' WRITE operation code and memory address - - // write data (MSb first) - for (uint8_t b=(mirowire_master_organization_x16 ? 16 : 8); b>0; b--) { - if (data&(1<<(b-1))) { // bit is set - microwire_master_send_bit(true); // send '1' data bit - } else { - microwire_master_send_bit(false); // send '0' data bit - } - } - - microwire_master_stop(); // clean up -} - -void microwire_master_wait_ready(void) -{ - - // initial setup - gpio_clear(GPIO(MICROWIRE_MASTER_SCK_PORT), GPIO(MICROWIRE_MASTER_SCK_PIN)); // ensure clock is low (to sample on rising edge) - timer_set_counter(TIM(MICROWIRE_MASTER_TIMER),0); // reset timer counter - timer_clear_flag(TIM(MICROWIRE_MASTER_TIMER), TIM_SR_UIF); // clear timer flag - nvic_clear_pending_irq(NVIC_TIM_IRQ(MICROWIRE_MASTER_TIMER)); // clear IRQ flag (else event doesn't wake up) - timer_enable_counter(TIM(MICROWIRE_MASTER_TIMER)); // start timer to generate timing - - - // SDI low on busy, high on ready, clock is ignored - while (!microwire_master_read_bit()); // wait until slave is ready - - microwire_master_stop(); // clean up -} - -void microwire_master_erase(uint32_t address) -{ - // sanity checks - if (0==mirowire_master_address_size) { // can't send address - return; - } - - microwire_master_start(0x03, address); // send '11' ERASE operation code and memory address - microwire_master_stop(); // clean up -} - -void microwire_master_erase_all(void) -{ - // sanity checks - if (mirowire_master_address_size<2) { // can't send '11...' address - return; - } - - microwire_master_start(0x00, 0x2<<(mirowire_master_address_size-2)); // send '00' ERAL operation code and '10...' address - microwire_master_stop(); // clean up -} - -void microwire_master_write_all(uint16_t data) -{ - // sanity checks - if (0==mirowire_master_address_size) { // can't send address - return; - } - - microwire_master_start(0x00, 0x1<<(mirowire_master_address_size-2)); // send '00' WRAL operation code and '01...' address - // write data (MSb first) - for (uint8_t b=(mirowire_master_organization_x16 ? 16 : 8); b>0; b--) { - if (data&(1<<(b-1))) { // bit is set - microwire_master_send_bit(true); // send '1' data bit - } else { - microwire_master_send_bit(false); // send '0' data bit - } - } - - microwire_master_stop(); // clean up -} diff --git a/lib/microwire_master.h b/lib/microwire_master.h deleted file mode 100644 index 6a6e47d..0000000 --- a/lib/microwire_master.h +++ /dev/null @@ -1,68 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to communicate using microwore as master (API) - * @file microwire_master.h - * @author King Kévin - * @date 2017 - * @note peripherals used: GPIO @ref microwire_master_gpio, timer @ref microwire_master_timer - * microwire is a 3-Wire half-duplex synchronous bus. It is very similar to SPI without fixed length messages (bit-wised). - * @note the user has to handle the slave select pin (high during operations) so to be able to handle multiple slaves. - * @warning this library implements the M93Cx8 EEPROM operation codes. Other microwire-based ICs might use different ones. - */ -#pragma once - -/** setup microwire peripheral - * @param[in] frequency clock frequency in Hz - * @param[in] organization_x16 if x16 memory organization (16-bits) is used, or x8 (8-bits) - * @param[in] address_size address size in bits - * @note frequency practically limited to 500 kHz due to the software implementation nature - */ -void microwire_master_setup(uint32_t frequency, bool organization_x16, uint8_t address_size); -/** read data from slave memory - * @param[in] address memory address of data to read - * @param[out] data array to store read data - * @param[in] length number of data bytes/words to read - */ -void microwire_master_read(uint32_t address, uint16_t* data, size_t length); -/** enable write and erase operations - * @note on slave boot write is disable to prevent corruption - */ -void microwire_master_write_enable(void); -/** disable write and erase operations - * @note this should be done after every complete write operation to protect against corruption - */ -void microwire_master_write_disable(void); -/** write data to slave memory - * @param[in] address memory address of data to read - * @param[in] data byte/word to write - * @note after each write and before the next operation user should wait for the slave to be ready - */ -void microwire_master_write(uint32_t address, uint16_t data); -/** wait until slave is ready after a write or erase */ -void microwire_master_wait_ready(void); -/** erase memory - * @param[in] address memory address of data to read - * @note after each erase and before the next operation user should wait for the slave to be ready - */ -void microwire_master_erase(uint32_t address); -/** erase all memory - * @note after each erase and before the next operation user should wait for the slave to be ready - */ -void microwire_master_erase_all(void); -/** write data to all slave memory - * @param[in] data byte/word to write - * @note after each write and before the next operation user should wait for the slave to be ready - */ -void microwire_master_write_all(uint16_t data); diff --git a/lib/oled_ssd1306.c b/lib/oled_ssd1306.c deleted file mode 100644 index cf57871..0000000 --- a/lib/oled_ssd1306.c +++ /dev/null @@ -1,140 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** SSD1306 OLED library (code) - * @file oled_ssd1306.c - * @author King Kévin - * @date 2018 - * @note peripherals used: I2C @ref oled_ssd1306_i2c - */ -/* standard libraries */ -#include // standard integer types -#include // boolean type -#include // general utilities - -/* STM32 (including CM3) libraries */ -#include // I2C library - -/* own libraries */ -#include "global.h" // global utilities -#include "oled_ssd1306.h" // OLED definitions -#include "i2c_master.h" // I2C header and definitions - -/** SSD1306 OLED display I2C slave address */ -#define OLED_SSD1306_SLAVE 0x3c - -/** @defgroup oled_ssd1306_i2c I2C peripheral to communicate with the SSD1306 OLED - * @{ - */ -#define OLED_SSD1306_I2C I2C1 /**< I2C peripheral */ -/** @} */ - -bool oled_ssd1306_setup(void) -{ - if (!i2c_master_check_signals(OLED_SSD1306_I2C)) { // check if there are pull-ups to operator I2C - return false; - } - i2c_master_setup(OLED_SSD1306_I2C, 400); // setup I2C bus ( SSD1306 supports an I2C cleck up to 400 kHz) - const uint8_t oled_init[] = { - 0x00, // control byte: continuous (multiple byes), command - 0xae, // Set Display ON/OFF: OFF - // hardware configuration - 0xa8, 0x3f, // Set Multiplex Ratio: 64 - 0xd3, 0x00, // Set Display Offset: 0 - 0xa1, // Set Segment Re-map: column address 0 is mapped to SEG127 - 0xc8, // Set COM Output Scan Direction: normal mode (RESET) Scan from COM[N-1] to COM[0] - 0xda, 0x12, // Set COM Pins Hardware Configuration: Alternative COM pin configuration, Disable COM Left/Right remap - 0x40, // Set Display Start Line: start line register from 0 - // fundamental commands - 0x81, 0xff, // Set Contrast Control: 256 - 0xa6, // Set Normal/Inverse Display: Normal display (RESET) - // Timing & Driving Scheme Setting - 0xd5, 0xf0, // Set Display Clock Divide Ratio/Oscillator Frequency: Divide ratio=129, F_OSC=1 - 0xd9, 0x22, // Set Pre-charge Period: Phase 1=2 DCLK, Phase 2=2DCLK - 0xdb, 0x20, // Set V_COMH Deselect Level: ~0.77xV_CC - // Charge Pump - 0x8d, 0x14, // Charge Pump Setting: Enable Charge Pump - // Addressing Setting - 0x20, 0x00 // Set Memory Addressing Mode: Horizontal Addressing Mode - }; // command to initialize the display - return I2C_MASTER_RC_NONE==i2c_master_slave_write(OLED_SSD1306_I2C, OLED_SSD1306_SLAVE, false, oled_init, LENGTH(oled_init)); // send command to initialize display -} - -void oled_ssd1306_on(void) -{ - const uint8_t oled_display_on[] = { - 0x80, // control byte: no continuation, command - 0xaf, // Set Display ON/OFF: ON - }; // command to switch on display - i2c_master_slave_write(OLED_SSD1306_I2C, OLED_SSD1306_SLAVE, false, oled_display_on, LENGTH(oled_display_on)); // sent command to switch on display -} - -void oled_ssd1306_off(void) -{ - const uint8_t oled_display_off[] = { - 0x80, // control byte: no continuation, command - 0xae, // Set Display ON/OFF: OFF - }; // command to switch off display - i2c_master_slave_write(OLED_SSD1306_I2C, OLED_SSD1306_SLAVE, false, oled_display_off, LENGTH(oled_display_off)); // sent command to switch onff display -} - -void oled_ssd1306_test(void) -{ - const uint8_t oled_entire_display_on[] = { - 0x80, // control byte: no continuation, command - 0xa5 // Entire Display ON: Entire display ON Output ignores RAM content - }; // command to set entire display on - i2c_master_slave_write(OLED_SSD1306_I2C, OLED_SSD1306_SLAVE, false, oled_entire_display_on, LENGTH(oled_entire_display_on)); // send command to switch entire display on - oled_ssd1306_on(); // set display on - sleep_ms(200); // let is on for a bit (enough for the user to see it is completely on - oled_ssd1306_off(); // set display off - const uint8_t oled_entire_display_ram[] = { - 0x80, // control byte: no continuation, command - 0xa4 // Entire Display ON: Resume to RAM content display - }; // command to display RAM - i2c_master_slave_write(OLED_SSD1306_I2C, OLED_SSD1306_SLAVE, false, oled_entire_display_ram, LENGTH(oled_entire_display_ram)); // send command to display RAM -} - -void oled_ssd1306_display(const uint8_t* display_data, uint16_t display_length) -{ - // verify input - if (0==display_length || NULL==display_data) { - return; - } - - const uint8_t oled_start_page[] = { - 0x00, // control byte: continuous (multiple byes), command - 0xb0, // Set Page Start Address for Page Addressing Mode: PAGE0 - 0x00, // Set Lower Column Start Address for Page Addressing Mode: 0 - 0x10 // Set Higher Column Start Address for Page Addressing Mode: 0 - }; // command to set addressing mode - i2c_master_slave_write(OLED_SSD1306_I2C, OLED_SSD1306_SLAVE, false, oled_start_page, LENGTH(oled_start_page)); // send command to set addressing mode - if (I2C_MASTER_RC_NONE!=i2c_master_start(OLED_SSD1306_I2C)) { // send start condition - return; - } - if (I2C_MASTER_RC_NONE!=i2c_master_select_slave(OLED_SSD1306_I2C, OLED_SSD1306_SLAVE, false, true)) { // select OLED display - return; - } - const uint8_t oled_data[] = { - 0x40, // control byte: continuous (multiple byes), data - }; - if (I2C_MASTER_RC_NONE!=i2c_master_write(OLED_SSD1306_I2C, oled_data, LENGTH(oled_data))) { // send data header - return; - } - if (I2C_MASTER_RC_NONE!=i2c_master_write(OLED_SSD1306_I2C, display_data, display_length)) { // send template picture to display - return; - } - i2c_master_stop(OLED_SSD1306_I2C); // send stop condition -} - diff --git a/lib/oled_ssd1306.h b/lib/oled_ssd1306.h deleted file mode 100644 index 00ad79c..0000000 --- a/lib/oled_ssd1306.h +++ /dev/null @@ -1,36 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** SSD1306 OLED library (API) - * @file oled_ssd1306.h - * @author King Kévin - * @date 2018 - * @note peripherals used: I2C @ref oled_ssd1306_i2c - */ - -/** setup OLED display - * @return if the display setup is successful, else the display is probably not on the I2C bus - */ -bool oled_ssd1306_setup(void); -/** switch OLED display on */ -void oled_ssd1306_on(void); -/** switch OLED display off */ -void oled_ssd1306_off(void); -/** test OLED display: switch entire screen on for a brief time */ -void oled_ssd1306_test(void); -/** send data to display to OLED display - * @param[in] display_data data to display (first byte is left column, MSb is top pixel, warps pages) - * @param[in] display_length length of data to display - */ -void oled_ssd1306_display(const uint8_t* display_data, uint16_t display_length); diff --git a/lib/onewire_master.c b/lib/onewire_master.c deleted file mode 100644 index 730eb6f..0000000 --- a/lib/onewire_master.c +++ /dev/null @@ -1,513 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library for 1-wire protocol as master - * @file - * @author King Kévin - * @date 2017-2018 - * @note peripherals used: timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio - * @note overdrive mode is not provided - * @implements 1-Wire protocol description from Book of iButton Standards - */ - -/* standard libraries */ -#include // standard integer types -#include // boolean type -#include // NULL definition - -/* STM32 (including CM3) libraries */ -#include // Cortex M3 utilities -#include // interrupt handler -#include // real-time control clock library -#include // general purpose input output library -#include // timer library - -/* own libraries */ -#include "global.h" // help macros -#include "interrupt.h" // runtime interrupt table -#include "onewire_master.h" // own definitions - -/** @defgroup onewire_master_timer timer used to measure 1-wire signal timing - * @{ - */ -#define ONEWIRE_MASTER_TIMER 5 /**< timer ID */ -/** @} */ - -/** set if the timer ISR should be set in the interrupt table instead of the vector table - * @note the vector table is faster, but doesn't allow to change the ISR - */ -#define ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE false - -/** state of 1-Wire communication */ -volatile enum { - ONEWIRE_STATE_IDLE, /**< no current communication */ - ONEWIRE_STATE_DONE, /**< communication complete */ - ONEWIRE_STATE_ERROR, /**< communication error */ - ONEWIRE_STATE_MASTER_RESET, /**< reset pulse started */ - ONEWIRE_STATE_SLAVE_PRESENCE, /**< waiting for slave response to reset pulse */ - ONEWIRE_STATE_MASTER_WRITE, /**< master is writing bits */ - ONEWIRE_STATE_MASTER_READ, /**< master is reading bits */ - ONEWIRE_MAX /** to count the number of possible states */ -} onewire_master_state = ONEWIRE_STATE_IDLE; - -static volatile bool slave_presence = false; /**< if slaves have been detected */ -static uint8_t* buffer = NULL; /**< input/output buffer for read/write commands/functions */ -static uint32_t buffer_size = 0; /**< size of buffer in bits */ -static volatile uint32_t buffer_bit = 0; /**< number of bits read/written */ -#if defined(ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE) && ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE -static void (*isr_backup)(void) = NULL; /**< backup for the existing timer ISR */ -static bool irq_backup = false; /**< backup for the existing timer IRQ */ -#endif - -/** interrupt service routine called for timer */ -#if defined(ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE) && ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE -static void onewire_master_timer_isr(void) -#else -void TIM_ISR(ONEWIRE_MASTER_TIMER)(void) -#endif -{ - if (timer_get_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_UIF)) { // overflow update event happened - timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_UIF); // clear flag - switch (onewire_master_state) { - case ONEWIRE_STATE_MASTER_RESET: // reset pulse has been started - timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC4IF); // clear output compare flag - timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC4IE); // enable compare interrupt for presence detection - gpio_set(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // set signal high again for slaves to respond - onewire_master_state = ONEWIRE_STATE_SLAVE_PRESENCE; // set new state - break; - case ONEWIRE_STATE_SLAVE_PRESENCE: // waiting for slave presence but none received - timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer - timer_disable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC4IE); // disable compare interrupt for presence detection - onewire_master_state = ONEWIRE_STATE_DONE; // go to next state - break; - case ONEWIRE_STATE_MASTER_READ: // end of time slot and recovery time for reading bit - case ONEWIRE_STATE_MASTER_WRITE: // end of time slot and recovery time for writing bit - if (buffer_bit 480 us used for the reset pulse ( 1/(72E6/1/(2**16))=910us ) - - // use comparator to time signal (without using the output), starting at slot start - timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC1IF); // clear flag - timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC1, 1*(rcc_ahb_frequency/1000000)-1); // use compare function to time master pulling low when reading (1 < Tlowr < 15) - timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC2IF); // clear flag - timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC2, 7*(rcc_ahb_frequency/1000000)-1); // use compare function to read or write 0 or 1 (1 < Trw < 15) - timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC3IF); // clear flag - timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC3, 62*(rcc_ahb_frequency/1000000)-1); // use compare function to end time slot (60 < Tslot < 120), this will be followed by a recovery time (end of timer) - timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC4IF); // clear flag - timer_set_oc_value(TIM(ONEWIRE_MASTER_TIMER), TIM_OC4, (70-10)*(rcc_ahb_frequency/1000000)-1); // use compare function to detect slave presence (15 < Tpdh < 60 + 60 < Tpdl < 240), with hand tuning - timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_UIF); // clear update (overflow) flag - timer_update_on_overflow(TIM(ONEWIRE_MASTER_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout) - timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_UIE); // enable update interrupt for overflow -#if defined(ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE) && ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE - isr_backup = interrupt_table[NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)]; // backup timer ISR - interrupt_table[NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)] = &onewire_master_timer_isr; // set the 1-wire timer ISR - irq_backup = nvic_get_irq_enabled(NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)); // backup timer IRQ setting -#endif - nvic_enable_irq(NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)); // catch interrupt in service routine - - slave_presence = false; // reset state - onewire_master_state = ONEWIRE_STATE_IDLE; // reset state -} - -void onewire_master_release(void) -{ - // release timer - timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer - timer_reset(TIM(ONEWIRE_MASTER_TIMER)); // reset timer state - rcc_periph_clock_disable(RCC_TIM(ONEWIRE_MASTER_TIMER)); // disable clock for timer peripheral - - // release GPIO - gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(ONEWIRE_MASTER_PIN)); // put back to input floating - - // disable timer ISR -#if defined(ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE) && ONEWIRE_MASTER_TIMER_USE_INTERRUPT_TABLE - if (!irq_backup) { // don't disable the IRQ if there was already enabled - nvic_disable_irq(NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)); // stop timer IRQ - } - interrupt_table[NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)] = isr_backup; // set back original timer ISR -#else - nvic_disable_irq(NVIC_TIM_IRQ(ONEWIRE_MASTER_TIMER)); // stop timer IRQ -#endif -} - -bool onewire_master_reset(void) -{ - // prepare timer - timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it - timer_set_counter(TIM(ONEWIRE_MASTER_TIMER),0); // reset counter - timer_set_period(TIM(ONEWIRE_MASTER_TIMER), 490*(rcc_ahb_frequency/1000000)-1); // set timeout to > 480 us (480 < Trst) - - slave_presence = false; // reset state - onewire_master_state = ONEWIRE_STATE_MASTER_RESET; // set new state - - gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor) - gpio_clear(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal low to start reset (it's not important if it was low in the first place since the reset pulse has no maximum time) - timer_enable_counter(TIM(ONEWIRE_MASTER_TIMER)); // start timer - - while (onewire_master_state!=ONEWIRE_STATE_DONE && onewire_master_state!=ONEWIRE_STATE_ERROR) { // wait until reset procedure completed - __WFI(); // go to sleep - } - if (ONEWIRE_STATE_ERROR==onewire_master_state) { // an error occurred - return false; - } - - return slave_presence; -} - -/** write bits on 1-Wire bus - * @warning buffer_size must be set to the number of bits to writen and buffer must contain the data to write - * @return if write succeeded - */ -static bool onewire_master_write(void) -{ - buffer_bit = 0; // reset bit index - onewire_master_state = ONEWIRE_STATE_MASTER_WRITE; // set new state - - // prepare timer - timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it - timer_set_counter(TIM(ONEWIRE_MASTER_TIMER), 0); // reset counter - timer_set_period(TIM(ONEWIRE_MASTER_TIMER), TIM_CCR3(TIM(ONEWIRE_MASTER_TIMER))+2*(rcc_ahb_frequency/1000000)); // set time for new time slot (recovery timer Trec>1, after time slot end ) - timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC2IF); // clear output compare flag - timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC2IE); // enable compare interrupt for bit setting - timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC3IF); // clear output compare flag - timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC3IE); // enable compare interrupt for end of time slow - - // start writing - gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor) - gpio_clear(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal low to start slot - timer_enable_counter(TIM(ONEWIRE_MASTER_TIMER)); // start timer - while (onewire_master_state!=ONEWIRE_STATE_DONE && onewire_master_state!=ONEWIRE_STATE_ERROR) { // wait until write procedure completed - __WFI(); // go to sleep - } - if (ONEWIRE_STATE_ERROR==onewire_master_state) { // an error occurred - return false; - } - return true; -} - -/** read bits on 1-Wire bus - * @warning buffer_size must be set to the number of bits to read - * @return if read succeeded - */ -static bool onewire_master_read(void) -{ - if (0==buffer_size) { // check input - return false; - } - buffer_bit = 0; // reset bit index - onewire_master_state = ONEWIRE_STATE_MASTER_READ; // set new state - - // prepare timer - timer_disable_counter(TIM(ONEWIRE_MASTER_TIMER)); // disable timer to reconfigure it - timer_set_counter(TIM(ONEWIRE_MASTER_TIMER), 0); // reset counter - timer_set_period(TIM(ONEWIRE_MASTER_TIMER), TIM_CCR3(TIM(ONEWIRE_MASTER_TIMER))+2*(rcc_ahb_frequency/1000000)); // set time for new time slot (recovery timer Trec>1, after time slot end ) - timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC1IF); // clear output compare flag - timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC1IE); // enable compare interrupt for stop pulling low - timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC2IF); // clear output compare flag - timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC2IE); // enable compare interrupt for bit setting - timer_clear_flag(TIM(ONEWIRE_MASTER_TIMER), TIM_SR_CC3IF); // clear output compare flag - timer_enable_irq(TIM(ONEWIRE_MASTER_TIMER), TIM_DIER_CC3IE); // enable compare interrupt for end of time slow - - // start reading - gpio_set_mode(GPIO(ONEWIRE_MASTER_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_MASTER_PIN)); // normal 1-Wire communication (only using external pull-up resistor) - gpio_clear(GPIO(ONEWIRE_MASTER_PORT),GPIO(ONEWIRE_MASTER_PIN)); // pull signal low to start slot - timer_enable_counter(TIM(ONEWIRE_MASTER_TIMER)); // start timer - while (onewire_master_state!=ONEWIRE_STATE_DONE && onewire_master_state!=ONEWIRE_STATE_ERROR) { // wait until read procedure completed - __WFI(); // go to sleep - } - if (ONEWIRE_STATE_ERROR==onewire_master_state) { // an error occurred - return false; - } - return true; -} - -uint8_t onewire_master_crc(uint8_t* data, uint32_t length) -{ - if (NULL==data || 0==length) { // check input - return 0; // wrong input - } - - uint8_t crc = 0x00; // initial value - for (uint8_t i=0; i>1)^0x8C; // // shift to the right (for the next bit) and XOR with (reverse) polynomial - } else { - crc >>= 1; // just shift right (for the next bit) - } - } - } - return crc; -} - -bool onewire_master_read_byte(uint8_t* data) -{ - if (NULL==data) { // check input - return false; // wrong input - } - - // read data - buffer_size = 8; // save number of bits to read (1 byte) - buffer = data; // set the buffer to the data to write - if (!onewire_master_read()) { // read bits from slave - return false; // an error occurred - } - return true; -} - -bool onewire_master_write_byte(uint8_t data) -{ - // send data byte - buffer_size = 8; // function command is only one byte - buffer = &data; // set the buffer to the function code - if (!onewire_master_write()) { // send command - return false; // an error occurred - } - return true; -} - -bool onewire_master_function_read(uint8_t function, uint8_t* data, uint32_t bits) -{ - // send function command - if (!onewire_master_write_byte(function)) { - return false; // an error occurred - } - - if (NULL==data || 0==bits) { // there is no data to read - return true; // operation completed - } - - // read data - buffer_size = bits; // save number of bits to read - buffer = data; // set the buffer to the data to write - if (!onewire_master_read()) { // read bits from slave - return false; // an error occurred - } - - return true; -} - -bool onewire_master_function_write(uint8_t function, uint8_t* data, uint32_t bits) -{ - // send function command - if (!onewire_master_write_byte(function)) { - return false; // an error occurred - } - - if (NULL==data || 0==bits) { // there is no data to read - return true; // operation completed - } - - // copy data from user buffer - buffer_size = bits; // save number of bits to write - buffer = data; // set the buffer to the data to write - // write data - if (!onewire_master_write()) { // read bits from slave - return false; // an error occurred - } - - return true; -} - -uint64_t onewire_master_rom_read(void) -{ - uint8_t rom_code[8] = {0}; // to store 64 bits ROM code - if (!onewire_master_function_read(0x33, rom_code, 64)) { // read ROM code (I'm cheating because the ROM command isn't a function command, but it works the same way in the end) - return 0; // an error occurred - } - if (onewire_master_crc(rom_code, LENGTH(rom_code))) { // verify checksum - return 0; // checksum is wrong (not 0) - } - - // return ROM code - uint64_t code = 0; - for (uint32_t i=0; i<8; i++) { - code += (uint64_t)rom_code[i]<<(8*i); // add byte - } - - return code; -} - -bool onewire_master_rom_search(uint64_t* code, bool alarm) -{ - static uint8_t conflict_last = 64; // on which bit has the conflict been detected (64 means there hasn't been) - uint8_t conflict_current = 64; // to remember on which bit the last unknown conflict has been detected - uint8_t bits[1] = {0}; // small buffer to store the bits used to search the ROM codes - - // send SEARCH ROM command - uint8_t command = 0xf0; // SEARCH ROM command - if (alarm) { // looking only for ROM codes for slaves in alarm state - command = 0xec; // use ALARM SEARCH ROM command instead - } - if (!onewire_master_function_read(command, NULL, 0)) { // send SEARCH ROM command - goto end; // an error occurred - } - - if (conflict_last>=64) { // no previous conflict has been detected - *code = 0; // restart search codes - } - - buffer = bits; // buffer to read up to two bits - for (uint8_t bit=0; bit<64; bit++) { // go through all 64 bits ROM code - buffer_size = 2; // read two first bits to detect conflict - if (!onewire_master_read()) { // read ROM ID from slave - goto end; // an error occurred - } - switch (buffer[0]&0x03) { // check 2 bits received - case 0: // collision detected - if (bit==conflict_last) { // this conflict is known - *code |= (((uint64_t)1)<>bit); // set bit to send - if (!onewire_master_write()) { // send bit - goto end; // an error has occurred - } - } - // verify ROM code - uint8_t rom_code[8] = {0}; // to store ROM code - for (uint8_t i=0; i>(8*i); // split and save last code in ROM code - } - if (onewire_master_crc(rom_code, LENGTH(rom_code))) { // verify checksum - *code = 0; // return the last code found since it's valid - } - -end: - conflict_last = conflict_current; // update the last seen and unknown conflict - if (conflict_current<64) { // we have seen an unknown conflict - return true; // tell there are more slaves - } else { // no conflict seen - return false; // no more slaves - } -} - -bool onewire_master_rom_skip(void) -{ - if (!onewire_master_function_write(0xcc, NULL, 0)) { // send SKIP ROM command - return false; // an error occurred - } - return true; -} - -bool onewire_master_rom_match(uint64_t code) -{ - uint8_t rom_code[8] = {0}; // to store ROM code - for (uint8_t i=0; i>(8*i); // split and save code in ROM code - } - if (!onewire_master_function_write(0x55, rom_code, 64)) { // send MATCH ROM command with ROM code - return false; // an error occurred - } - return true; -} - diff --git a/lib/onewire_master.h b/lib/onewire_master.h deleted file mode 100644 index ca218c7..0000000 --- a/lib/onewire_master.h +++ /dev/null @@ -1,387 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library for 1-wire protocol as master - * @file - * @author King Kévin - * @date 2017-2018 - * @note peripherals used: timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio - * @note overdrive mode is not provided - */ -#pragma once - -/** @defgroup onewire_master_gpio GPIO used for 1-wire signal - * @note external pull-up resistor on pin is required (< 5 kOhm) - * @{ - */ -#define ONEWIRE_MASTER_PORT C /**< GPIO port */ -#define ONEWIRE_MASTER_PIN 9 /**< GPIO pin */ -/** @} */ - -/** setup 1-wire peripheral - */ -void onewire_master_setup(void); -/** release 1-wire peripheral - */ -void onewire_master_release(void); -/** send reset pulse - * @return if slaves have indicated their presence - */ -bool onewire_master_reset(void); -/** compute CRC for 1-Wire - * @note this CRC-8 uses normal polynomial 0x31, reverse polynomial 0x8C, start value 0x00 - * @param[in] data bytes on which to calculate CRC checksum on - * @param[in] length number of bytes in data - * @return computed CRC checksum - */ -uint8_t onewire_master_crc(uint8_t* data, uint32_t length); -/** send READ ROM command and read ROM code response - * @note user needs to send reset pulse before - * @return ROM code read - */ -uint64_t onewire_master_rom_read(void); -/** send SEARCH ROM command - * @note user needs to send reset pulse before - * @warning undefined behaviour if a ROM code different than the last found is provided - * @param[in,out] code use 0 to start search ROM code from scratch, or last know value to search next; writes back next ROM code found, or 0 if error occurred - * @param[in] alarm search only for ROM codes for slaves with an alarm flag set - * @return if an additional slave has been detected - * @warning when the code found is 0 it very probably means that the 1-wire line is not pulled up instead of actually having found a slave with ROM code 0 - */ -bool onewire_master_rom_search(uint64_t* code, bool alarm); -/** send SKIP ROM command (all slaves on the bus will be selected) - * @note user needs to send reset pulse before - * @return if operation succeeded - */ -bool onewire_master_rom_skip(void); -/** send MATCH ROM command to select a specific slave - * @note user needs to send reset pulse before - * @param[in] code ROM code of slave to select - * @return if operation succeeded - */ -bool onewire_master_rom_match(uint64_t code); -/** read data byte - * @note it is up to the user to send the reset pulse - * @param[out] data buffer to save data read - * @return if operation succeeded - */ -bool onewire_master_read_byte(uint8_t* data); -/** write data byte - * @note it is up to the user to send the reset pulse - * @param[in] data byte to write - * @return if operation succeeded - */ -bool onewire_master_write_byte(uint8_t data); -/** issue function and read data - * @note user needs to send a ROM command before - * @param[in] function function command to send - * @param[out] data buffer to save read bits (NULL if only the function command should be sent) - * @param[in] bits number of bits to read (0 if only the function command should be sent) - * @return if operation succeeded - */ -bool onewire_master_function_read(uint8_t function, uint8_t* data, uint32_t bits); -/** issue function and write data - * @note user needs to send a ROM command before - * @param[in] function function command to send - * @param[out] data data to write (NULL if only the function command should be sent) - * @param[in] bits number of bits to write (0 if only the function command should be sent) - * @return if operation succeeded - */ -bool onewire_master_function_write(uint8_t function, uint8_t* data, uint32_t bits); - -/** device corresponding to a family code - */ -struct onewire_family_code_t { - uint8_t code; /**< ROM ID code */ - const char* device; /**< device name(s) */ -}; - -/** list of possible devices corresponding to the family code - * sources: - * - http://owfs.org/index.php?page=family-code-list - * - http://owfs.sourceforge.net/family.html - * - https://www.maximintegrated.com/en/app-notes/index.mvp/id/155 - * - https://github.com/owfs/owfs-doc/wiki/1Wire-Device-List - * - IDs seen or reported - */ -static const struct onewire_family_code_t onewire_family_codes[] = { - { - .code = 0x01, - .device = "DS1990R/DS2401/DS2411/DS2490A", - }, - { - .code = 0x02, - .device = "DS1991/DS1425", - }, - { - .code = 0x04, - .device = "DS1994/DS2404", - }, - { - .code = 0x05, - .device = "DS2405", - }, - { - .code = 0x06, - .device = "DS1993", - }, - { - .code = 0x08, - .device = "DS1992", - }, - { - .code = 0x09, - .device = "DS1982/DS2502/DS2703/DS2704", - }, - { - .code = 0x0a, - .device = "DS1995", - }, - { - .code = 0x0b, - .device = "DS1985/DS2505", - }, - { - .code = 0x0c, - .device = "DS1996", - }, - { - .code = 0x0f, - .device = "DS1986/DS2506", - }, - { - .code = 0x10, - .device = "DS1920/DS18S20", - }, - { - .code = 0x12, - .device = "DS2406/DS2407", - }, - { - .code = 0x14, - .device = "DS1971/DS2430A", - }, - { - .code = 0x16, - .device = "DS1954/DS1957", - }, - { - .code = 0x18, - .device = "DS1963S/DS1962", - }, - { - .code = 0x1a, - .device = "DS1963L", - }, - { - .code = 0x1b, - .device = "DS2436", - }, - { - .code = 0x1c, - .device = "DS28E04-100", - }, - { - .code = 0x1d, - .device = "DS2423", - }, - { - .code = 0x1e, - .device = "DS2437", - }, - { - .code = 0x1f, - .device = "DS2409", - }, - { - .code = 0x20, - .device = "DS2450", - }, - { - .code = 0x21, - .device = "DS1921", - }, - { - .code = 0x22, - .device = "DS1922", - }, - { - .code = 0x23, - .device = "DS1973/DS2433", - }, - { - .code = 0x24, - .device = "DS1904/DS2415", - }, - { - .code = 0x26, - .device = "DS2438", - }, - { - .code = 0x27, - .device = "DS2417", - }, - { - .code = 0x28, - .device = "DS18B20", - }, - { - .code = 0x29, - .device = "DS2408", - }, - { - .code = 0x2c, - .device = "DS2890", - }, - { - .code = 0x2d, - .device = "DS1972/DS2431", - }, - { - .code = 0x2e, - .device = "DS2770", - }, - { - .code = 0x2f, - .device = "DS28E01-100", - }, - { - .code = 0x30, - .device = "DS2760/DS2761/DS2762", - }, - { - .code = 0x31, - .device = "DS2720", - }, - { - .code = 0x32, - .device = "DS2780", - }, - { - .code = 0x33, - .device = "DS1961S/DS2432", - }, - { - .code = 0x34, - .device = "DS2703", - }, - { - .code = 0x35, - .device = "DS2755", - }, - { - .code = 0x36, - .device = "DS2740", - }, - { - .code = 0x37, - .device = "DS1977", - }, - { - .code = 0x3a, - .device = "DS2413", - }, - { - .code = 0x3b, - .device = "DS1825/MAX31826/MAX31850", - }, - { - .code = 0x3d, - .device = "DS2781", - }, - { - .code = 0x41, - .device = "DS1922/DS1923/DS2422", - }, - { - .code = 0x42, - .device = "DS28EA00", - }, - { - .code = 0x43, - .device = "DS28EC20", - }, - { - .code = 0x44, - .device = "DS28E10", - }, - { - .code = 0x51, - .device = "DS2751", - }, - { - .code = 0x7e, - .device = "EDS00xx", - }, - { - .code = 0x81, - .device = "DS1420/DS2490R/DS2490B", - }, - { - .code = 0x82, - .device = "DS1425", - }, - { - .code = 0x84, - .device = "DS2404S", - }, - { - .code = 0x89, - .device = "DS1982U/DS2502", - }, - { - .code = 0x8b, - .device = "DS1985U/DS2505", - }, - { - .code = 0x8f, - .device = "DS1986U/DS2506", - }, - { - .code = 0xa0, - .device = "mRS001", - }, - { - .code = 0xa1, - .device = "mVM001", - }, - { - .code = 0xa2, - .device = "mCM001", - }, - { - .code = 0xa6, - .device = "mTS017", - }, - { - .code = 0xb1, - .device = "mTC001", - }, - { - .code = 0xb2, - .device = "mAM001", - }, - { - .code = 0xb3, - .device = "DS2432/mTC002", - }, - { - .code = 0xfc, - .device = "BAE0910/BAE0911", - }, - { - .code = 0xff, - .device = "Swart LCD", - } -}; diff --git a/lib/onewire_slave.c b/lib/onewire_slave.c deleted file mode 100644 index bf5ab0d..0000000 --- a/lib/onewire_slave.c +++ /dev/null @@ -1,409 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library for 1-wire protocol as master (code) - * @file onewire_slave.c - * @author King Kévin - * @date 2017 - * @note peripherals used: GPIO and timer @ref onewire_slave_timer, GPIO @ref onewire_slave_gpio - * @note overdrive mode is not supported - * @implements 1-Wire protocol description from Book of iButton Standards - */ - -/* standard libraries */ -#include // standard integer types -#include // boolean type -#include // NULL definition - -/* STM32 (including CM3) libraries */ -#include // Cortex M3 utilities -#include // interrupt handler -#include // real-time control clock library -#include // general purpose input output library -#include // timer library -#include // external interrupt library - -/* own libraries */ -#include "global.h" // help macros -#include "onewire_slave.h" // own definitions - -/** @defgroup onewire_slave_timer timer used to measure 1-wire signal timing - * @{ - */ -#define ONEWIRE_SLAVE_TIMER 2 /**< timer ID */ -/** @} */ - -/** @defgroup onewire_slave_gpio GPIO used for 1-wire signal - * @warning ensure no same pin number on other parts are used for external interrupts - * @note external pull-up resistor on pin is required (< 5 kOhm), generally provided by the master - * @{ - */ -#define ONEWIRE_SLAVE_PORT A /**< GPIO port */ -#define ONEWIRE_SLAVE_PIN 4 /**< GPIO pin */ -/** @} */ - -/** state of 1-Wire communication */ -static volatile enum { - ONEWIRE_STATE_IDLE, /**< no current communication */ - ONEWIRE_STATE_RESET, /**< reset pulse has been detected */ - ONEWIRE_STATE_WAIT_PRESENCE, /**< waiting before sending the presence pulse */ - ONEWIRE_STATE_PULSE_PRESENCE, /**< sending the presence pulse */ - ONEWIRE_STATE_ROM_COMMAND, /**< slave is reading ROM command bits */ - ONEWIRE_STATE_ROM_READ, /**< slave is sending ROM code in response to ROM command READ ROM */ - ONEWIRE_STATE_ROM_MATCH, /**< master is sending ROM code to select slave */ - ONEWIRE_STATE_ROM_SEARCH_TRUE, /**< master is searching ROM code, slave will send first bit (not negated) */ - ONEWIRE_STATE_ROM_SEARCH_FALSE, /**< master is searching ROM code, slave will send first bit (not negated) */ - ONEWIRE_STATE_ROM_SEARCH_SELECT, /**< master is searching ROM code, slave will read selected bit */ - ONEWIRE_STATE_FUNCTION_COMMAND, /**< slave is reading function command bits */ - ONEWIRE_STATE_FUNCTION_DATA, /**< waiting for user to provide data to transfer */ - ONEWIRE_STATE_FUNCTION_READ, /**< slave is reading bits */ - ONEWIRE_STATE_FUNCTION_WRITE, /**< slave is writing bits */ - ONEWIRE_MAX /** to count the number of possible states */ -} onewire_slave_state = ONEWIRE_STATE_IDLE; - -static uint8_t onewire_slave_rom_code[8] = {0}; /**< slave ROM code */ - -volatile bool onewire_slave_function_code_received = false; -volatile uint8_t onewire_slave_function_code = 0; -volatile bool onewire_slave_transfer_complete = false; - -static volatile uint8_t bits_buffer = 0; /**< buffer for the incoming bits (up to one byte) */ -static volatile uint32_t bits_bit = 0; /**< number of incoming bits */ -static volatile uint8_t* onewire_slave_transfer_data = NULL; /**< data to transfer (read or write) */ -static volatile uint32_t onewire_slave_transfer_bits = 0; /**< number of bits to transfer */ - -/** compute CRC for 1-Wire - * @note this CRC-8 uses normal polynomial 0x31, reverse polynomial 0x8C, start value 0x00 - * @param[in] data bytes on which to calculate CRC checksum on - * @param[in] length number of bytes in data - * @return computed CRC checksum - */ -static uint8_t onewire_slave_crc(uint8_t* data, uint32_t length) -{ - if (NULL==data || 0==length) { // check input - return 0; // wrong input - } - - uint8_t crc = 0x00; // initial value - for (uint8_t i=0; i>1)^0x8C; // // shift to the right (for the next bit) and XOR with (reverse) polynomial - } else { - crc >>= 1; // just shift right (for the next bit) - } - } - } - return crc; -} - -void onewire_slave_setup(uint8_t family, uint64_t serial) -{ - // save ROM code (LSB first) - onewire_slave_rom_code[0] = family; - onewire_slave_rom_code[1] = serial >> 40; - onewire_slave_rom_code[2] = serial >> 32; - onewire_slave_rom_code[3] = serial >> 24; - onewire_slave_rom_code[4] = serial >> 16; - onewire_slave_rom_code[5] = serial >> 8; - onewire_slave_rom_code[6] = serial >> 0; - onewire_slave_rom_code[7] = onewire_slave_crc(onewire_slave_rom_code, 7); // calculate CRC - - // setup timer to generate/measure signal timing - rcc_periph_clock_enable(RCC_TIM(ONEWIRE_SLAVE_TIMER)); // enable clock for timer peripheral - timer_reset(TIM(ONEWIRE_SLAVE_TIMER)); // reset timer state - timer_set_mode(TIM(ONEWIRE_SLAVE_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up - timer_set_prescaler(TIM(ONEWIRE_SLAVE_TIMER), 1-1); // don't use prescale since this 16 bits timer allows to wait > 480 us used for the reset pulse ( 1/(72E6/1/(2**16))=910us ) - - // use comparator to time signal (without using the output), starting at slot start - timer_set_period(TIM(ONEWIRE_SLAVE_TIMER), 480*(rcc_ahb_frequency/1000000)-1-1300); // minimum time needed for a reset pulse (480 < Trst), plus hand tuning - timer_set_oc_mode(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC1, TIM_OCM_FROZEN); - timer_set_oc_value(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC1, 16*(rcc_ahb_frequency/1000000)-1); // time to wait before sending the presence pulse, after the rising edge of the reset pulse (15 < Tpdh < 60) - timer_set_oc_mode(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC2, TIM_OCM_FROZEN); - timer_set_oc_value(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC2, 45*(rcc_ahb_frequency/1000000)-1-350); // time to sample the bit after being set (1 < Tlow1 < 15, 60 < Tslot < 120), or stop sending the bit use compare function to detect slave presence (15 = Trdv + 0 < Trelease < 45), plus hand tuning - timer_set_oc_value(TIM(ONEWIRE_SLAVE_TIMER), TIM_OC3, 90*(rcc_ahb_frequency/1000000)-1); // time to stop the presence pulse (60 < Tpdl < 120) - timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_UIF | TIM_SR_CC1IF | TIM_SR_CC2IF | TIM_SR_CC3IF); // clear all interrupt flags - timer_update_on_overflow(TIM(ONEWIRE_SLAVE_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout) - timer_enable_irq(TIM(ONEWIRE_SLAVE_TIMER), TIM_DIER_UIE); // enable update interrupt for overflow - nvic_enable_irq(NVIC_TIM_IRQ(ONEWIRE_SLAVE_TIMER)); // catch interrupt in service routine - - onewire_slave_function_code_received = false; // reset state - onewire_slave_state = ONEWIRE_STATE_IDLE; // reset state - onewire_slave_transfer_complete = false; // reset state - - // setup GPIO with external interrupt - rcc_periph_clock_enable(RCC_GPIO(ONEWIRE_SLAVE_PORT)); // enable clock for GPIO peripheral - gpio_set(GPIO(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN)); // idle is high (using pull-up resistor) - gpio_set_mode(GPIO(ONEWIRE_SLAVE_PORT), GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, GPIO(ONEWIRE_SLAVE_PIN)); // control output using open drain (this mode also allows to read the input signal) - rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt - exti_select_source(EXTI(ONEWIRE_SLAVE_PIN), GPIO(ONEWIRE_SLAVE_PORT)); // mask external interrupt of this pin only for this port - exti_set_trigger(EXTI(ONEWIRE_SLAVE_PIN), EXTI_TRIGGER_BOTH); // trigger on signal change - exti_enable_request(EXTI(ONEWIRE_SLAVE_PIN)); // enable external interrupt - nvic_enable_irq(NVIC_EXTI_IRQ(ONEWIRE_SLAVE_PIN)); // enable interrupt -} - -bool onewire_slave_function_read(uint8_t* data, size_t size) -{ - if (NULL==data || 0==size) { // verify input - return false; - } - if (UINT32_MAX/87) { // complete ROM command code received - bits_bit = 0; // reset buffer - rom_code_byte = 0; // reset ROM code byte index - switch (bits_buffer) { // act depending on ROM command code - case 0x33: // READ ROM - bits_buffer = onewire_slave_rom_code[rom_code_byte]; // prepare to send the first byte - onewire_slave_state = ONEWIRE_STATE_ROM_READ; // write ROM code - break; - case 0xcc: // SKIP ROM - onewire_slave_state = ONEWIRE_STATE_FUNCTION_COMMAND; // now read function command code - break; - case 0x55: // MATCH ROM - onewire_slave_state = ONEWIRE_STATE_ROM_MATCH; // read ROM code - break; - case 0xf0: // SEARCH ROM - bits_buffer = onewire_slave_rom_code[rom_code_byte]; // prepare to search code - onewire_slave_state = ONEWIRE_STATE_ROM_SEARCH_TRUE; // prepare to start sending first new bit - break; - default: // unknown ROM code - onewire_slave_state = ONEWIRE_STATE_IDLE; // go back to default idle state - break; - } - } - break; - case ONEWIRE_STATE_ROM_READ: // send ROM code - if (bits_bit>7) { // complete byte transmitted - rom_code_byte++; // go to next ROM code byte - if (rom_code_byte>LENGTH(onewire_slave_rom_code)) { // complete ROM code send - onewire_slave_state = ONEWIRE_STATE_IDLE; // go back to default idle state - } else { - bits_bit = 0; // reset buffer - bits_buffer = onewire_slave_rom_code[rom_code_byte]; // send next ROM code byte - } - } - break; - case ONEWIRE_STATE_ROM_MATCH: // compare ROM code - if (bits_bit>7) { // complete byte received - if (bits_buffer==onewire_slave_rom_code[rom_code_byte]) { // ROM code byte matches - bits_bit = 0; // reset buffer - rom_code_byte++; // go to next ROM code byte - if (rom_code_byte>=LENGTH(onewire_slave_rom_code)) { // complete ROM code matches - onewire_slave_state = ONEWIRE_STATE_FUNCTION_COMMAND; // now read function command code - } - } else { // ROM code does not match - onewire_slave_state = ONEWIRE_STATE_IDLE; // stop comparing and go back to idle - } - } - break; - case ONEWIRE_STATE_ROM_SEARCH_TRUE: // ROM code bit is send, prepare to send negated version - bits_buffer ^= (1<7) { // complete byte searched - bits_bit = 0; // reset buffer - rom_code_byte++; // go to next ROM code byte - if (rom_code_byte>=LENGTH(onewire_slave_rom_code)) { // complete ROM code search - onewire_slave_state = ONEWIRE_STATE_FUNCTION_COMMAND; // now read function command code - } else { - bits_buffer = onewire_slave_rom_code[rom_code_byte]; // prepare next ROM code byte - } - } - break; - case ONEWIRE_STATE_FUNCTION_COMMAND: // read function command - if (bits_bit>7) { // complete function command code received - onewire_slave_function_code = bits_buffer; // save function command code to user buffer - onewire_slave_state = ONEWIRE_STATE_FUNCTION_DATA; // let the user transfer data - onewire_slave_function_code_received = true; // notify user - } - break; - case ONEWIRE_STATE_FUNCTION_READ: // save function data bit - if (0==bits_bit%8) { // complete byte received - onewire_slave_transfer_data[(bits_bit-1)/8] = bits_buffer; // save received bytes - } - if (bits_bit>=onewire_slave_transfer_bits) { // read transfer complete - onewire_slave_transfer_data[(bits_bit-1)/8] = bits_buffer; // save last bits - onewire_slave_state = ONEWIRE_STATE_FUNCTION_DATA; // let the user transfer more data - onewire_slave_transfer_complete = true; // notify user - } - break; - case ONEWIRE_STATE_FUNCTION_WRITE: // update function data bit to write - if (0==bits_bit%8) { // complete byte transfer - bits_buffer = onewire_slave_transfer_data[bits_bit/8]; // prepare next byte to write - } - if (bits_bit>=onewire_slave_transfer_bits) { // write transfer complete - onewire_slave_state = ONEWIRE_STATE_FUNCTION_DATA; // let the user transfer more data - onewire_slave_transfer_complete = true; // notify user - } - break; - default: // no action needed - break; - } - timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC2IF); // clear flag - } - if (timer_interrupt_source(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC3IF)) { // end of presence pulse timer triggered - timer_clear_flag(TIM(ONEWIRE_SLAVE_TIMER), TIM_SR_CC3IF); // clear flag - if (ONEWIRE_STATE_PULSE_PRESENCE==onewire_slave_state) { - gpio_set(GPIO(ONEWIRE_SLAVE_PORT), GPIO(ONEWIRE_SLAVE_PIN)); // stop sending presence pulse - // if the pin stays low the reset timer will catch it - } - } -} diff --git a/lib/onewire_slave.h b/lib/onewire_slave.h deleted file mode 100644 index af05eb4..0000000 --- a/lib/onewire_slave.h +++ /dev/null @@ -1,53 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library for 1-wire protocol as slave (API) - * @file onewire_slave.h - * @author King Kévin - * @date 2017 - * @note peripherals used: timer @ref onewire_slave_timer, GPIO @ref onewire_slave_gpio - * @note overdrive mode is not supported - */ -#pragma once - -/** set when a function command code has been received - * @note needs to be cleared by user - */ -extern volatile bool onewire_slave_function_code_received; -/** last function command code received */ -extern volatile uint8_t onewire_slave_function_code; -/** set when data read/write transfer has been completed - * @note needs to be cleared by user - */ -extern volatile bool onewire_slave_transfer_complete; - -/** setup 1-wire peripheral - * @param[in] family family code for slave ROM code (8 bits) - * @param[in] serial serial number for slave ROM code (48 bits) - */ -void onewire_slave_setup(uint8_t family, uint64_t serial); -/** read data from master - * @param[out] data buffer to save read bits - * @param[in] size number of bytes to read - * @return if transfer initialization succeeded - * @note onewire_slave_transfer_complete is set when transfer is completed - */ -bool onewire_slave_function_read(uint8_t* data, size_t size); -/** write data to master - * @param[in] data data to write - * @param[in] size number of bytes to write - * @return if transfer initialization succeeded - * @note onewire_slave_transfer_complete is set when transfer is completed - */ -bool onewire_slave_function_write(const uint8_t* data, size_t size); diff --git a/lib/radio_esp8266.c b/lib/radio_esp8266.c deleted file mode 100644 index bd66c23..0000000 --- a/lib/radio_esp8266.c +++ /dev/null @@ -1,175 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to send data using ESP8266 WiFi SoC (code) - * @file radio_esp8266.c - * @author King Kévin - * @date 2016 - * @note peripherals used: USART @ref radio_esp8266_usart - */ - -/* standard libraries */ -#include // standard integer types -#include // general utilities -#include // string and memory utilities -#include // string utilities - -/* STM32 (including CM3) libraries */ -#include // real-time control clock library -#include // general purpose input output library -#include // universal synchronous asynchronous receiver transmitter library -#include // interrupt handler -#include // Cortex M3 utilities - -#include "radio_esp8266.h" // radio header and definitions -#include "global.h" // common methods - -/** @defgroup radio_esp8266_usart USART peripheral used for communication with radio - * @{ - */ -#define RADIO_ESP8266_USART 2 /**< USART peripheral */ -/** @} */ - -/* input and output buffers and used memory */ -static uint8_t rx_buffer[24] = {0}; /**< buffer for received data (we only expect AT responses) */ -static volatile uint16_t rx_used = 0; /**< number of byte in receive buffer */ -static uint8_t tx_buffer[256] = {0}; /**< buffer for data to transmit */ -static volatile uint16_t tx_used = 0; /**< number of bytes used in transmit buffer */ - -volatile bool radio_esp8266_activity = false; -volatile bool radio_esp8266_success = false; - -/** transmit data to radio - * @param[in] data data to transmit - * @param[in] length length of data to transmit - */ -static void radio_esp8266_transmit(uint8_t* data, uint8_t length) { - while (tx_used || !usart_get_flag(USART(RADIO_ESP8266_USART), USART_SR_TXE)) { // wait until ongoing transmission completed - usart_enable_tx_interrupt(USART(RADIO_ESP8266_USART)); // enable transmit interrupt - __WFI(); // sleep until something happened - } - usart_disable_tx_interrupt(USART(RADIO_ESP8266_USART)); // ensure transmit interrupt is disable to prevent index corruption (the ISR should already have done it) - radio_esp8266_activity = false; // reset status because of new activity - for (tx_used=0; tx_used0) { - radio_esp8266_transmit((uint8_t*)command, length); - } -} - -void radio_esp8266_send(uint8_t* data, uint8_t length) -{ - char command[16+1] = {0}; // string to create command - int command_length = snprintf(command, LENGTH(command), "AT+CIPSEND=%u\r\n", length); // create AT command to send data - if (command_length>0) { - radio_esp8266_transmit((uint8_t*)command, command_length); // transmit AT command - while (!radio_esp8266_activity || !radio_esp8266_success) { // wait for response - __WFI(); // sleep until something happened - } - if (!radio_esp8266_success) { // send AT command did not succeed - return; // don't transmit data - } - radio_esp8266_transmit(data, length); // transmit data - } -} - -void radio_esp8266_close(void) -{ - radio_esp8266_transmit((uint8_t*)"AT+CIPCLOSE\r\n", 13); // send AT command to close established connection -} - -/** USART interrupt service routine called when data has been transmitted or received */ -void USART_ISR(RADIO_ESP8266_USART)(void) -{ - if (usart_get_interrupt_source(USART(RADIO_ESP8266_USART), USART_SR_TXE)) { // data has been transmitted - if (tx_used) { // there is still data in the buffer to transmit - usart_send(USART(RADIO_ESP8266_USART),tx_buffer[tx_used-1]); // put data in transmit register - tx_used--; // update used size - } else { // no data in the buffer to transmit - usart_disable_tx_interrupt(USART(RADIO_ESP8266_USART)); // disable transmit interrupt - } - } - if (usart_get_interrupt_source(USART(RADIO_ESP8266_USART), USART_SR_RXNE)) { // data has been received - while (rx_used>=LENGTH(rx_buffer)) { // if buffer is full - memmove(rx_buffer,&rx_buffer[1],LENGTH(rx_buffer)-1); // drop old data to make space (ring buffer are more efficient but harder to handle) - rx_used--; // update used buffer information - } - rx_buffer[rx_used++] = usart_recv(USART(RADIO_ESP8266_USART)); // put character in buffer - // if the used send a packet with these strings during the commands detection the AT command response will break (AT commands are hard to handle perfectly) - if (rx_used>=4 && memcmp((char*)&rx_buffer[rx_used-4], "OK\r\n", 4)==0) { // OK received - radio_esp8266_activity = true; // response received - radio_esp8266_success = true; // command succeeded - rx_used = 0; // reset buffer - } else if (rx_used>=7 && memcmp((char*)&rx_buffer[rx_used-7], "ERROR\r\n", 7)==0) { // ERROR received - radio_esp8266_activity = true; // response received - radio_esp8266_success = false; // command failed - rx_used = 0; // reset buffer - } - } -} diff --git a/lib/radio_esp8266.h b/lib/radio_esp8266.h deleted file mode 100644 index 193db70..0000000 --- a/lib/radio_esp8266.h +++ /dev/null @@ -1,47 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to send data using ESP8266 WiFi SoC (API) - * @file radio_esp8266.h - * @author King Kévin - * @date 2016 - * @note peripherals used: USART @ref radio_esp8266_usart - */ -#pragma once - -/** a response has been returned by the radio */ -extern volatile bool radio_esp8266_activity; -/** the last command has succeeded */ -extern volatile bool radio_esp8266_success; - -/** setup peripherals to communicate with radio - * @note this is blocking to ensure we are connected to the WiFi network - */ -void radio_esp8266_setup(void); -/** establish TCP connection - * @param[in] host host to connect to - * @param[in] port TCP port to connect to - * @note wait for activity to get success status - */ -void radio_esp8266_tcp_open(char* host, uint16_t port); -/** send data (requires established connection) - * @param[in] data data to send - * @param[in] length size of data to send - * @note wait for activity to get success status - */ -void radio_esp8266_send(uint8_t* data, uint8_t length); -/** close established connection - * @note wait for activity to get success status - */ -void radio_esp8266_close(void); diff --git a/lib/rtc_dcf77.c b/lib/rtc_dcf77.c deleted file mode 100644 index 741dfb5..0000000 --- a/lib/rtc_dcf77.c +++ /dev/null @@ -1,286 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to get time from a DCF77 module (code) - * @file rtc_dcf77.c - * @author King Kévin - * @date 2016-2017 - * @note peripherals used: GPIO @ref rtc_dcf77_gpio, timer @ref rtc_dcf77_timer - */ - -/* standard libraries */ -#include // standard integer types -#include // general utilities - -/* STM32 (including CM3) libraries */ -#include // Cortex M3 utilities -#include // interrupt handler -#include // real-time control clock library -#include // general purpose input output library -#include // SPI library -#include // timer library - -#include "rtc_dcf77.h" // RTC DCF77 library API -#include "global.h" // common methods - -/** @defgroup rtc_dcf77_gpio output to enable DCF module and input to capture DCF signal - * @{ - */ -#define RTC_DCF77_ENABLE_PORT A /**< GPIO port to enable the module */ -#define RTC_DCF77_ENABLE_PIN 2 /**< GPIO pinto enable the module */ -#define RTC_DCF77_SIGNAL_PORT A /**< GPIO port to capture the DCF signal */ -#define RTC_DCF77_SIGNAL_PIN 3 /**< GPIO pin to capture the DCF signal */ -/** @} */ - -/** @defgroup rtc_dcf77_timer timer to sample DCF77 signal - * @{ - */ -#define RTC_DCF77_TIMER 4 /**< timer peripheral */ -/** @} */ - -volatile bool rtc_dcf77_time_flag = false; -struct rtc_dcf77_time_t rtc_dcf77_time = {false, 0, 0, 0, 0, 0, 0, 0, 0}; - -/**< the received DCF77 frame bits */ -static volatile uint64_t rtc_dcf77_frame = 0; -/** values of the DCF77 signal over 10 ms for 1 s (how many times it is high) */ -static uint8_t rtc_dcf77_bins[100] = {0}; -/** the bin shift for the bit phase */ -static uint8_t rtc_dcf77_phase = 0; -/** the maximum phase value */ -static int16_t rtc_dcf77_phase_max = INT16_MIN; -/** if the current phase has been verified */ -static bool rtc_dcf77_phase_locked = false; -/** number of invalid decoding */ -static uint8_t rtc_dcf77_invalid = 0; -/** maximum number of invalid decoding before switching off */ -#define RTC_DCF77_INVALID_MAX 5 - -void rtc_dcf77_setup(void) -{ - // setup enable output - rcc_periph_clock_enable(RCC_GPIO(RTC_DCF77_ENABLE_PORT)); // enable clock GPIO peripheral - gpio_set_mode(GPIO(RTC_DCF77_ENABLE_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(RTC_DCF77_ENABLE_PIN)); // set pin to output push-pull to be able to enable the module - rtc_dcf77_off(); // disable module at start - - // setup signal input - rcc_periph_clock_enable(RCC_GPIO(RTC_DCF77_SIGNAL_PORT)); // enable clock for signal input peripheral - gpio_set_mode(GPIO(RTC_DCF77_SIGNAL_PORT), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, GPIO(RTC_DCF77_SIGNAL_PIN)); // set signal pin to input - - // setup timer to sample signal at 1kHz - rcc_periph_clock_enable(RCC_TIM(RTC_DCF77_TIMER)); // enable clock for timer peripheral - timer_reset(TIM(RTC_DCF77_TIMER)); // reset timer state - timer_set_mode(TIM(RTC_DCF77_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up - timer_set_prescaler(TIM(RTC_DCF77_TIMER), 1); // set prescaler to divide frequency by two, to be able to have a period of 1 kHz (72MHz/2/2^16=549Hz<1kHz) - timer_set_period(TIM(RTC_DCF77_TIMER), rcc_ahb_frequency/2/1000-1); // set period to 1kHz (plus hand tuning) - timer_clear_flag(TIM(RTC_DCF77_TIMER), TIM_SR_UIF); // clear update event flag - timer_update_on_overflow(TIM(RTC_DCF77_TIMER)); // only use counter overflow as UEV source - timer_enable_irq(TIM(RTC_DCF77_TIMER), TIM_DIER_UIE); // enable update interrupt for timer - nvic_enable_irq(NVIC_TIM_IRQ(RTC_DCF77_TIMER)); // catch interrupt in service routine -} - -void rtc_dcf77_on(void) -{ - if (!gpio_get(GPIO(RTC_DCF77_ENABLE_PORT), GPIO(RTC_DCF77_ENABLE_PIN))) { // receiver is already turned on - return; // do nothing - } - rtc_dcf77_frame = 0; // reset frame - rtc_dcf77_phase_locked = false; // reset phase lock - rtc_dcf77_phase_max = INT16_MIN; // restart searching for phase - rtc_dcf77_invalid = 0; // reset invalid count - gpio_clear(GPIO(RTC_DCF77_ENABLE_PORT), GPIO(RTC_DCF77_ENABLE_PIN)); // enable module by pulling pin low - timer_set_counter(TIM(RTC_DCF77_TIMER), 0); // reset timer counter - timer_enable_counter(TIM(RTC_DCF77_TIMER)); // start timer to sample signal -} - -void rtc_dcf77_off(void) -{ - gpio_set(GPIO(RTC_DCF77_ENABLE_PORT), GPIO(RTC_DCF77_ENABLE_PIN)); // disable module by pull pin high - timer_disable_counter(TIM(RTC_DCF77_TIMER)); // stop timer since we don't need to sample anymore -} - -/** decode rtc_dcf77_frame DCF77 frame into rtc_dcf77_time DCF77 time - * @note check valid for validity - */ -static void rtc_dcf77_decode(void) -{ - rtc_dcf77_time.valid = false; // reset validity - - if (rtc_dcf77_frame==0) { // no time received yet - return; - } - if (!(rtc_dcf77_frame&((uint64_t)1<<20))) { // start of encoded time should always be 1 - return; - } - - // check minute parity - uint8_t parity = 0; // to check parity - for (uint8_t bit=21; bit<=28; bit++) { - if (rtc_dcf77_frame&((uint64_t)1<>21)&(0x1))+2*((rtc_dcf77_frame>>22)&(0x1))+4*((rtc_dcf77_frame>>23)&(0x1))+8*((rtc_dcf77_frame>>24)&(0x1))+10*((rtc_dcf77_frame>>25)&(0x1))+20*((rtc_dcf77_frame>>26)&(0x1))+40*((rtc_dcf77_frame>>27)&(0x1)); // read minute (00-59) - if (rtc_dcf77_time.minutes>59) { // minutes should not be more than 59 - return; - } - - // check hour parity - parity = 0; - for (uint8_t bit=29; bit<=35; bit++) { - if (rtc_dcf77_frame&((uint64_t)1<>29)&(0x1))+2*((rtc_dcf77_frame>>30)&(0x1))+4*((rtc_dcf77_frame>>31)&(0x1))+8*((rtc_dcf77_frame>>32)&(0x1))+10*((rtc_dcf77_frame>>33)&(0x1))+20*((rtc_dcf77_frame>>34)&(0x1)); // read hour (00-23) - if (rtc_dcf77_time.hours>23) { // hours should not be more than 23 - return; - } - - // check date parity - parity = 0; - for (uint8_t bit=36; bit<=58; bit++) { - if (rtc_dcf77_frame&((uint64_t)1<>36)&(0x1))+2*((rtc_dcf77_frame>>37)&(0x1))+4*((rtc_dcf77_frame>>38)&(0x1))+8*((rtc_dcf77_frame>>39)&(0x1))+10*((rtc_dcf77_frame>>40)&(0x1))+20*((rtc_dcf77_frame>>41)&(0x1)); // read day of the month (01-31) - if (rtc_dcf77_time.day==0 || rtc_dcf77_time.day>31) { // day of the month should be 1-31 - return; - } - rtc_dcf77_time.weekday = 1*((rtc_dcf77_frame>>42)&(0x1))+2*((rtc_dcf77_frame>>43)&(0x1))+4*((rtc_dcf77_frame>>44)&(0x1)); // read day of the week (1=Monday - 7=Sunday) - if (rtc_dcf77_time.weekday==0 || rtc_dcf77_time.weekday>7) { // day of the week should be 1-7 - return; - } - rtc_dcf77_time.month = 1*((rtc_dcf77_frame>>45)&(0x1))+2*((rtc_dcf77_frame>>46)&(0x1))+4*((rtc_dcf77_frame>>47)&(0x1))+8*((rtc_dcf77_frame>>48)&(0x1))+10*((rtc_dcf77_frame>>49)&(0x1)); // read month of the year (01-12) - if (rtc_dcf77_time.month==0 || rtc_dcf77_time.month>12) { // month of the year should be 1-12 - return; - } - rtc_dcf77_time.year = 1*((rtc_dcf77_frame>>50)&(0x1))+2*((rtc_dcf77_frame>>51)&(0x1))+4*((rtc_dcf77_frame>>52)&(0x1))+8*((rtc_dcf77_frame>>53)&(0x1))+10*((rtc_dcf77_frame>>54)&(0x1))+20*((rtc_dcf77_frame>>55)&(0x1))+40*((rtc_dcf77_frame>>56)&(0x1))+80*((rtc_dcf77_frame>>57)&(0x1)); // read year of the century (00-99) - if (rtc_dcf77_time.year>99) { // year should be <100 - return; - } - - rtc_dcf77_time.valid = true; // if we managed it until here the decoding is successful -} - -/** find phase of 1 seconds DCF77 signal in the bins - * searches the complete second for the highest correlation if the phase it not locked - * searches only around the last phase if locked - * saves the new phase with the highest correlation - */ -static void rtc_dcf77_phase_detector(void) { - uint8_t integral_i = 0; // which bin has the highest integral/correlation - int16_t integral_max = 0; // maximum integral value found - - for (uint8_t start=0; start<(rtc_dcf77_phase_locked ? 10 : LENGTH(rtc_dcf77_bins)); start++) { // which bin has been used to start the convolution (only use +/- 15 bits of previous phase if locked) - int16_t integral = 0; // value of the integral - for (uint8_t bin=0; binintegral_max) { // we found a better correlation result - integral_max = integral; // save new best result - integral_i = (start+rtc_dcf77_phase+LENGTH(rtc_dcf77_bins)-5)%LENGTH(rtc_dcf77_bins); // start new best phase start - } - } - - if ((int16_t)(integral_max+40)>rtc_dcf77_phase_max) { // only save new phase if it is better than the last one, with some margin to compensate for the drift (perfect correlation = 100, worst correlation = -800) - rtc_dcf77_phase_max = integral_max; // save best phase value - rtc_dcf77_phase = integral_i; // save bin index corresponding to start of the phase - } -} - -/** interrupt service routine called for timer */ -void TIM_ISR(RTC_DCF77_TIMER)(void) -{ - static uint8_t bin_state = 0; // how many samples have been stored in the bin - static uint8_t bin_i = 0; // current bin filled - static uint8_t bit_i = 0; // current bit in the DCF77 minute frame - if (timer_get_flag(TIM(RTC_DCF77_TIMER), TIM_SR_UIF)) { // overflow update event happened - timer_clear_flag(TIM(RTC_DCF77_TIMER), TIM_SR_UIF); // clear flag - // fill bin with current sample state - if (gpio_get(GPIO(RTC_DCF77_SIGNAL_PORT), GPIO(RTC_DCF77_SIGNAL_PIN))) { - rtc_dcf77_bins[bin_i]++; // only need to increase if the signal is high - } - bin_state++; // remember we filled the bin - - if (bin_state>=10) { // bin has 10x1 ms samples, it is now full - bin_i = (bin_i+1)%LENGTH(rtc_dcf77_bins); // go to next bin - rtc_dcf77_bins[bin_i] = 0; // restart bin - bin_state = 0; // restart collecting - } - - if (0==bin_i && 0==bin_state) { // we have 1 s of samples - if (bit_i<59) { - rtc_dcf77_phase_detector(); // detect phase in signal - // check modulation of first 100 ms - uint16_t modulation = 0; - for (uint8_t bin=0; bin<10; bin++) { - modulation += rtc_dcf77_bins[(rtc_dcf77_phase+bin)%LENGTH(rtc_dcf77_bins)]; - } - if (modulation<50) { // signal is not modulated, it might be the 60th pause bit - bit_i = 0; // restart frame - rtc_dcf77_frame = 0; // restart frame - rtc_dcf77_phase_max = INT16_MIN; // restart searching for phase - rtc_dcf77_phase_locked = false; // unlock phase since the decoding seems wrong - } else { // modulation detected - // check modulation of next 100 ms - modulation = 0; - for (uint8_t bin=10; bin<20; bin++) { - modulation += rtc_dcf77_bins[(rtc_dcf77_phase+bin)%LENGTH(rtc_dcf77_bins)]; - } - if (modulation<50) { // it's a 0 - // bit is already cleared - } else { // it's a 1 - rtc_dcf77_frame |= (1ULL<=RTC_DCF77_INVALID_MAX) { // too many invalid decoding - rtc_dcf77_off(); // switch off receiver so it can re-tune - } - bit_i = 0; // restart frame - rtc_dcf77_frame = 0; // restart frame - } - } - } else { // no other interrupt should occur - while (true); // unhandled exception: wait for the watchdog to bite - } -} diff --git a/lib/rtc_dcf77.h b/lib/rtc_dcf77.h deleted file mode 100644 index 7c4a3da..0000000 --- a/lib/rtc_dcf77.h +++ /dev/null @@ -1,46 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to get time from a DCF77 module (API) - * @file rtc_dcf77.h - * @author King Kévin - * @date 2016-2017 - * @note peripherals used: GPIO @ref rtc_dcf77_gpio, timer @ref rtc_dcf77_timer - */ -#pragma once - -/** set when time information has been received */ -extern volatile bool rtc_dcf77_time_flag; - -/** decoded DCF77 time received */ -extern struct rtc_dcf77_time_t { - bool valid; /**< if the time is valid */ - uint8_t milliseconds; /**< milliseconds (00-99) */ - uint8_t seconds; /**< seconds (always 0) */ - uint8_t minutes; /**< minutes (00-49) */ - uint8_t hours; /**< hours (00-23) */ - uint8_t day; /**< day of the month (01-31) */ - uint8_t weekday; /**< day of the week (1-7=Monday-Sunday) */ - uint8_t month; /**< month (01-12) */ - uint8_t year; /**< year within century (00-99) */ -} rtc_dcf77_time; - -/** setup DCF77 time receiver module */ -void rtc_dcf77_setup(void); -/** switch on DCF77 time receiver module - * @note it switches back off after too many invalid decoding attempts - */ -void rtc_dcf77_on(void); -/** switch off DCF77 time receiver module */ -void rtc_dcf77_off(void); diff --git a/lib/rtc_ds1307.c b/lib/rtc_ds1307.c deleted file mode 100644 index 5575281..0000000 --- a/lib/rtc_ds1307.c +++ /dev/null @@ -1,397 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to communicate with the Maxim DS1307 I2C RTC IC (code) - * @file rtc_ds1307.c - * @author King Kévin - * @date 2016-2017 - * @note peripherals used: I2C @ref i2c_master_i2c - */ - -/* standard libraries */ -#include // standard integer types -#include // standard I/O facilities -#include // general utilities - -/* STM32 (including CM3) libraries */ -#include // real-time control clock library -#include // general purpose input output library -#include // I2C library - -#include "global.h" // global utilities -#include "rtc_ds1307.h" // RTC header and definitions -#include "i2c_master.h" // i2c utilities - -#define RTC_DS1307_I2C_ADDR 0x68 /**< DS1307 I2C address (fixed to 0b1101000) */ - -void rtc_ds1307_setup(void) -{ - // configure I2C peripheral - i2c_master_setup(false); // DS1307 only supports normal mode (up to 100 kHz) -} - -bool rtc_ds1307_oscillator_disabled(void) -{ - uint8_t data[1] = {0}; // to read data over I2C - const uint8_t address[] = {0x00}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing CH value - return false; - } - return data[0]&0x80; // return CH bit value to indicate if oscillator is disabled -} - -uint16_t rtc_ds1307_read_square_wave(void) -{ - uint16_t to_return = 0; // square wave frequency to return (in Hz) - uint8_t data[1] = {0}; // to read data over I2C - const uint16_t rtc_ds1307_rs[] = {1, 4096, 8192, 32768}; // RS1/RS0 values - const uint8_t address[] = {0x07}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing control register - return 0xffff; // error occurred - } - if (data[0]&0x10) { // verify if the square wave is enabled (SQWE) - to_return = rtc_ds1307_rs[data[0]&0x03]; // read RS1/RS0 and get value - } else { - to_return = 0; // square wave output is disabled - } - return to_return; -} - -uint8_t rtc_ds1307_read_seconds(void) -{ - uint8_t to_return = 0; // seconds to return - uint8_t data[1] = {0}; // to read data over I2C - const uint8_t address[] = {0x00}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value - return 0xff; - } - to_return = ((data[0]&0x70)>>4)*10+(data[0]&0x0f); // convert BCD coding into seconds - return to_return; -} - -uint8_t rtc_ds1307_read_minutes(void) -{ - uint8_t to_return = 0; // minutes to return - uint8_t data[1] = {0}; // to read data over I2C - const uint8_t address[] = {0x01}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value - return 0xff; - } - to_return = (data[0]>>4)*10+(data[0]&0x0f); // convert BCD coding into minutes - return to_return; -} - -uint8_t rtc_ds1307_read_hours(void) -{ - uint8_t to_return = 0; // hours to return - uint8_t data[1] = {0}; // to read data over I2C - const uint8_t address[] = {0x02}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value - return 0xff; - } - if (data[0]&0x40) { // 12 hour mode - if (data[0]&0x02) { // PM - to_return += 12; // add the 12 hours - } - to_return += ((data[0]&0x10)>>4)*10; // convert BCD coding into hours (first digit) - } else { - to_return = ((data[0]&0x30)>>4)*10; // convert BCD coding into hours (first digit) - } - to_return += (data[0]&0x0f); // convert BCD coding into hours (second digit) - return to_return; -} - -uint8_t rtc_ds1307_read_day(void) -{ - uint8_t to_return = 0; // day to return - uint8_t data[1] = {0}; // to read data over I2C - const uint8_t address[] = {0x03}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value - return 0xff; - } - to_return = (data[0]&0x07); // convert BCD coding into days - return to_return; -} - -uint8_t rtc_ds1307_read_date(void) -{ - uint8_t to_return = 0; // date to return - uint8_t data[1] = {0}; // to read data over I2C - const uint8_t address[] = {0x04}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value - return 0xff; - } - to_return = ((data[0]&0x30)>>4)*10+(data[0]&0x0f); // convert BCD coding into date - return to_return; -} - -uint8_t rtc_ds1307_read_month(void) -{ - uint8_t to_return = 0; // month to return - uint8_t data[1] = {0}; // to read data over I2C - const uint8_t address[] = {0x05}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value - return 0xff; - } - to_return = ((data[0]&0x10)>>4)*10+(data[0]&0x0f); // convert BCD coding into month - return to_return; -} - -uint8_t rtc_ds1307_read_year(void) -{ - uint8_t data[1] = {0}; // to read data over I2C - const uint8_t address[] = {0x06}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read a single byte containing value - return 0xff; - } - uint8_t to_return = ((data[0]&0xf0)>>4)*10+(data[0]&0x0f); // convert BCD coding into year - return to_return; -} - -uint8_t* rtc_ds1307_read_time(void) -{ - static uint8_t time[7] = {0}; // store time {seconds, minutes, hours, day, date, month, year} - uint8_t data[7] = {0}; // to read data over I2C - const uint8_t address[] = {0x00}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read all time bytes - return NULL; // error occurred - } - time[0] = ((data[0]&0x70)>>4)*10+(data[0]&0x0f); // convert seconds from BCD - time[1] = (data[1]>>4)*10+(data[1]&0x0f); // convert minutes from BCD - time[2] = 0; // re-initialize hours - if (data[2]&0x40) { // 12 hour mode - if (data[2]&0x02) { // PM - time[2] += 12; // add the 12 hours - } - time[2] += ((data[2]&0x10)>>4)*10; // convert BCD coding into hours (first digit) - } else { - time[2] = ((data[2]&0x30)>>4)*10; // convert BCD coding into hours (first digit) - } - time[2] += (data[2]&0x0f); // convert BCD coding into hours (second digit) - time[3] = (data[3]&0x07); // convert BCD coding into days - time[4] = ((data[4]&0x30)>>4)*10+(data[4]&0x0f); // convert BCD coding into date - time[5] = ((data[5]&0x10)>>4)*10+(data[5]&0x0f); // convert BCD coding into month - time[6] = ((data[6]&0xf0)>>4)*10+(data[6]&0x0f); // convert BCD coding into year - return time; -} - -bool rtc_ds1307_read_ram(uint8_t* data, uint8_t start, uint8_t length) -{ - // sanity checks - if (data==NULL || length==0) { // nothing to read - return false; - } - if (start>55 || start+length>56) { // out of bounds RAM - return false; - } - - const uint8_t address[] = {0x08+start}; // memory address for data - return i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // read RAM (starting at 0x08) -} - -bool rtc_ds1307_oscillator_disable(void) -{ - uint8_t data[1] = {0}; // to write CH value data over I2C - const uint8_t address[] = {0x00}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read seconds with CH value - return false; - } - data[0] |= 0x80; // set CH to disable oscillator - return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write current seconds with CH value -} - -bool rtc_ds1307_oscillator_enable(void) -{ - uint8_t data[1] = {0}; // to write CH value data over I2C - const uint8_t address[] = {0x00}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read seconds with CH value - return false; - } - data[0] &= 0x7f; // clear CH to enable oscillator - return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write current seconds with CH value -} - -bool rtc_ds1307_write_square_wave(uint16_t frequency) -{ - uint8_t data[1] = {0}; // to write control register value data over I2C - switch (frequency) { // set RS1/RS0 based on frequency - case 0: - data[0] = 0; - break; - case 1: - data[0] = 0|(1<<4); - break; - case 4096: - data[0] = 1|(1<<4); - break; - case 8192: - data[0] = 2|(1<<4); - break; - case 32768: - data[0] = 3|(1<<4); - break; - default: // unspecified frequency - return false; - } - const uint8_t address[] = {0x07}; // memory address for data - return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write current seconds with CH value -} - -bool rtc_ds1307_write_seconds(uint8_t seconds) -{ - if (seconds>59) { - return false; - } - uint8_t data[1] = {0}; // to read CH value data and write seconds value over I2C - const uint8_t address[] = {0x00}; // memory address for data - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data))) { // read seconds with CH value - return false; - } - data[0] &= 0x80; // only keep CH flag - data[0] |= (((seconds/10)%6)<<4)+(seconds%10); // encode seconds in BCD format - - return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write current seconds with previous CH value -} - -bool rtc_ds1307_write_minutes(uint8_t minutes) -{ - if (minutes>59) { - return false; - } - uint8_t data[1] = {0}; // to write time value - data[0] = (((minutes/10)%6)<<4)+(minutes%10); // encode minutes in BCD format - - const uint8_t address[] = {0x01}; // memory address for data - return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC -} - -bool rtc_ds1307_write_hours(uint8_t hours) -{ - if (hours>24) { - return false; - } - uint8_t data[1] = {0}; // to write time value - data[0] = (((hours/10)%3)<<4)+(hours%10); // encode hours in BCD 24h format - - const uint8_t address[] = {0x02}; // memory address for data - return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC -} - -bool rtc_ds1307_write_day(uint8_t day) -{ - if (day<1 || day>7) { - return false; - } - uint8_t data[1] = {0}; // to write time value - data[0] = (day%8); // encode day in BCD format - - const uint8_t address[] = {0x03}; // memory address for data - return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC -} - -bool rtc_ds1307_write_date(uint8_t date) -{ - if (date<1 || date>31) { - return false; - } - uint8_t data[1] = {0}; // to write time value - data[0] = (((date/10)%4)<<4)+(date%10); // encode date in BCD format - - const uint8_t address[] = {0x04}; // memory address for data - return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC -} - -bool rtc_ds1307_write_month(uint8_t month) -{ - if (month<1 || month>12) { - return false; - } - uint8_t data[1] = {0}; // to write time value - data[0] = (((month/10)%2)<<4)+(month%10); // encode month in BCD format - - const uint8_t address[] = {0x05}; // memory address for data - return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC -} - -bool rtc_ds1307_write_year(uint8_t year) -{ - if (year>99) { - return false; - } - uint8_t data[1] = {0}; // to write time value - data[0] = (((year/10)%10)<<4)+(year%10); // encode year in BCD format - - const uint8_t address[] = {0x06}; // memory address for data - return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC -} - -bool rtc_ds1307_write_time(uint8_t seconds, uint8_t minutes, uint8_t hours, uint8_t day, uint8_t date, uint8_t month, uint8_t year) -{ - uint8_t data[7] = {0}; // to write all time values - const uint8_t address[] = {0x00}; // memory address for data - // seconds - if (seconds>59) { - return false; - } - if (!i2c_master_read(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, 1)) { // read seconds with CH value - return false; - } - data[0] &= 0x80; // only keep CH flag - data[0] |= (((seconds/10)%6)<<4)+(seconds%10); // encode seconds in BCD format - // minutes - if (minutes>59) { - return false; - } - data[1] = (((minutes/10)%6)<<4)+(minutes%10); // encode minutes in BCD format - // hours - if (hours>24) { - return false; - } - data[2] = (((hours/10)%3)<<4)+(hours%10); // encode hours in BCD 24h format - // day - if (day<1 || day>7) { - return false; - } - data[3] = (day%8); // encode day in BCD format - // date - if (date<1 || date>31) { - return false; - } - data[4] = (((date/10)%4)<<4)+(date%10); // encode date in BCD format - // month - if (month<1 || month>12) { - return false; - } - data[5] = (((month/10)%2)<<4)+(month%10); // encode month in BCD format - // year - if (year>99) { - return false; - } - data[6] = (((year/10)%10)<<4)+(year%10); // encode year in BCD format - - return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write time value on RTC -} - -bool rtc_ds1307_write_ram(uint8_t* data, uint8_t start, uint8_t length) -{ - // sanity checks - if (data==NULL || length==0) { // nothing to read - return false; - } - if (start>55 || start+length>56) { // out of bounds RAM - return false; - } - const uint8_t address[] = {0x08+start}; // memory address for data - return i2c_master_write(RTC_DS1307_I2C_ADDR, address, LENGTH(address), data, LENGTH(data)); // write RAM (starting at 0x08) -} diff --git a/lib/rtc_ds1307.h b/lib/rtc_ds1307.h deleted file mode 100644 index 112a13f..0000000 --- a/lib/rtc_ds1307.h +++ /dev/null @@ -1,140 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to communicate with the Maxim DS1307 I2C RTC IC (API) - * @file rtc_ds1307.h - * @author King Kévin - * @date 2016-2017 - * @note peripherals used: I2C @ref i2c_master_i2c - */ -#pragma once - -/** setup communication with RTC IC - * configure the I2C port defined in the sources - */ -void rtc_ds1307_setup(void); -/** verify if oscillator is disabled - * @return if oscillator is disabled (or if communication error occurred) - */ -bool rtc_ds1307_oscillator_disabled(void); -/** read square wave output frequency (in Hz) - * @return square wave output frequency in Hz, 0 if disabled (0xffff if communication error occurred) - */ -uint16_t rtc_ds1307_read_square_wave(void); -/** read seconds from RTC IC - * @return number of seconds (0-59) of the current time (0xff if communication error occurred) - */ -uint8_t rtc_ds1307_read_seconds(void); -/** read minutes from RTC IC - * @return number of minutes (0-59) of the current time (0xff if communication error occurred) - */ -uint8_t rtc_ds1307_read_minutes(void); -/** read hours from RTC IC - * @return number of hours (0-23) of the current time (0xff if communication error occurred) - */ -uint8_t rtc_ds1307_read_hours(void); -/** read day from RTC IC - * @return day of the week (1-7, 1 is Sunday) of the current time, 1 being Sunday (0xff if communication error occurred) - */ -uint8_t rtc_ds1307_read_day(void); -/** read date from RTC IC - * @return day of the month (1-31) of the current time (0xff if communication error occurred) - */ -uint8_t rtc_ds1307_read_date(void); -/** read month from RTC IC - * @return month of the year (1-12) of the current time (0xff if communication error occurred) - */ -uint8_t rtc_ds1307_read_month(void); -/** read year from RTC IC - * @return year of the century (00-99) of the current time (0xff if communication error occurred) - */ -uint8_t rtc_ds1307_read_year(void); -/** read time from RTC IC - * @return array of {seconds, minutes, hours, day, date, month, year} as defined above (NULL if communication error occurred) - */ -uint8_t* rtc_ds1307_read_time(void); -/** read user RAM from RTC IC - * @param[out] data array to store the RAM read - * @param[in] start start of the user RAM to read (0-55) - * @param[in] length number of user RAM bytes to read (0-55) - * @return if read succeeded - */ -bool rtc_ds1307_read_ram(uint8_t* data, uint8_t start, uint8_t length); -/** disable RTC IC oscillator - * @return if disabling oscillator succeeded - */ -bool rtc_ds1307_oscillator_disable(void); -/** enable RTC IC oscillator - * @return if enabling oscillator succeeded - */ -bool rtc_ds1307_oscillator_enable(void); -/** write square wave output frequency (in Hz) - * @param[in] frequency square wave output frequency in Hz (0 to disable, 1, 4096, 8192, 32768) - * @return if write succeeded - */ -bool rtc_ds1307_write_square_wave(uint16_t frequency); -/** write seconds into RTC IC - * @param[in] seconds number of seconds (0-59) - * @return if write succeeded - */ -bool rtc_ds1307_write_seconds(uint8_t seconds); -/** write minutes into RTC IC - * @param[in] minutes number of minutes (0-59) - * @return if write succeeded - */ -bool rtc_ds1307_write_minutes(uint8_t minutes); -/** write hours into RTC IC - * @param[in] hours number of hours (0-23) - * @return if write succeeded - */ -bool rtc_ds1307_write_hours(uint8_t hours); -/** write day into RTC IC - * @param[in] day day of the week (1-7, 1 is Sunday) - * @return if write succeeded - */ -bool rtc_ds1307_write_day(uint8_t day); -/** write date into RTC IC - * @param[in] date day of the month (1-31) - * @return if write succeeded - */ -bool rtc_ds1307_write_date(uint8_t date); -/** write month into RTC IC - * @param[in] month month of the year (1-12) - * @return if write succeeded - */ -bool rtc_ds1307_write_month(uint8_t month); -/** write year into RTC IC - * @param[in] year year of the century (00-99) - * @return if write succeeded - */ -bool rtc_ds1307_write_year(uint8_t year); -/** write time into RTC IC - * @param[in] seconds number of seconds (0-59) - * @param[in] minutes number of minutes (0-59) - * @param[in] hours number of hours (0-23) - * @param[in] day day of the week (1-7, 1 is Sunday) - * @param[in] date day of the month (1-31) - * @param[in] month month of the year (1-12) - * @param[in] year year of the century (00-99) - * @return if write succeeded - */ -bool rtc_ds1307_write_time(uint8_t seconds, uint8_t minutes, uint8_t hours, uint8_t day, uint8_t date, uint8_t month, uint8_t year); -/** write user RAM from RTC IC - * @param[in] data array of byte to write in RAM - * @param[in] start start of the user RAM to write (0-55) - * @param[in] length number of user RAM bytes to write (0-55) - * @return if write succeeded - */ -bool rtc_ds1307_write_ram(uint8_t* data, uint8_t start, uint8_t length); - diff --git a/lib/sensor_dht11.c b/lib/sensor_dht11.c deleted file mode 100644 index 3433724..0000000 --- a/lib/sensor_dht11.c +++ /dev/null @@ -1,190 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to query measurements from Aosong DHT11 temperature and relative humidity sensor (code) - * @file sensor_dht11.c - * @author King Kévin - * @date 2017 - * @note peripherals used: timer channel @ref sensor_dht11_timer - */ - -/* standard libraries */ -#include // standard integer types - -/* STM32 (including CM3) libraries */ -#include // Cortex M3 utilities -#include // interrupt handler -#include // real-time control clock library -#include // general purpose input output library -#include // timer utilities - -/* own libraries */ -#include "sensor_dht11.h" // PZEM electricity meter header and definitions -#include "global.h" // common methods - -/** @defgroup sensor_dht11_timer timer peripheral used to measure signal timing for bit decoding - * @{ - */ -#define SENSOR_DHT11_TIMER 3 /**< timer peripheral */ -#define SENSOR_DHT11_CHANNEL 1 /**< channel used as input capture */ -#define SENSOR_DHT11_JITTER 0.1 /**< signal timing jitter tolerated in timing */ -/** @} */ - -volatile bool sensor_dht11_measurement_received = false; - -/** communication states */ -volatile enum sensor_dht11_state_t { - SENSOR_DHT11_OFF, // no request has started - SENSOR_DHT11_HOST_START, // host starts request (and waits >18ms) - SENSOR_DHT11_HOST_STARTED, // host started request and waits for slave answer - SENSOR_DHT11_SLAVE_START, // slave responds to request and puts signal low for 80 us and high for 80 us - SENSOR_DHT11_SLAVE_BIT, // slave is sending bit by putting signal low for 50 us and high (26-28 us = 0, 70 us = 1) - SENSOR_DHT11_MAX -} sensor_dht11_state = SENSOR_DHT11_OFF; /**< current communication state */ - -/** the bit number being sent (MSb first), up to 40 */ -volatile uint8_t sensor_dht11_bit = 0; - -/** the 40 bits (5 bytes) being sent by the device */ -volatile uint8_t sensor_dht11_bits[5] = {0}; - -/** reset all states */ -static void sensor_dht11_reset(void) -{ - // reset states - sensor_dht11_state = SENSOR_DHT11_OFF; - sensor_dht11_bit = 0; - sensor_dht11_measurement_received = false; - gpio_set(TIM_CH_PORT(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL), TIM_CH_PIN(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL)); // idle is high (using pull-up resistor), pull-up before setting as output else the signal will be low for short - gpio_set_mode(TIM_CH_PORT(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, TIM_CH_PIN(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL)); // setup GPIO pin as output (host starts communication before slave replies) - timer_ic_disable(TIM(SENSOR_DHT11_TIMER), TIM_IC(SENSOR_DHT11_CHANNEL)); // enable capture interrupt only when receiving data - timer_disable_counter(TIM(SENSOR_DHT11_TIMER)); // disable timer -} - -void sensor_dht11_setup(void) -{ - // setup timer to measure signal timing for bit decoding (use timer channel as input capture) - rcc_periph_clock_enable(RCC_TIM_CH(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL)); // enable clock for GPIO peripheral - rcc_periph_clock_enable(RCC_TIM(SENSOR_DHT11_TIMER)); // enable clock for timer peripheral - timer_reset(TIM(SENSOR_DHT11_TIMER)); // reset timer state - timer_set_mode(TIM(SENSOR_DHT11_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock,edge alignment (simple count), and count up - timer_set_prescaler(TIM(SENSOR_DHT11_TIMER), 20-1); // set the prescaler so this 16 bits timer allows to wait for 18 ms for the start signal ( 1/(72E6/20/(2**16))=18.20ms ) - timer_ic_set_input(TIM(SENSOR_DHT11_TIMER), TIM_IC(SENSOR_DHT11_CHANNEL), TIM_IC_IN_TI(SENSOR_DHT11_CHANNEL)); // configure ICx to use TIn - timer_ic_set_filter(TIM(SENSOR_DHT11_TIMER), TIM_IC(SENSOR_DHT11_CHANNEL), TIM_IC_OFF); // use no filter input (precise timing needed) - timer_ic_set_polarity(TIM(SENSOR_DHT11_TIMER), TIM_IC(SENSOR_DHT11_CHANNEL), TIM_IC_FALLING); // capture on rising edge - timer_ic_set_prescaler(TIM(SENSOR_DHT11_TIMER), TIM_IC(SENSOR_DHT11_CHANNEL), TIM_IC_PSC_OFF); // don't use any prescaler since we want to capture every pulse - - timer_clear_flag(TIM(SENSOR_DHT11_TIMER), TIM_SR_UIF); // clear flag - timer_update_on_overflow(TIM(SENSOR_DHT11_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout) - timer_enable_irq(TIM(SENSOR_DHT11_TIMER), TIM_DIER_UIE); // enable update interrupt for timer - - timer_clear_flag(TIM(SENSOR_DHT11_TIMER), TIM_SR_CCIF(SENSOR_DHT11_CHANNEL)); // clear input compare flag - timer_enable_irq(TIM(SENSOR_DHT11_TIMER), TIM_DIER_CCIE(SENSOR_DHT11_CHANNEL)); // enable capture interrupt - - nvic_enable_irq(NVIC_TIM_IRQ(SENSOR_DHT11_TIMER)); // catch interrupt in service routine - - sensor_dht11_reset(); // reset state -} - -bool sensor_dht11_measurement_request(void) -{ - if (sensor_dht11_state!=SENSOR_DHT11_OFF) { // not the right state to start (wait up until timeout to reset state) - return false; - } - if (gpio_get(TIM_CH_PORT(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL), TIM_CH_PIN(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL))==0) { // signal should be high per default - return false; - } - if (TIM_CR1(TIM(SENSOR_DHT11_TIMER))&(TIM_CR1_CEN)) { // timer should be off - return false; - } - sensor_dht11_reset(); // reset states - - // send start signal (pull low for > 18 ms) - gpio_clear(TIM_CH_PORT(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL), TIM_CH_PIN(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL)); // set signal to low - timer_set_counter(TIM(SENSOR_DHT11_TIMER), 0); // reset timer counter - timer_enable_counter(TIM(SENSOR_DHT11_TIMER)); // enable timer to wait for 18 ms until overflow - sensor_dht11_state = SENSOR_DHT11_HOST_START; // remember we started sending signal - - return true; -} - -struct sensor_dht11_measurement_t sensor_dht11_measurement_decode(void) -{ - struct sensor_dht11_measurement_t measurement = { 0xff, 0xff }; // measurement to return - if (sensor_dht11_bit<40) { // not enough bits received - return measurement; - } - if ((uint8_t)(sensor_dht11_bits[0]+sensor_dht11_bits[1]+sensor_dht11_bits[2]+sensor_dht11_bits[3])!=sensor_dht11_bits[4]) { // error in checksum (not really parity bit, as mentioned in the datasheet) - return measurement; - } - // calculate measured values (byte 1 and 3 should be the factional value but they are always 0) - measurement.humidity = sensor_dht11_bits[0]; - measurement.temperature = sensor_dht11_bits[2]; - - return measurement; -} - -/** interrupt service routine called for timer */ -void TIM_ISR(SENSOR_DHT11_TIMER)(void) -{ - if (timer_get_flag(TIM(SENSOR_DHT11_TIMER), TIM_SR_UIF)) { // overflow update event happened - timer_clear_flag(TIM(SENSOR_DHT11_TIMER), TIM_SR_UIF); // clear flag - if (sensor_dht11_state==SENSOR_DHT11_HOST_START) { // start signal sent - gpio_set_mode(TIM_CH_PORT(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, TIM_CH_PIN(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL)); // switch pin to input (the external pull up with also set the signal high) - sensor_dht11_state = SENSOR_DHT11_HOST_STARTED; // switch to next state - timer_ic_enable(TIM(SENSOR_DHT11_TIMER), TIM_IC(SENSOR_DHT11_CHANNEL)); // enable capture interrupt only when receiving data - } else { // timeout occurred - sensor_dht11_reset(); // reset states - } - } else if (timer_get_flag(TIM(SENSOR_DHT11_TIMER), TIM_SR_CCIF(SENSOR_DHT11_CHANNEL))) { // edge detected on input capture - uint16_t time = TIM_CCR(SENSOR_DHT11_TIMER,SENSOR_DHT11_CHANNEL); // save captured bit timing (this clear also the flag) - timer_set_counter(TIM(SENSOR_DHT11_TIMER), 0); // reset timer counter - time = (time*1E6)/(rcc_ahb_frequency/(TIM_PSC(TIM(SENSOR_DHT11_TIMER))+1)); // calculate time in us - switch (sensor_dht11_state) { - case (SENSOR_DHT11_HOST_STARTED): // the host query data and the slave is responding - sensor_dht11_state = SENSOR_DHT11_SLAVE_START; // set new state - break; - case (SENSOR_DHT11_SLAVE_START): // the slave sent the start signal - if (time >= ((80+80)*(1-SENSOR_DHT11_JITTER)) && time <= ((80+80)*(1+SENSOR_DHT11_JITTER))) { // response time should be 80 us low and 80 us high - sensor_dht11_state = SENSOR_DHT11_SLAVE_BIT; // set new state - } else { - goto error; - } - break; - case (SENSOR_DHT11_SLAVE_BIT): // the slave sent a bit - if (sensor_dht11_bit>=40) { // no bits should be received after 40 bits - goto error; - } - if (time >= ((50+26)*(1-SENSOR_DHT11_JITTER)) && time <= ((50+28)*(1+SENSOR_DHT11_JITTER))) { // bit 0 time should be 50 us low and 26-28 us high - sensor_dht11_bits[sensor_dht11_bit/8] &= ~(1<<(7-(sensor_dht11_bit%8))); // clear bit - } else if (time >= ((50+70)*(1-SENSOR_DHT11_JITTER)) && time <= ((50+70)*(1+SENSOR_DHT11_JITTER))) { // bit 1 time should be 50 us low and 70 us high - sensor_dht11_bits[sensor_dht11_bit/8] |= (1<<(7-(sensor_dht11_bit%8))); // set bit - } else { - goto error; - } - sensor_dht11_bit++; - if (sensor_dht11_bit>=40) { // all bits received - sensor_dht11_reset(); // reset states - sensor_dht11_bit = 40; // signal decoder all bits have been received - sensor_dht11_measurement_received = true; // signal user all bits have been received - } - break; - default: // unexpected state -error: - sensor_dht11_reset(); // reset states - } - } else { // no other interrupt should occur - while (true); // unhandled exception: wait for the watchdog to bite - } -} diff --git a/lib/sensor_dht11.h b/lib/sensor_dht11.h deleted file mode 100644 index fae7397..0000000 --- a/lib/sensor_dht11.h +++ /dev/null @@ -1,41 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to query measurements from Aosong DHT11 temperature and relative humidity sensor (API) - * @file sensor_dht11.h - * @author King Kévin - * @date 2017 - * @note peripherals used: timer channel @ref sensor_dht11_timer (add external pull-up resistor) - */ -#pragma once - -/** a measurement response has been received */ -extern volatile bool sensor_dht11_measurement_received; - -/** measurement returned by sensor */ -struct sensor_dht11_measurement_t { - uint8_t humidity; /**< relative humidity in %RH (20-95) */ - uint8_t temperature; /**< temperature in °C (0-50) */ -}; - -/** setup peripherals to communicate with sensor */ -void sensor_dht11_setup(void); -/** request measurement from sensor - * @return request started successfully - */ -bool sensor_dht11_measurement_request(void); -/** decode received measurement - * @return decoded measurement (0xff,0xff if invalid) - */ -struct sensor_dht11_measurement_t sensor_dht11_measurement_decode(void); diff --git a/lib/sensor_dht22.c b/lib/sensor_dht22.c deleted file mode 100644 index 8c97e31..0000000 --- a/lib/sensor_dht22.c +++ /dev/null @@ -1,194 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to query measurements from Aosong DHT22 temperature and relative humidity sensor (code) - * @file sensor_dht22.c - * @author King Kévin - * @date 2017 - * @note peripherals used: GPIO and timer @ref sensor_dht22_timer - * @note the DHT22 protocol is very similar but nit completely compatible with the DHT22 protocol: only 1 ms initial host pull low is required (vs. 18 ms), the data is encoded as int16_t (vs. uint8_t), and the signal has more jitter - */ - -/* standard libraries */ -#include // standard integer types -#include // maths utilities - -/* STM32 (including CM3) libraries */ -#include // Cortex M3 utilities -#include // interrupt handler -#include // real-time control clock library -#include // general purpose input output library -#include // timer utilities - -/* own libraries */ -#include "sensor_dht22.h" // PZEM electricity meter header and definitions -#include "global.h" // common methods - -/** @defgroup sensor_dht22_timer timer peripheral used to measure signal timing for bit decoding - * @{ - */ -#define SENSOR_DHT22_TIMER 4 /**< timer peripheral */ -#define SENSOR_DHT22_CHANNEL 3 /**< channel used as input capture */ -#define SENSOR_DHT22_JITTER 0.2 /**< signal timing jitter tolerated in timing */ -/** @} */ - -volatile bool sensor_dht22_measurement_received = false; - -/** communication states */ -volatile enum sensor_dht22_state_t { - SENSOR_DHT22_OFF, // no request has started - SENSOR_DHT22_HOST_START, // host starts request (and waits >18ms) - SENSOR_DHT22_HOST_STARTED, // host started request and waits for slave answer - SENSOR_DHT22_SLAVE_START, // slave responds to request and puts signal low for 80 us and high for 80 us - SENSOR_DHT22_SLAVE_BIT, // slave is sending bit by putting signal low for 50 us and high (26-28 us = 0, 70 us = 1) - SENSOR_DHT22_MAX -} sensor_dht22_state = SENSOR_DHT22_OFF; /**< current communication state */ - -/** the bit number being sent (MSb first), up to 40 */ -volatile uint8_t sensor_dht22_bit = 0; - -/** the 40 bits (5 bytes) being sent by the device */ -volatile uint8_t sensor_dht22_bits[5] = {0}; - -/** reset all states */ -static void sensor_dht22_reset(void) -{ - // reset states - sensor_dht22_state = SENSOR_DHT22_OFF; - sensor_dht22_bit = 0; - sensor_dht22_measurement_received = false; - - gpio_set(TIM_CH_PORT(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL), TIM_CH_PIN(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL)); // idle is high (using pull-up resistor), pull-up before setting as output else the signal will be low for short - gpio_set_mode(TIM_CH_PORT(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, TIM_CH_PIN(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL)); // setup GPIO pin as output (host starts communication before slave replies) - - timer_ic_disable(TIM(SENSOR_DHT22_TIMER), TIM_IC(SENSOR_DHT22_CHANNEL)); // enable capture interrupt only when receiving data - timer_disable_counter(TIM(SENSOR_DHT22_TIMER)); // disable timer -} - -void sensor_dht22_setup(void) -{ - // setup timer to measure signal timing for bit decoding (use timer channel as input capture) - rcc_periph_clock_enable(RCC_TIM_CH(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL)); // enable clock for GPIO peripheral - rcc_periph_clock_enable(RCC_TIM(SENSOR_DHT22_TIMER)); // enable clock for timer peripheral - timer_reset(TIM(SENSOR_DHT22_TIMER)); // reset timer state - timer_set_mode(TIM(SENSOR_DHT22_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock,edge alignment (simple count), and count up - timer_set_prescaler(TIM(SENSOR_DHT22_TIMER), 2-1); // set the prescaler so this 16 bits timer allows to wait for 18 ms for the start signal ( 1/(72E6/2/(2**16))=1.820ms ) - timer_ic_set_input(TIM(SENSOR_DHT22_TIMER), TIM_IC(SENSOR_DHT22_CHANNEL), TIM_IC_IN_TI(SENSOR_DHT22_CHANNEL)); // configure ICx to use TIn - timer_ic_set_filter(TIM(SENSOR_DHT22_TIMER), TIM_IC(SENSOR_DHT22_CHANNEL), TIM_IC_OFF); // use no filter input (precise timing needed) - timer_ic_set_polarity(TIM(SENSOR_DHT22_TIMER), TIM_IC(SENSOR_DHT22_CHANNEL), TIM_IC_FALLING); // capture on rising edge - timer_ic_set_prescaler(TIM(SENSOR_DHT22_TIMER), TIM_IC(SENSOR_DHT22_CHANNEL), TIM_IC_PSC_OFF); // don't use any prescaler since we want to capture every pulse - - timer_clear_flag(TIM(SENSOR_DHT22_TIMER), TIM_SR_UIF); // clear flag - timer_update_on_overflow(TIM(SENSOR_DHT22_TIMER)); // only use counter overflow as UEV source (use overflow as start time or timeout) - timer_enable_irq(TIM(SENSOR_DHT22_TIMER), TIM_DIER_UIE); // enable update interrupt for timer - - timer_clear_flag(TIM(SENSOR_DHT22_TIMER), TIM_SR_CCIF(SENSOR_DHT22_CHANNEL)); // clear input compare flag - timer_enable_irq(TIM(SENSOR_DHT22_TIMER), TIM_DIER_CCIE(SENSOR_DHT22_CHANNEL)); // enable capture interrupt - - nvic_enable_irq(NVIC_TIM_IRQ(SENSOR_DHT22_TIMER)); // catch interrupt in service routine - - sensor_dht22_reset(); // reset state -} - -bool sensor_dht22_measurement_request(void) -{ - if (sensor_dht22_state!=SENSOR_DHT22_OFF) { // not the right state to start (wait up until timeout to reset state) - return false; - } - if (gpio_get(TIM_CH_PORT(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL), TIM_CH_PIN(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL))==0) { // signal should be high per default - return false; - } - if (TIM_CR1(TIM(SENSOR_DHT22_TIMER))&(TIM_CR1_CEN)) { // timer should be off - return false; - } - sensor_dht22_reset(); // reset states - - // send start signal (pull low for > 1 ms) - gpio_clear(TIM_CH_PORT(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL), TIM_CH_PIN(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL)); // set signal to low - timer_set_counter(TIM(SENSOR_DHT22_TIMER), 0); // reset timer counter - timer_enable_counter(TIM(SENSOR_DHT22_TIMER)); // enable timer to wait for 1.8 ms until overflow - sensor_dht22_state = SENSOR_DHT22_HOST_START; // remember we started sending signal - - return true; -} - -struct sensor_dht22_measurement_t sensor_dht22_measurement_decode(void) -{ - struct sensor_dht22_measurement_t measurement = { NAN, NAN }; // measurement to return - if (sensor_dht22_bit<40) { // not enough bits received - return measurement; - } - if ((uint8_t)(sensor_dht22_bits[0]+sensor_dht22_bits[1]+sensor_dht22_bits[2]+sensor_dht22_bits[3])!=sensor_dht22_bits[4]) { // error in checksum (not really parity bit, as mentioned in the datasheet) - return measurement; - } - // calculate measured values (stored as uint16_t deci-value) - measurement.humidity = (int16_t)((sensor_dht22_bits[0]<<8)+sensor_dht22_bits[1])/10.0; - measurement.temperature = (int16_t)((sensor_dht22_bits[2]<<8)+sensor_dht22_bits[3])/10.0; - - return measurement; -} - -/** interrupt service routine called for timer */ -void TIM_ISR(SENSOR_DHT22_TIMER)(void) -{ - if (timer_get_flag(TIM(SENSOR_DHT22_TIMER), TIM_SR_UIF)) { // overflow update event happened - timer_clear_flag(TIM(SENSOR_DHT22_TIMER), TIM_SR_UIF); // clear flag - if (sensor_dht22_state==SENSOR_DHT22_HOST_START) { // start signal sent - gpio_set_mode(TIM_CH_PORT(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL), GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, TIM_CH_PIN(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL)); // switch pin to input (the external pull up with also set the signal high) - sensor_dht22_state = SENSOR_DHT22_HOST_STARTED; // switch to next state - timer_ic_enable(TIM(SENSOR_DHT22_TIMER), TIM_IC(SENSOR_DHT22_CHANNEL)); // enable capture interrupt only when receiving data - } else { // timeout occurred - sensor_dht22_reset(); // reset states - } - } else if (timer_get_flag(TIM(SENSOR_DHT22_TIMER), TIM_SR_CCIF(SENSOR_DHT22_CHANNEL))) { // edge detected on input capture - uint16_t time = TIM_CCR(SENSOR_DHT22_TIMER,SENSOR_DHT22_CHANNEL); // save captured bit timing (this clear also the flag) - timer_set_counter(TIM(SENSOR_DHT22_TIMER), 0); // reset timer counter - time = (time*1E6)/(rcc_ahb_frequency/(TIM_PSC(TIM(SENSOR_DHT22_TIMER))+1)); // calculate time in us - switch (sensor_dht22_state) { - case (SENSOR_DHT22_HOST_STARTED): // the host query data and the slave is responding - sensor_dht22_state = SENSOR_DHT22_SLAVE_START; // set new state - break; - case (SENSOR_DHT22_SLAVE_START): // the slave sent the start signal - if (time >= ((80+80)*(1-SENSOR_DHT22_JITTER)) && time <= ((80+80)*(1+SENSOR_DHT22_JITTER))) { // response time should be 80 us low and 80 us high - sensor_dht22_state = SENSOR_DHT22_SLAVE_BIT; // set new state - } else { - goto error; - } - break; - case (SENSOR_DHT22_SLAVE_BIT): // the slave sent a bit - if (sensor_dht22_bit>=40) { // no bits should be received after 40 bits - goto error; - } - if (time >= ((50+26)*(1-SENSOR_DHT22_JITTER)) && time <= ((50+28)*(1+SENSOR_DHT22_JITTER))) { // bit 0 time should be 50 us low and 26-28 us high - sensor_dht22_bits[sensor_dht22_bit/8] &= ~(1<<(7-(sensor_dht22_bit%8))); // clear bit - } else if (time >= ((50+70)*(1-SENSOR_DHT22_JITTER)) && time <= ((50+70)*(1+SENSOR_DHT22_JITTER))) { // bit 1 time should be 50 us low and 70 us high - sensor_dht22_bits[sensor_dht22_bit/8] |= (1<<(7-(sensor_dht22_bit%8))); // set bit - } else { - goto error; - } - sensor_dht22_bit++; - if (sensor_dht22_bit>=40) { // all bits received - sensor_dht22_reset(); // reset states - sensor_dht22_bit = 40; // signal decoder all bits have been received - sensor_dht22_measurement_received = true; // signal user all bits have been received - } - break; - default: // unexpected state -error: - sensor_dht22_reset(); // reset states - } - } else { // no other interrupt should occur - while (true); // unhandled exception: wait for the watchdog to bite - } -} diff --git a/lib/sensor_dht22.h b/lib/sensor_dht22.h deleted file mode 100644 index ba2b4d9..0000000 --- a/lib/sensor_dht22.h +++ /dev/null @@ -1,41 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to query measurements from Aosong DHT22 (aka. AM2302) temperature and relative humidity sensor (API) - * @file sensor_dht22.h - * @author King Kévin - * @date 2017 - * @note peripherals used: timer channel @ref sensor_dht22_timer (add external pull-up resistor) - */ -#pragma once - -/** a measurement response has been received */ -extern volatile bool sensor_dht22_measurement_received; - -/** measurement returned by sensor */ -struct sensor_dht22_measurement_t { - float humidity; /**< relative humidity in %RH (0-100) */ - float temperature; /**< temperature in °C (-40-80) */ -}; - -/** setup peripherals to communicate with sensor */ -void sensor_dht22_setup(void); -/** request measurement from sensor - * @return request started successfully - */ -bool sensor_dht22_measurement_request(void); -/** decode received measurement - * @return decoded measurement (0xff,0xff if invalid) - */ -struct sensor_dht22_measurement_t sensor_dht22_measurement_decode(void); diff --git a/lib/sensor_ds18b20.c b/lib/sensor_ds18b20.c deleted file mode 100644 index 1923837..0000000 --- a/lib/sensor_ds18b20.c +++ /dev/null @@ -1,196 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library for Maxim DS18B20 digital temperature sensor (using 1-Wire protocol) (code) - * @file sensor_ds18b20.c - * @author King Kévin - * @date 2017 - * @note peripherals used: 1-Wire (timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio) - * @warning this library does not support parasite power mode and alarms - */ -/* standard libraries */ -#include // standard integer types -#include // boolean type -#include // size_t definition -#include // NAN definition - -/* own libraries */ -#include "global.h" // help macros -#include "onewire_master.h" // 1-Wire utilities -#include "sensor_ds18b20.h" // own definitions - -/** remember number of DS18B20 sensors on 1-Wire bus for certain functions */ -uint64_t sensors = 0; -/** remember if only DS18B20 sensors on 1-Wire bus for certain functions */ -bool only = false; -/** remember code of last sensor **/ -uint64_t last = 0; - -void sensor_ds18b20_setup(void) -{ - onewire_master_setup(); // setup 1-Wire peripheral to communicate with sensors on bus - sensor_ds18b20_number(); // scan for sensor (remembers sensor number and exclusivity) -} - -uint64_t sensor_ds18b20_number(void) -{ - sensors = 0; // reset number - only = true; // reset state - uint64_t code = 0; // ROM code found (use 0 to start from scratch) - bool more = true; // save if other additional slaves exist - - while (more) { // scan for all 1-Wire slaves - if (!onewire_master_reset()) { // send reset to start communication - return 0; // no slave presence detected - } - more = onewire_master_rom_search(&code, false); // get next slave ROM code (without alarm) - if (0==code) { // error occurred - return 0; - } - if (0x28==(code&0xff)) { // family code (8-LSb) for DS18B20 sensors is 0x28 - last = code; // save last found code - sensors++; // we found an additional sensor - } else { - only = false; // we found a slave which is not a sensor - } - } - - return sensors; -} - -bool sensor_ds18b20_only(void) -{ - sensor_ds18b20_number(); // this also checks for exclusivity - return only; -} - -bool sensor_ds18b20_list(uint64_t* code) -{ - if (!onewire_master_reset()) { // send reset to start communication - return false; // no slave presence detected - } - onewire_master_rom_search(code, false); // get next code - return (last!=*code); // verify if the last has been found -} - -bool sensor_ds18b20_convert(uint64_t code) -{ - if (0==code && !only) { // asked for broadcast but there are different slaves on bus - return false; // broadcast not possible when there are also different slaves on bus - } - - // send reset pulse - if (!onewire_master_reset()) { // send reset to start communication - return false; // no slave presence detected - } - - // send ROM command to select slave(s) - if (0==code) { // broadcast convert - if (!onewire_master_rom_skip()) { // select all slaves - return false; // ROM command failed - } - } else { - if (!onewire_master_rom_match(code)) { // select specific slave - return false; // ROM command failed - } - } - - // send convert T function command - return onewire_master_function_read(0x44, NULL, 0); -} - -float sensor_ds18b20_temperature(uint64_t code) -{ - if (0==code && (sensors>1 || !only)) { // broadcast read requested - return NAN; // this function is not possible when several sensors or other devices are present - } - - // send reset pulse - if (!onewire_master_reset()) { // send reset to start communication - return NAN; // no slave presence detected - } - - // send ROM command to select slave - if (!onewire_master_rom_match(code)) { // select specific slave - return NAN; // ROM command failed - } - - // read scratchpad to get temperature (on byte 0 and 1) - uint8_t scratchpad[9] = {0}; // to store read scratchpad - if (!onewire_master_function_read(0xbe, scratchpad, sizeof(scratchpad)*8)) { // read complete scratchpad - return NAN; // error occurred during read - } - - // verify if data is valid - if (onewire_master_crc(scratchpad, sizeof(scratchpad))) { // check CRC checksum - return NAN; // data corrupted - } - - // calculate temperature (stored as int16_t but on 0.125 C steps) - return ((int16_t)(scratchpad[1]<<8)+scratchpad[0])/16.0; // get temperature (on < 16 precision the last bits are undefined, but that doesn't matter for the end result since the lower precision is still provided) -} - -bool sensor_ds18b20_precision(uint64_t code, uint8_t precision) -{ - if (precision<9 || precision>12) { // check input - return false; // wrong precision value - } - - if (0==code && !only) { // asked for broadcast but there are different slaves on bus - return false; // broadcast not possible when there are also different slaves on bus - } - - // send reset pulse - if (!onewire_master_reset()) { // send reset to start communication - return false; // no slave presence detected - } - - // send ROM command to select slave(s) - if (0==code) { // broadcast convert - if (!onewire_master_rom_skip()) { // select all slaves - return false; // ROM command failed - } - } else { - if (!onewire_master_rom_match(code)) { // select specific slave - return false; // ROM command failed - } - } - - // read scratchpad to get alarm values (on byte 2 and 3) - uint8_t scratchpad[9] = {0}; // to store read scratchpad - if (!onewire_master_function_read(0xbe, scratchpad, sizeof(scratchpad)*8)) { // read complete scratchpad - return false; // error occurred during read - } - - // verify if data is valid - if (onewire_master_crc(scratchpad, sizeof(scratchpad))) { // check CRC checksum - return false; // data corrupted - } - - // send new configuration (and keep the alarm values) - uint8_t configuration[3] = {0}; // to store T_HIGH, T_LOW, and configuration - configuration[0] = scratchpad[2]; // keep T_HIGH - configuration[1] = scratchpad[3]; // keep T_LOW - configuration[2] = 0x1f+((precision-9)<<5); // set precision bit (R1-R0 on bit 6-5) - if (!onewire_master_function_write(0x4e, configuration, sizeof(configuration)*8)) { // write scratchpad with new configuration (all three bytes must be written) - return false; // error occurred during write - } - - // store new configuration into sensor's EEPROM for retrieval on next power up - if (!onewire_master_function_read(0x48, NULL, 0)) { // copy scratchpad (to EEPROM) - return false; // error during copy - } - - return true; -} diff --git a/lib/sensor_ds18b20.h b/lib/sensor_ds18b20.h deleted file mode 100644 index 046b7aa..0000000 --- a/lib/sensor_ds18b20.h +++ /dev/null @@ -1,56 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library for Maxim DS18B20 digital temperature sensor (using 1-Wire protocol) (API) - * @file sensor_ds18b20.h - * @author King Kévin - * @date 2017 - * @note peripherals used: 1-Wire (timer @ref onewire_master_timer, GPIO @ref onewire_master_gpio) - * @warning this library does not support parasite power mode and alarms - */ -#pragma once - -/** setup 1-Wire peripheral to communicate with sensors on bus */ -void sensor_ds18b20_setup(void); -/** get number of DS18B20 sensors on bus - * @return number of DS18B20 sensors on bus - */ -uint64_t sensor_ds18b20_number(void); -/** verify if only DS18B20 sensors are on the bus - * @return if only DS18B20 sensors are on the bus - */ -bool sensor_ds18b20_only(void); -/** send all DS18B20 slaves on the bus - * @param[out] code ROM code for sensor (0 if error occurred) - * @return if an additional sensors have been detected - */ -bool sensor_ds18b20_list(uint64_t* code); -/** start converting (e.g. measuring) temperature - * @warning conversion time to wait before reading temperature depends on the resolution set (9 bits: 93.75ms, 10 bits: 187.5ms, 11 bits: 375ms, 12 bits: 950ms) - * @param[in] code ROM code of sensor to start conversion on (0 for all, if only DS18B20 sensors are on the bus) - * @return if conversion started - */ -bool sensor_ds18b20_convert(uint64_t code); -/** get converted temperature - * @note 85.0 C is the default temperature when no conversion has been performed - * @param[in] code ROM code of sensor - * @return temperature (NaN if error) - */ -float sensor_ds18b20_temperature(uint64_t code); -/** set conversion precision - * @param[in] code ROM code of sensor to start conversion on (0 for all, if only DS18B20 sensors are on the bus) - * @param[in] precision precision in bits (9-12) - * @return if operation succeeded - */ -bool sensor_ds18b20_precision(uint64_t code, uint8_t precision); diff --git a/lib/sensor_pzem.c b/lib/sensor_pzem.c deleted file mode 100644 index 0d81d99..0000000 --- a/lib/sensor_pzem.c +++ /dev/null @@ -1,214 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to query measurements from peacefair PZEM-004 and PZEM-004T electricity meter (code) - * @file sensor_pzem.c - * @author King Kévin - * @date 2016 - * @note peripherals used: USART @ref sensor_pzem_usart, timer @ref sensor_pzem_timer - */ - -/* standard libraries */ -#include // standard integer types -#include // general utilities - -/* STM32 (including CM3) libraries */ -#include // Cortex M3 utilities -#include // interrupt handler -#include // real-time control clock library -#include // general purpose input output library -#include // universal synchronous asynchronous receiver transmitter library -#include // timer utilities - -/* own libraries */ -#include "sensor_pzem.h" // PZEM electricity meter header and definitions -#include "global.h" // common methods - -/** @defgroup sensor_pzem_usart USART peripheral used for communication with electricity meter - * @{ - */ -#define SENSOR_PZEM_USART 2 /**< USART peripheral */ -/** @} */ - -/** @defgroup sensor_pzem_timer timer peripheral used for waiting before sending the next request - * @{ - */ -#define SENSOR_PZEM_TIMER 2 /**< timer peripheral */ -/** @} */ - -/* input and output ring buffer, indexes, and available memory */ -static uint8_t rx_buffer[7] = {0}; /**< buffer for received response */ -static volatile uint8_t rx_i = 0; /**< current position of read received data */ -static uint8_t tx_buffer[7] = {0}; /**< buffer for request to transmit */ -static volatile uint8_t tx_i = 0; /**< current position of transmitted data */ - -volatile bool sensor_pzem_measurement_received = false; - -void sensor_pzem_setup(void) -{ - /* enable USART I/O peripheral */ - rcc_periph_clock_enable(RCC_AFIO); // enable pin alternate function (USART) - rcc_periph_clock_enable(USART_PORT_RCC(SENSOR_PZEM_USART)); // enable clock for USART port peripheral - rcc_periph_clock_enable(USART_RCC(SENSOR_PZEM_USART)); // enable clock for USART peripheral - gpio_set_mode(USART_PORT(SENSOR_PZEM_USART), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_PIN_TX(SENSOR_PZEM_USART)); // setup GPIO pin USART transmit - gpio_set_mode(USART_PORT(SENSOR_PZEM_USART), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_PIN_RX(SENSOR_PZEM_USART)); // setup GPIO pin USART receive - gpio_set(USART_PORT(SENSOR_PZEM_USART), USART_PIN_RX(SENSOR_PZEM_USART)); // pull up to avoid noise when not connected - - /* setup USART parameters for electricity meter: 9600 8N1 */ - usart_set_baudrate(USART(SENSOR_PZEM_USART), 9600); // the electricity meter uses a fixed baud rate of 9600 bps - usart_set_databits(USART(SENSOR_PZEM_USART), 8); - usart_set_stopbits(USART(SENSOR_PZEM_USART), USART_STOPBITS_1); - usart_set_mode(USART(SENSOR_PZEM_USART), USART_MODE_TX_RX); - usart_set_parity(USART(SENSOR_PZEM_USART), USART_PARITY_NONE); - usart_set_flow_control(USART(SENSOR_PZEM_USART), USART_FLOWCONTROL_NONE); - - nvic_enable_irq(USART_IRQ(SENSOR_PZEM_USART)); // enable the USART interrupt - usart_enable_rx_interrupt(USART(SENSOR_PZEM_USART)); // enable receive interrupt - usart_enable(USART(SENSOR_PZEM_USART)); // enable USART - - // setup timer to wait for minimal time before next transmission (after previous transmission or reception) - rcc_periph_clock_enable(RCC_TIM(SENSOR_PZEM_TIMER)); // enable clock for timer block - timer_reset(TIM(SENSOR_PZEM_TIMER)); // reset timer state - timer_set_mode(TIM(SENSOR_PZEM_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock,edge alignment (simple count), and count up - timer_one_shot_mode(TIM(SENSOR_PZEM_TIMER)); // stop counter after update event (we only need to count down once) - timer_set_prescaler(TIM(SENSOR_PZEM_TIMER), 550-1); // set the prescaler so this 16 bits timer allows to wait for maximum 500 ms ( 1/(72E6/550/(2**16))=500.62ms ) - timer_set_period(TIM(SENSOR_PZEM_TIMER), 0xffff/2); // the timing is not defined in the specification. I tested until the communication was reliable (all requests get an response) - timer_clear_flag(TIM(SENSOR_PZEM_TIMER), TIM_SR_UIF); // clear flag - timer_enable_irq(TIM(SENSOR_PZEM_TIMER), TIM_DIER_UIE); // enable update interrupt for timer - nvic_enable_irq(NVIC_TIM_IRQ(SENSOR_PZEM_TIMER)); // catch interrupt in service routine - - /* reset buffer states */ - tx_i = LENGTH(tx_buffer); - rx_i = 0; - sensor_pzem_measurement_received = false; -} - -void sensor_pzem_measurement_request(uint32_t address, enum sensor_pzem_measurement_type_t type) -{ - if (tx_i=SENSOR_PZEM_MAX) { // invalid type - return; - } - tx_buffer[0] = 0xB0+type; // set request nibble and type nibble - tx_buffer[1] = (address>>24)&0xff; // set address - tx_buffer[2] = (address>>16)&0xff; // set address - tx_buffer[3] = (address>>8)&0xff; // set address - tx_buffer[4] = (address>>0)&0xff; // set address - tx_buffer[5] = 0; // only used to set alarm - tx_buffer[6] = 0; // to calculate checksum (sum of all previous bytes) - for (uint8_t i=0; i=SENSOR_PZEM_MAX) { // not a valid response type received (actually 4 and 5 are valid, but should not happen when using this code - return measurement; - } - uint8_t checksum = 0; // calculate checksum (sum of all other bytes) - for (uint8_t i=0; i=LENGTH(rx_buffer)) { // buffer full - sensor_pzem_measurement_received = true; // notify used response has been received - } - } else { // previous response not read before receiving the next - usart_recv(USART(SENSOR_PZEM_USART)); // drop received buffer - } - timer_set_counter(TIM(SENSOR_PZEM_TIMER), 0); // reset timer counter to get preset waiting time - timer_enable_counter(TIM(SENSOR_PZEM_TIMER)); // start timer between requests - } -} - -/** interrupt service routine called on timeout */ -void TIM_ISR(SENSOR_PZEM_TIMER)(void) -{ - if (timer_get_flag(TIM(SENSOR_PZEM_TIMER), TIM_SR_UIF)) { // update event happened - timer_clear_flag(TIM(SENSOR_PZEM_TIMER), TIM_SR_UIF); // clear flag - if (tx_i. - * - */ -/** library to query measurements from peacefair PZEM-004 and PZEM-004T electricity meter (API) - * @file sensor_pzem.h - * @author King Kévin - * @date 2016 - * @note peripherals used: USART @ref sensor_pzem_usart, timer @ref sensor_pzem_timer - */ -#pragma once - -/** a measurement response has been received */ -extern volatile bool sensor_pzem_measurement_received; - -/** measurements (and configurations) offered by electricity meter */ -enum sensor_pzem_measurement_type_t { - SENSOR_PZEM_VOLTAGE = 0, - SENSOR_PZEM_CURRENT = 1, - SENSOR_PZEM_POWER = 2, - SENSOR_PZEM_ENERGY = 3, -// SENSOR_PZEM_ADDRESS = 4, // this is a setting, not a measurement -// SENSOR_PZEM_ALARM = 5, // this is a setting, not a measurement - SENSOR_PZEM_MAX -}; - -/** measurement returned by electricity meter */ -struct sensor_pzem_measurement_t { - enum sensor_pzem_measurement_type_t type; /**< measurement type */ - bool valid; /**< is the measurement valid (e.g. format and checksum are correct) */ - /** possible measurement values */ - union measurement_t { - float voltage; /**< measured voltage in volts */ - float current; /**< measured current in amperes */ - uint16_t power; /**< measured power in watts */ - uint32_t energy; /**< measured energy in watts/hour (24 bits) */ - } value; /**< measurement value */ -}; - -/** setup peripherals to communicate with electricity meter */ -void sensor_pzem_setup(void); -/** request measurement from electricity meter - * @param[in] address electricity meter device address - * @param[in] type measurement type to request - */ -void sensor_pzem_measurement_request(uint32_t address, enum sensor_pzem_measurement_type_t type); -/** decode received measurement - * @return decoded measurement (invalid if no new measurement has been received) - */ -struct sensor_pzem_measurement_t sensor_pzem_measurement_decode(void); diff --git a/lib/sensor_sdm120.c b/lib/sensor_sdm120.c deleted file mode 100644 index 77f4951..0000000 --- a/lib/sensor_sdm120.c +++ /dev/null @@ -1,371 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to query measurements from eastron SDM120-ModBus electricity meter (code) - * @file sensor_sdm120.c - * @author King Kévin - * @date 2016 - * @note peripherals used: USART @ref sensor_sdm120_usart , GPIO @ref sensor_sdm120_gpio , timer @ref sensor_sdm120_timer - */ -/* standard libraries */ -#include // standard integer types -#include // general utilities -#include // mathematical utilities - -/* STM32 (including CM3) libraries */ -#include // real-time control clock library -#include // general purpose input output library -#include // universal synchronous asynchronous receiver transmitter library -#include // timer utilities -#include // interrupt handler -#include // Cortex M3 utilities - -#include "sensor_sdm120.h" // SDM120 electricity meter header and definitions -#include "global.h" // common methods - -/** @defgroup sensor_sdm120_usart USART peripheral used for communication with electricity meter - * @{ - */ -#define SENSOR_SDM120_USART 3 /**< USART peripheral */ -/** @} */ - -/** @defgroup sensor_sdm120_gpio GPIO peripheral used for controlling RS-485 adapter - * @note driver output is enabled on high while receiver output is enabled on low, thus one pin can be used to control both - * @{ - */ -#define SENSOR_SDM120_REDE_PORT B /**< GPIO port for RS-485 receiver and driver output enable signal */ -#define SENSOR_SDM120_REDE_PIN 12 /**< GPIO pin for RS-485 receiver and driver output enable signal */ -/** @} */ - -/** @defgroup sensor_sdm120_timer timer peripheral to enforce waiting time between messages - * @note 60 ms are recommended between messages in SDM630 ModBus protocol implementation and this seem to also apply to SDM120 - * @{ - */ -#define SENSOR_SDM120_TIMER 3 /**< timer number to count time */ -/** @} */ - -/* input and output ring buffer, indexes, and available memory */ -static uint8_t rx_buffer[9] = {0}; /**< buffer for received response (ModBus response messages can be 2+256+2 long but we will only read up to 2 registers) */ -static volatile uint8_t rx_used = 0; /**< number of received data bytes in buffer */ -static uint8_t tx_buffer[13] = {0}; /**< buffer for request to transmit (ModBus request messages can be 7+256+2 long but we will only write up to 2 registers */ -static volatile uint8_t tx_used = 0; /**< number of byte to transmit */ - -volatile bool sensor_sdm120_measurement_received = false; - -/** the ModBus timeouts to respect for sending messages **/ -static enum timeout_t { - TIMEOUT_BEGIN = 0, /**< silent time before sending data */ - TIMEOUT_END, /**< silent time after sending data */ - TIMEOUT_BETWEEN, /**< time to wait between messages */ - TIMEOUT_MAX /**< last element (useful to no the number of elements) */ -} timeout; /**< the current timeout used */ -/** current timeout used */ -static uint16_t timeout_times[TIMEOUT_MAX] = {0}; - -/** SDM120 3xxxx input register start addresses for the measurement types */ -static const uint16_t register_input[] = { - 0x0000, // 30001 voltage (in volts) - 0x0006, // 30007 current (in amperes) - 0x000c, // 30013 active power (in watts) - 0x0012, // 30019 apparent power (in volt amperes) - 0x0018, // 30025 reactive power (in volt amperes reactive) - 0x001e, // 30031 power factor (0-1) - 0x0046, // 30071 frequency (in hertz) - 0x0048, // 30073 import active energy (in kWh) - 0x004a, // 30075 export active energy (in kWh) - 0x004c, // 30077 import reactive energy (in kVArh) - 0x004e, // 30079 export reactive energy (in kVArh) - 0x0156, // 30343 total active energy (in kWh) - 0x0158 // 30345 total reactive energy (in kVArh) -}; - -/** SDM120 4xxxx holding register start addresses for the configuration types */ -static const uint16_t register_holding[] = { - 0x000c, // relay pulse width (60, 100, or 200 ms) - 0x0012, // network parity stop (0: 1 stop bit no parity, 1: one stop bit even parity, 2: one stop bit odd parity, 3: two stop bits no parity) - 0x0014, // meter slave address (1-247) - 0x001c, // baud rate (0: 2400 bps, 1: 4800 bps, 2: 9600 bps, 5: 1200 bps) - 0x0056, // pulse 1 output mode (1: import active energy, 2: import+export active energy, 4: export active energy, 5: import reactive energy, 6: import+export reactive energy, 8: export reactive energy) - 0xf900, // time of scroll display (0-30 s) - 0xf910, // pulse 1 output (0: 0.001 kWh/imp, 1: 0.01 kWh/imp, 2: 0.1 kWh/imp, 3: 1 kWh/imp) - 0xf920 // measurement mode (1: total=import, 2: total=import+export, 3: total=import-export) -}; - -/** compute CRC for ModBus - * @note ModBus uses ANSi/IBM 16-bits CRC (with normal polynomial 0x8005, reverse polynomial 0xA001, start value 0xfff) - * @param[in] buffer data on which to compute the CRC for - * @param[in] size number of byte to compute the CRC for - * @return computed CRC checksum - */ -static uint16_t crc_modbus(uint8_t* buffer, uint8_t size) -{ - uint16_t crc = 0xffff; // initial value (for ModBus) - for (uint8_t i=0; i>1)^0xA001; // // shift to the right (for the next bit) and XOR with (reverse) polynomial - } else { - crc >>= 1; // just shift right (for the next bit) - } - } - } - return crc; -} - -void sensor_sdm120_setup(uint32_t baudrate) -{ - // enable USART I/O peripheral - rcc_periph_clock_enable(RCC_AFIO); // enable pin alternate function (USART) - rcc_periph_clock_enable(USART_PORT_RCC(SENSOR_SDM120_USART)); // enable clock for USART port peripheral - rcc_periph_clock_enable(USART_RCC(SENSOR_SDM120_USART)); // enable clock for USART peripheral - gpio_set_mode(USART_PORT(SENSOR_SDM120_USART), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, USART_PIN_TX(SENSOR_SDM120_USART)); // setup GPIO pin USART transmit - gpio_set_mode(USART_PORT(SENSOR_SDM120_USART), GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, USART_PIN_RX(SENSOR_SDM120_USART)); // setup GPIO pin USART receive - gpio_clear(USART_PORT(SENSOR_SDM120_USART), USART_PIN_RX(SENSOR_SDM120_USART)); // pull down to avoid noise when not connected (it will be set low by RS485 chip when RO is enabled) - - // setup USART parameters for electricity meter - usart_set_baudrate(USART(SENSOR_SDM120_USART), baudrate); // get baud rate by scrolling through the measurements on the electricity meter's screen (default 2400) - usart_set_databits(USART(SENSOR_SDM120_USART), 8); - usart_set_stopbits(USART(SENSOR_SDM120_USART), USART_STOPBITS_1); - usart_set_mode(USART(SENSOR_SDM120_USART), USART_MODE_TX_RX); - usart_set_parity(USART(SENSOR_SDM120_USART), USART_PARITY_NONE); // get parity by scrolling through the measurements on the electricity meter's screen (default none) - usart_set_flow_control(USART(SENSOR_SDM120_USART), USART_FLOWCONTROL_NONE); - - nvic_enable_irq(USART_IRQ(SENSOR_SDM120_USART)); // enable the USART interrupt - usart_enable_rx_interrupt(USART(SENSOR_SDM120_USART)); // enable receive interrupt - usart_enable(USART(SENSOR_SDM120_USART)); // enable USART - - // setup GPIO - rcc_periph_clock_enable(RCC_GPIO(SENSOR_SDM120_REDE_PORT)); // enable clock for GPIO peripheral - gpio_set_mode(GPIO(SENSOR_SDM120_REDE_PORT), GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, GPIO(SENSOR_SDM120_REDE_PIN)); // setup GPIO pin for receiver and driver output enable pin - gpio_clear(GPIO(SENSOR_SDM120_REDE_PORT),GPIO(SENSOR_SDM120_REDE_PIN)); // disable driver output and enable receive output - - // setup timer to wait for minimal time before next transmission - rcc_periph_clock_enable(RCC_TIM(SENSOR_SDM120_TIMER)); // enable clock for timer block - timer_reset(TIM(SENSOR_SDM120_TIMER)); // reset timer state - timer_set_mode(TIM(SENSOR_SDM120_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock,edge alignment (simple count), and count up - timer_one_shot_mode(TIM(SENSOR_SDM120_TIMER)); // stop counter after update event (we only need to count down once) - timer_set_prescaler(TIM(SENSOR_SDM120_TIMER), 66-1); // set the prescaler so this 16 bits timer allows to wait for 60 ms ( 1/(72E6/66/(2**16))=60.07ms ) - timeout_times[TIMEOUT_BEGIN] = (rcc_ahb_frequency/(TIM_PSC(TIM(SENSOR_SDM120_TIMER))+1))/baudrate/8/2.5; // wait at least 2.5 characters before sending data - timeout_times[TIMEOUT_END] = (rcc_ahb_frequency/(TIM_PSC(TIM(SENSOR_SDM120_TIMER))+1))/baudrate/8/2.5; // wait at least 2.5 characters after sending data - timeout_times[TIMEOUT_BETWEEN] = 0.06*(rcc_ahb_frequency/(TIM_PSC(TIM(SENSOR_SDM120_TIMER))+1)); // wait at least 60 ms before sending the next message - timer_clear_flag(TIM(SENSOR_SDM120_TIMER), TIM_SR_UIF); // clear flag - timer_enable_irq(TIM(SENSOR_SDM120_TIMER), TIM_DIER_UIE); // enable update interrupt for timer - nvic_enable_irq(NVIC_TIM_IRQ(SENSOR_SDM120_TIMER)); // catch interrupt in service routine - - // reset states - tx_used = 0; - rx_used = 0; - sensor_sdm120_measurement_received = false; -} - -/** send request to electricity meter - * @param[in] meter_id electricity meter device id (ModBus salve address) - * @param[in] function ModBus function: 0x03 read two 16 bits holding registers, 0x04 read two 16 bits input registers, 0x10 write two 16 bits holding registers - * @param[in] address register start point address - * @param[in] value value to store in holding register (if function 0x10 is used) - * @return if request is correct and transmission started - */ -static bool sensor_sdm120_transmit_request(uint8_t meter_id, uint8_t function, uint16_t address, float value) -{ - if (meter_id==0) { // broadcast request are not supported - return false; - } - if (function!=0x03 && function!=0x04 && function!=0x10) { // function not supported - return false; - } - if (address%2) { // even register addresses are not supported by device - return false; - } - while (tx_used) { // transmission is ongoing - __WFI(); // wait until something happens (transmission ended) - } - // build request packet - uint8_t packet[11]; // buffer to build ModBus message (without error check) - uint8_t packet_size = 0; // ModBus message size (without error check) - packet[0] = meter_id; // set slave device address - packet[1] = function; // set function - packet[2] = address>>8; // set high register address - packet[3] = address; // set low register address - packet[4] = 0; // set high number of registers to read - packet[5] = 2; // set low number of register to read (the measurement are encoded using 32 bits IEE745 float, and register hold 16 bits, thus we want to read 2 registers - if (function==0x03 || function==0x04) { // read register - packet_size = 6; // set message size - } else if (function==0x10) { // write register - packet[6] = 4; // byte count (writing two 16 bits registers) - // store little endian encoded value in big endian encoded data - uint8_t* data = (uint8_t*)&value; - packet[7] = data[3]; - packet[8] = data[2]; - packet[9] = data[1]; - packet[10] = data[0]; - packet_size = 11; // set message size - } - uint16_t crc = crc_modbus(packet, packet_size); // compute error check - for (uint8_t i=0; i>8; // set high error check - tx_used = packet_size+2; // set request size - rx_used = 0; // reset reset buffer - sensor_sdm120_measurement_received = false; // reset measurement flag - while (TIM_CR1(TIM(SENSOR_SDM120_TIMER))&TIM_CR1_CEN) { // timer is already used - __WFI(); // wait until something happens (timer is available again) - } - gpio_set(GPIO(SENSOR_SDM120_REDE_PORT),GPIO(SENSOR_SDM120_REDE_PIN)); // enable driver output and disable receive output - // start timeout - timeout = TIMEOUT_BEGIN; // select time before sending message - timer_set_period(TIM(SENSOR_SDM120_TIMER), timeout_times[timeout]); // set corresponding timeout - timer_set_counter(TIM(SENSOR_SDM120_TIMER), 0); // reset timer counter to get preset waiting time - timer_enable_counter(TIM(SENSOR_SDM120_TIMER)); // wait - - return true; -} - -bool sensor_sdm120_measurement_request(uint8_t meter_id, enum sensor_sdm120_measurement_type_t type) -{ - if (type>=SENSOR_SDM120_MEASUREMENT_MAX) { // invalid type - return false; - } - return sensor_sdm120_transmit_request(meter_id, 0x04, register_input[type], 0); -} - -bool sensor_sdm120_configuration_request(uint8_t meter_id, enum sensor_sdm120_configuration_type_t type) -{ - if (type>=SENSOR_SDM120_CONFIGURATION_MAX) { // invalid type - return false; - } - return sensor_sdm120_transmit_request(meter_id, 0x03, register_holding[type], 0); -} - -bool sensor_sdm120_configuration_set(uint8_t meter_id, enum sensor_sdm120_configuration_type_t type, float value) -{ - if (type>=SENSOR_SDM120_CONFIGURATION_MAX) { // invalid type - return false; - } - return sensor_sdm120_transmit_request(meter_id, 0x10, register_holding[type], value); -} - -float sensor_sdm120_measurement_decode(void) -{ - float measurement = NAN; // decoded measurement to return (invalid in the beginning) - if (!sensor_sdm120_measurement_received) { // no measurement received - return NAN; - } else { - sensor_sdm120_measurement_received = false; // reset flag - } - if (rx_used<5) { // not a complete response (minimum is address, function, size/error, error check low, error check high) - return NAN; - } - // a complete message has been received - if (crc_modbus(rx_buffer,rx_used)) { // checksum error, error check failed - measurement = NAN; - } else if (rx_buffer[1]&0x80) { // error condition received - measurement = INFINITY; // indicate we received and error - } else { - switch (rx_buffer[1]) { - case 0x03: // read 4xxx holding register response received - case 0x04: // read 3xxxx input register response received - if (rx_buffer[2]==0x04 && rx_used>=(4+5)) { // 2 registers received, corresponds to implemented request - // convert big endian received float value to little endian return value - uint8_t* convert = (uint8_t*)&measurement; - convert[0] = rx_buffer[6]; - convert[1] = rx_buffer[5]; - convert[2] = rx_buffer[4]; - convert[3] = rx_buffer[3]; - } - break; - case 0x10: // write 4xxx holding register response received - measurement = (rx_buffer[4]<<8)+rx_buffer[5]; // number of registers written - break; // not supported currently - default: // unknown function response received - measurement = INFINITY; - break; // nothing to do - } - } - rx_used = 0; // reset rx_buffer usage - return measurement; -} - -/** USART interrupt service routine called when data has been transmitted or received */ -void USART_ISR(SENSOR_SDM120_USART)(void) -{ - if (usart_get_interrupt_source(USART(SENSOR_SDM120_USART), USART_SR_TXE)) { // data has been transmitted - if (tx_used) { // not all bytes transmitted - usart_send(USART(SENSOR_SDM120_USART),tx_buffer[--tx_used]); // transmit next byte (clears flag) - } else { // all bytes transmitted - usart_disable_tx_interrupt(USART(SENSOR_SDM120_USART)); // disable transmit interrupt - USART_SR(USART(SENSOR_SDM120_USART)) &= ~USART_SR_TXE; // clear flag - USART_CR1(USART(SENSOR_SDM120_USART)) |= USART_CR1_TCIE; // enable transfer complete interrupt - } - } - if (usart_get_interrupt_source(USART(SENSOR_SDM120_USART), USART_SR_TC)) { // data has been completely transmitted - USART_CR1(USART(SENSOR_SDM120_USART)) |= USART_CR1_TCIE; // disable transfer complete interrupt - USART_SR(USART(SENSOR_SDM120_USART)) &= ~USART_SR_TC; // clear flag - timeout = TIMEOUT_END; // select wait time after sending data - timer_set_period(TIM(SENSOR_SDM120_TIMER), timeout_times[timeout]); // set corresponding timeout - timer_set_counter(TIM(SENSOR_SDM120_TIMER), 0); // reset timer counter to get preset waiting time - timer_enable_counter(TIM(SENSOR_SDM120_TIMER)); // wait - } - if (usart_get_interrupt_source(USART(SENSOR_SDM120_USART), USART_SR_RXNE)) { // data has been received - if (gpio_get(GPIO(SENSOR_SDM120_REDE_PORT),GPIO(SENSOR_SDM120_REDE_PIN))) { // not in receiver mode - USART_SR(USART(SENSOR_SDM120_USART)) &= ~USART_SR_RXNE; // clear flag, ignore received data - } else if (rx_used=5 && (rx_buffer[1]&0x80)) { // error condition response received - sensor_sdm120_measurement_received = true; // notify used response has been received - } else if (rx_used>=5 && (uint8_t)(rx_used-5)>=rx_buffer[2] && (rx_buffer[1]==0x04 || rx_buffer[1]==0x03)) { // read input or holding register response received - sensor_sdm120_measurement_received = true; // notify used response has been receive - } else if (rx_used>=8 && rx_buffer[1]==0x10) { // write holding register response received - sensor_sdm120_measurement_received = true; // notify used response has been receive - } - } else { // buffer full and unknown response received - USART_SR(USART(SENSOR_SDM120_USART)) &= ~USART_SR_RXNE; // clear flag (wait for user to read measurement, this clears the buffer) - } - timeout = TIMEOUT_END; // select time after receiving data - timer_set_period(TIM(SENSOR_SDM120_TIMER), timeout_times[timeout]); // set corresponding timeout - timer_set_counter(TIM(SENSOR_SDM120_TIMER), 0); // reset timer counter to get preset waiting time - timer_enable_counter(TIM(SENSOR_SDM120_TIMER)); // wait - } -} - -/** interrupt service routine called on timeout */ -void TIM_ISR(SENSOR_SDM120_TIMER)(void) -{ - if (timer_get_flag(TIM(SENSOR_SDM120_TIMER), TIM_SR_UIF)) { // update event happened - timer_clear_flag(TIM(SENSOR_SDM120_TIMER), TIM_SR_UIF); // clear flag - // because of the one pulse mode the timer is stopped automatically - switch (timeout) { // timeout before action passed - case (TIMEOUT_BEGIN): // we can now send the data - USART_SR(USART(SENSOR_SDM120_USART)) &= USART_SR_TXE; // clear interrupt flag - usart_enable_tx_interrupt(USART(SENSOR_SDM120_USART)); // enable interrupt to send other bytes - usart_send(USART(SENSOR_SDM120_USART),tx_buffer[--tx_used]); // start transmission - break; - case (TIMEOUT_END): // we now have to wait before sending the next message - gpio_clear(GPIO(SENSOR_SDM120_REDE_PORT),GPIO(SENSOR_SDM120_REDE_PIN)); // disable driver output (and enable receive output) - timeout = TIMEOUT_BETWEEN; // select time between sending message - timer_set_period(TIM(SENSOR_SDM120_TIMER), timeout_times[timeout]); // set corresponding timeout - timer_set_counter(TIM(SENSOR_SDM120_TIMER), 0); // reset timer counter to get preset waiting time - timer_enable_counter(TIM(SENSOR_SDM120_TIMER)); // wait - case (TIMEOUT_BETWEEN): // nothing to do, we are allowed to send the next message - break; - default: - break; - } - } -} - diff --git a/lib/sensor_sdm120.h b/lib/sensor_sdm120.h deleted file mode 100644 index abef696..0000000 --- a/lib/sensor_sdm120.h +++ /dev/null @@ -1,83 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to query measurements from eastron SDM120-ModBus electricity meter (API) - * @file sensor_sdm120.h - * @author King Kévin - * @date 2016 - * @note peripherals used: USART @ref sensor_sdm120_usart , GPIO @ref sensor_sdm120_gpio , timer @ref sensor_sdm120_timer - */ -#pragma once - -/** a measurement response has been received */ -extern volatile bool sensor_sdm120_measurement_received; - -/** measurement types offered by electricity meter in 3xxx input registers */ -enum sensor_sdm120_measurement_type_t { - SENSOR_SDM120_VOLTAGE = 0, - SENSOR_SDM120_CURRENT, - SENSOR_SDM120_POWER_ACTIVE, - SENSOR_SDM120_POWER_APPARENT, - SENSOR_SDM120_POWER_REACTIVE, - SENSOR_SDM120_POWER_FACTOR, - SENSOR_SDM120_FREQUENCY, - SENSOR_SDM120_ENERGY_ACTIVE_IMPORT, - SENSOR_SDM120_ENERGY_ACTIVE_EXPORT, - SENSOR_SDM120_ENERGY_REACTIVE_IMPORT, - SENSOR_SDM120_ENERGY_REACTIVE_EXPORT, - SENSOR_SDM120_ENERGY_ACTIVE_TOTAL, - SENSOR_SDM120_ENERGY_REACTIVE_TOTAL, - SENSOR_SDM120_MEASUREMENT_MAX -}; - -/** configuration types for electricity meter in 4xxx holding registers */ -enum sensor_sdm120_configuration_type_t { - SENSOR_SDM120_RELAY_PULSE_WIDTH = 0, - SENSOR_SDM120_NETWORK_PARITY_STOP, - SENSOR_SDM120_METER_ID, - SENSOR_SDM120_BAUD_RATE, - SENSOR_SDM120_PULSE_1_OUTPUT_MODE, - SENSOR_SDM120_TIME_OF_SCROLL_DISPLAY, - SENSOR_SDM120_PULSE_1_OUTPUT, - SENSOR_SDM120_MEASUREMENT_MODE, - SENSOR_SDM120_CONFIGURATION_MAX -}; - -/** setup peripherals to communicate with electricity meter - * @param[in] baudrate baud rate of RS485 serial communication - */ -void sensor_sdm120_setup(uint32_t baudrate); -/** request measurement from electricity meter - * @param[in] meter_id electricity meter device ID - * @param[in] type measurement type to request - * @return if transmission started - */ -bool sensor_sdm120_measurement_request(uint8_t meter_id, enum sensor_sdm120_measurement_type_t type); -/** request configuration from electricity meter - * @param[in] meter_id electricity meter device ID - * @param[in] type configuration type to request - * @return if transmission started - */ -bool sensor_sdm120_configuration_request(uint8_t meter_id, enum sensor_sdm120_configuration_type_t type); -/** set configuration in electricity meter - * @param[in] meter_id electricity meter device ID - * @param[in] type configuration type to set - * @param[in] value configuration value to set - * @return if transmission started - */ -bool sensor_sdm120_configuration_set(uint8_t meter_id, enum sensor_sdm120_configuration_type_t type, float value); -/** decode received measurement - * @return decoded measurement or number of registers written, NaN if message has error or no new measurement has been received, infinity if an error or unknown message has been received - */ -float sensor_sdm120_measurement_decode(void); diff --git a/lib/uart_soft.c b/lib/uart_soft.c deleted file mode 100644 index 105d811..0000000 --- a/lib/uart_soft.c +++ /dev/null @@ -1,414 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to control up to 4 independent receive and transmit software UART ports (code) - * @file uart_soft.c - * @author King Kévin - * @date 2016 - * @note peripherals used: GPIO @ref uart_soft_gpio, timer @ref uart_soft_timer - */ - -/* standard libraries */ -#include // standard integer types -#include // general utilities - -/* STM32 (including CM3) libraries */ -#include // real-time control clock library -#include // general purpose input output library -#include // timer library -#include // interrupt handler -#include // external interrupt defines -#include // Cortex M3 utilities - -#include "uart_soft.h" // software UART library API -#include "global.h" // common methods - -/** @defgroup uart_soft_gpio GPIO used for the software 4 UART ports - * @note comment if unused - * @warning only one port must be used per line (pin number) - * @{ - */ -#define UART_SOFT_RX_PORT0 B /**< port for receive signal for UART port 0 */ -#define UART_SOFT_RX_PIN0 9 /**< pin for receive signal for UART port 0 */ -//#define UART_SOFT_RX_PORT1 A /**< port for receive signal for UART port 0 */ -//#define UART_SOFT_RX_PIN1 0 /**< pin for receive signal for UART port 0 */ -//#define UART_SOFT_RX_PORT2 A /**< port for receive signal for UART port 0 */ -//#define UART_SOFT_RX_PIN2 0 /**< pin for receive signal for UART port 0 */ -//#define UART_SOFT_RX_PORT3 A /**< port for receive signal for UART port 0 */ -//#define UART_SOFT_RX_PIN3 0 /**< pin for receive signal for UART port 0 */ -#define UART_SOFT_TX_PORT0 B /**< port for transmit signal for UART port 0 */ -#define UART_SOFT_TX_PIN0 8 /**< pin for transmit signal for UART port 0 */ -//#define UART_SOFT_TX_PORT1 A /**< port for transmit signal for UART port 0 */ -//#define UART_SOFT_TX_PIN1 0 /**< pin for transmit signal for UART port 0 */ -//#define UART_SOFT_TX_PORT2 A /**< port for transmit signal for UART port 0 */ -//#define UART_SOFT_TX_PIN2 0 /**< pin for transmit signal for UART port 0 */ -//#define UART_SOFT_TX_PORT3 A /**< port for transmit signal for UART port 0 */ -//#define UART_SOFT_TX_PIN3 0 /**< pin for transmit signal for UART port 0 */ -/** @} */ - -/** buffer size for receive and transmit buffers */ -#define UART_SOFT_BUFFER 128 -/** UART receive state definition */ -struct soft_uart_rx_state { - uint32_t port; /**< UART receive port */ - uint16_t pin; /**< UART receive pin */ - uint32_t rcc; /**< UART receive port peripheral clock */ - uint32_t exti; /**< UART receive external interrupt */ - uint32_t irq; /**< UART receive interrupt request */ - uint32_t baudrate; /**< UART receive baud rate */ - volatile uint16_t state; /**< GPIO state for receive pin */ - volatile uint8_t bit; /**< next UART frame bit to receive */ - volatile uint8_t byte; /**< byte being received */ - volatile uint8_t buffer[UART_SOFT_BUFFER]; /**< receive buffer */ - volatile uint8_t buffer_i; /**< index of current data to be read out */ - volatile uint8_t buffer_used; /**< how much data is available */ - volatile bool lock; /**< put lock when changing buffer_i or buffer_used */ - volatile uint8_t buffer_byte; /**< to temporary store byte while locked */ - volatile bool buffer_byte_used; /**< signal a byte has been stored in temporary buffer */ - -}; -/** UART transmit state definition */ -struct soft_uart_tx_state { - uint32_t port; /**< UART receive port */ - uint16_t pin; /**< UART receive pin */ - uint32_t rcc; /**< UART receive port peripheral clock */ - uint32_t baudrate; /**< UART receive baud rate */ - volatile uint8_t bit; /**< next UART frame bit to transmit */ - volatile uint8_t byte; /**< byte being transmitted */ - volatile uint8_t buffer[UART_SOFT_BUFFER]; /**< receive buffer */ - volatile uint8_t buffer_i; /**< index of current data to be read out */ - volatile uint8_t buffer_used; /**< how much data is available */ - volatile bool transmit; /**< flag to know it transmission is ongoing */ -}; - -static struct soft_uart_rx_state* uart_soft_rx_states[4] = {NULL}; /**< states of UART receive ports (up to 4) */ -static struct soft_uart_tx_state* uart_soft_tx_states[4] = {NULL}; /**< states of UART transmit ports (up to 4) */ - -volatile bool uart_soft_received[4] = {false, false, false, false}; - -/** @defgroup uart_soft_timer timer used to sample UART signals - * @{ - */ -#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0)) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1)) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2)) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN0)) - #define UART_SOFT_RX_TIMER 3 /**< timer peripheral for receive signals */ -#endif -#if (defined(UART_SOFT_TX_PORT0) && defined(UART_SOFT_TX_PIN0)) || (defined(UART_SOFT_TX_PORT1) && defined(UART_SOFT_TX_PIN1)) || (defined(UART_SOFT_TX_PORT2) && defined(UART_SOFT_TX_PIN2)) || (defined(UART_SOFT_TX_PORT3) && defined(UART_SOFT_TX_PIN0)) - #define UART_SOFT_TX_TIMER 4 /**< timer peripheral for transmit signals */ -#endif -/** @} */ - -static const uint32_t timer_flags[4] = {TIM_SR_CC1IF,TIM_SR_CC2IF,TIM_SR_CC3IF,TIM_SR_CC4IF}; /**< the interrupt flags for the compare units */ -static const uint32_t timer_interrupt[4] = {TIM_DIER_CC1IE,TIM_DIER_CC2IE,TIM_DIER_CC3IE,TIM_DIER_CC4IE}; /**< the interrupt enable for the compare units */ -static const enum tim_oc_id timer_oc[4] = {TIM_OC1,TIM_OC2,TIM_OC3,TIM_OC4}; /**< the output compares for the compare units */ - -bool uart_soft_setup(uint32_t *rx_baudrates, uint32_t *tx_baudrates) -{ - (void)rx_baudrates; // ensure compile does no complain even if no receive port is used - (void)tx_baudrates; // ensure compile does no complain even if no transmit port is used - - // save UART receive definition -#if defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) - uart_soft_rx_states[0] = calloc(1,sizeof(struct soft_uart_rx_state)); // create state definition - uart_soft_rx_states[0]->port = GPIO(UART_SOFT_RX_PORT0); // save receive port - uart_soft_rx_states[0]->pin = GPIO(UART_SOFT_RX_PIN0); // save receive pin - uart_soft_rx_states[0]->rcc = RCC_GPIO(UART_SOFT_RX_PORT0); // save receive port peripheral clock - uart_soft_rx_states[0]->exti = EXTI(UART_SOFT_RX_PIN0); // save receive external interrupt - uart_soft_rx_states[0]->irq = NVIC_EXTI_IRQ(UART_SOFT_RX_PIN0); // save receive interrupt request -#endif - - // setup UART receive GPIO - for (uint8_t rx=0; rx<4; rx++) { - if (!uart_soft_rx_states[rx]) { // verify is receive UART is defined - continue; // skip configuration if not defined - } - if (!rx_baudrates || rx_baudrates[rx]==0) { // verify if receive baud rate has been defined - return false; - } - uart_soft_rx_states[rx]->baudrate = rx_baudrates[rx]; // save baud rate - rcc_periph_clock_enable(uart_soft_rx_states[rx]->rcc); // enable clock for GPIO peripheral - gpio_set_mode(uart_soft_rx_states[rx]->port, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, uart_soft_rx_states[rx]->pin); // setup GPIO pin UART receive - gpio_set(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin); // pull up to avoid noise when not connected - rcc_periph_clock_enable(RCC_AFIO); // enable alternate function clock for external interrupt - exti_select_source(uart_soft_rx_states[rx]->exti, uart_soft_rx_states[rx]->port); // mask external interrupt of this pin only for this port - exti_enable_request(uart_soft_rx_states[rx]->exti); // enable external interrupt - exti_set_trigger(uart_soft_rx_states[rx]->exti, EXTI_TRIGGER_BOTH); // trigger when button is pressed - nvic_enable_irq(uart_soft_rx_states[rx]->irq); // enable interrupt - uart_soft_rx_states[rx]->state = gpio_get(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin); // save state of GPIO - uart_soft_rx_states[rx]->bit = 0; // reset bits received - } - - // save UART transmit definition -#if defined(UART_SOFT_TX_PORT0) && defined(UART_SOFT_TX_PIN0) - uart_soft_tx_states[0] = calloc(1,sizeof(struct soft_uart_tx_state)); // create state definition - uart_soft_tx_states[0]->port = GPIO(UART_SOFT_TX_PORT0); // save receive port - uart_soft_tx_states[0]->pin = GPIO(UART_SOFT_TX_PIN0); // save receive pin - uart_soft_tx_states[0]->rcc = RCC_GPIO(UART_SOFT_TX_PORT0); // save receive port peripheral clock -#endif - - // setup UART transmit GPIO - for (uint8_t tx=0; tx<4; tx++) { - if (!uart_soft_tx_states[tx]) { // verify is transmit UART is defined - continue; // skip configuration if not defined - } - if (!tx_baudrates || tx_baudrates[tx]==0) { // verify if transmit baud rate has been defined - return false; - } - uart_soft_tx_states[tx]->baudrate = tx_baudrates[tx]; // save baud rate - rcc_periph_clock_enable(uart_soft_tx_states[tx]->rcc); // enable clock for GPIO peripheral - gpio_set_mode(uart_soft_tx_states[tx]->port, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, uart_soft_tx_states[tx]->pin); // setup GPIO UART transmit pin - gpio_set(uart_soft_tx_states[tx]->port, uart_soft_tx_states[tx]->pin); // idle high - } - - // setup timer -#if defined(UART_SOFT_RX_TIMER) - rcc_periph_clock_enable(RCC_TIM(UART_SOFT_RX_TIMER)); // enable clock for timer peripheral - timer_reset(TIM(UART_SOFT_RX_TIMER)); // reset timer state - timer_set_mode(TIM(UART_SOFT_RX_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up - timer_set_prescaler(TIM(UART_SOFT_RX_TIMER), 0); // prescaler to be able to sample 2400-115200 bps (72MHz/2^16=1099<2400bps) - nvic_enable_irq(NVIC_TIM_IRQ(UART_SOFT_RX_TIMER)); // allow interrupt for timer - timer_enable_counter(TIM(UART_SOFT_RX_TIMER)); // start timer to generate interrupts for the receive pins -#endif -#if defined(UART_SOFT_TX_TIMER) - rcc_periph_clock_enable(RCC_TIM(UART_SOFT_TX_TIMER)); // enable clock for timer peripheral - timer_reset(TIM(UART_SOFT_TX_TIMER)); // reset timer state - timer_set_mode(TIM(UART_SOFT_TX_TIMER), TIM_CR1_CKD_CK_INT, TIM_CR1_CMS_EDGE, TIM_CR1_DIR_UP); // set timer mode, use undivided timer clock, edge alignment (simple count), and count up - timer_set_prescaler(TIM(UART_SOFT_TX_TIMER), 0); // prescaler to be able to output 2400-115200 bps (72MHz/2^16=1099<2400bps) - nvic_enable_irq(NVIC_TIM_IRQ(UART_SOFT_TX_TIMER)); // allow interrupt for timer - timer_enable_counter(TIM(UART_SOFT_TX_TIMER)); // start timer to generate interrupts for the transmit pins -#endif - - return true; // setup completed -} - -#if defined(UART_SOFT_RX_TIMER) -uint8_t uart_soft_getbyte(uint8_t uart) -{ - if (uart>=4 || !uart_soft_rx_states[uart]) { // ensure receive UART port is defined - return 0; // return - } - while (!uart_soft_rx_states[uart]->buffer_used) { // idle until data is available - __WFI(); // sleep until interrupt - } - uart_soft_rx_states[uart]->lock = true; // set lock - uint8_t to_return = uart_soft_rx_states[uart]->buffer[uart_soft_rx_states[uart]->buffer_i]; // get the next available character - uart_soft_rx_states[uart]->buffer_i = (uart_soft_rx_states[uart]->buffer_i+1)%LENGTH(uart_soft_rx_states[uart]->buffer); // update used buffer - uart_soft_rx_states[uart]->buffer_used--; // update used buffer - uart_soft_rx_states[uart]->lock = false; // free lock - if (uart_soft_rx_states[uart]->buffer_byte_used) { // temporary byte has been stored - uart_soft_rx_states[uart]->buffer[(uart_soft_rx_states[uart]->buffer_i+uart_soft_rx_states[uart]->buffer_used)%LENGTH(uart_soft_rx_states[uart]->buffer)] = uart_soft_rx_states[uart]->buffer_byte; // put byte in buffer - uart_soft_rx_states[uart]->buffer_used++; // update used buffer - uart_soft_rx_states[uart]->buffer_byte_used = false; // buffer byte is now in buffer - } - uart_soft_received[uart] = (uart_soft_rx_states[uart]->buffer_used!=0); // notify user if data is available - uart_soft_rx_states[uart]->lock = false; // free lock - return to_return; -} - -/** timer interrupt service routine to generate UART transmit signals */ -void TIM_ISR(UART_SOFT_RX_TIMER)(void) -{ - for (uint8_t rx=0; rx<4; rx++) { - if (timer_interrupt_source(TIM(UART_SOFT_RX_TIMER),timer_flags[rx])) { // got a match on compare for receive pin - timer_clear_flag(TIM(UART_SOFT_RX_TIMER),timer_flags[rx]); // clear flag - if (!uart_soft_rx_states[rx]) { // verify if RX exists - continue; // skip if receive port is not defined it - } - uart_soft_rx_states[rx]->byte += ((gpio_get(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin)==0 ? 0 : 1)<<(uart_soft_rx_states[rx]->bit-1)); // save bit value - if (uart_soft_rx_states[rx]->bit<8) { // not the last bit received - timer_set_oc_value(TIM(UART_SOFT_RX_TIMER),timer_oc[rx],timer_get_counter(TIM(UART_SOFT_RX_TIMER))+rcc_ahb_frequency/uart_soft_rx_states[rx]->baudrate); // set timer to next bit - uart_soft_rx_states[rx]->bit++; // wait for next bit - } else { // last bit received - if (uart_soft_rx_states[rx]->lock) { // someone is already reading data - uart_soft_rx_states[rx]->buffer_byte = uart_soft_rx_states[rx]->byte; // save byte - uart_soft_rx_states[rx]->buffer_byte_used = true; // notify reader there is a temporary byte - } else { // buffer can be updated - if (uart_soft_rx_states[rx]->buffer_used>=LENGTH(uart_soft_rx_states[rx]->buffer)) { // buffer is full - uart_soft_rx_states[rx]->buffer_i = (uart_soft_rx_states[rx]->buffer_i+1)%LENGTH(uart_soft_rx_states[rx]->buffer); // drop oldest byte - uart_soft_rx_states[rx]->buffer_used--; // update buffer usage - } - uart_soft_rx_states[rx]->buffer[(uart_soft_rx_states[rx]->buffer_i+uart_soft_rx_states[rx]->buffer_used)%LENGTH(uart_soft_rx_states[rx]->buffer)] = uart_soft_rx_states[rx]->byte; // put byte in buffer - uart_soft_rx_states[rx]->buffer_used++; // update used buffer - uart_soft_received[rx] = true; // notify user data is available - } - timer_disable_irq(TIM(UART_SOFT_RX_TIMER),timer_interrupt[rx]); // stop_interrupting - uart_soft_rx_states[rx]->bit = 0; // next bit should be first bit of next byte - } - } - } -} -#endif - -#if defined(UART_SOFT_TX_TIMER) -void uart_soft_flush(uint8_t uart) -{ - if (uart>=4 || !uart_soft_tx_states[uart]) { // ensure transmit UART port is defined - return; // return - } - while (uart_soft_tx_states[uart]->buffer_used) { // idle until buffer is empty - __WFI(); // sleep until interrupt - } - while (uart_soft_tx_states[uart]->transmit) { // idle until transmission is complete - __WFI(); // sleep until interrupt - } -} - -/** start transmitting a byte from the buffer - * @param[in] uart UART port used for transmission - */ -static void uart_soft_transmit(uint8_t uart) { - if (uart>=4 || !uart_soft_tx_states[uart]) { // ensure transmit UART port is defined - return; // UART transmit port not defined - } - if (uart_soft_tx_states[uart]->transmit) { // already transmitting - return; // transmission is already ongoing - } - if (!uart_soft_tx_states[uart]->buffer_used) { // no buffered data to transmit - return; // nothing to transmit - } - uart_soft_tx_states[uart]->byte = uart_soft_tx_states[uart]->buffer[uart_soft_tx_states[uart]->buffer_i]; // get byte - uart_soft_tx_states[uart]->buffer_i = (uart_soft_tx_states[uart]->buffer_i+1)%LENGTH(uart_soft_tx_states[uart]->buffer); // update index - uart_soft_tx_states[uart]->buffer_used--; // update used buffer - uart_soft_tx_states[uart]->bit = 0; // LSb is transmitted first - uart_soft_tx_states[uart]->transmit = true; // start transmission - gpio_clear(uart_soft_tx_states[uart]->port, uart_soft_tx_states[uart]->pin); // output start bit - timer_set_oc_value(TIM(UART_SOFT_TX_TIMER), timer_oc[uart], timer_get_counter(TIM(UART_SOFT_TX_TIMER))+(rcc_ahb_frequency/uart_soft_tx_states[uart]->baudrate)); // set timer to output UART frame 1 (data bit 0) in 1 bit - timer_clear_flag(TIM(UART_SOFT_TX_TIMER), timer_flags[uart]); // clear flag before enabling interrupt - timer_enable_irq(TIM(UART_SOFT_TX_TIMER), timer_interrupt[uart]);// enable timer IRQ for TX for this UART -} - -void uart_soft_putbyte_nonblocking(uint8_t uart, uint8_t byte) -{ - if (uart>=4 || !uart_soft_tx_states[uart]) { // ensure transmit UART port is defined - return; // return - } - while (uart_soft_tx_states[uart]->buffer_used>=LENGTH(uart_soft_tx_states[uart]->buffer)) { // idle until there is place in the buffer - __WFI(); // sleep until something happened - } - uart_soft_tx_states[uart]->buffer[(uart_soft_tx_states[uart]->buffer_i+uart_soft_tx_states[uart]->buffer_used)%LENGTH(uart_soft_tx_states[uart]->buffer)] = byte; // save byte to be transmitted - uart_soft_tx_states[uart]->buffer_used++; // update used buffer - uart_soft_transmit(uart); // start transmission -} - -void uart_soft_putbyte_blocking(uint8_t uart, uint8_t byte) -{ - uart_soft_putbyte_nonblocking(uart, byte); // put byte in queue - uart_soft_flush(uart); // wait for all byte to be transmitted -} - -/** timer interrupt service routine to sample UART receive signals */ -void TIM_ISR(UART_SOFT_TX_TIMER)(void) -{ - for (uint8_t tx=0; tx<4; tx++) { - if (timer_interrupt_source(TIM(UART_SOFT_TX_TIMER),timer_flags[tx])) { // got a match on compare for transmit pin - timer_clear_flag(TIM(UART_SOFT_TX_TIMER),timer_flags[tx]); // clear flag - if (!uart_soft_tx_states[tx]) { // verify if transmit is defined - continue; // skip if transmit port is not defined it - } - if (uart_soft_tx_states[tx]->bit<8) { // there is a data bit to transmit - if ((uart_soft_tx_states[tx]->byte>>uart_soft_tx_states[tx]->bit)&0x01) { // bit to transmit is a 1 - gpio_set(uart_soft_tx_states[tx]->port, uart_soft_tx_states[tx]->pin); // set output to high - } else { // bit to transmit is a 0 - gpio_clear(uart_soft_tx_states[tx]->port, uart_soft_tx_states[tx]->pin); // set output to low - } - timer_set_oc_value(TIM(UART_SOFT_TX_TIMER), timer_oc[tx], timer_get_counter(TIM(UART_SOFT_TX_TIMER))+(rcc_ahb_frequency/uart_soft_tx_states[tx]->baudrate)); // wait for the next frame bit - uart_soft_tx_states[tx]->bit++; // go to next bit - } else if (uart_soft_tx_states[tx]->bit==8) { // transmit stop bit - gpio_set(uart_soft_tx_states[tx]->port, uart_soft_tx_states[tx]->pin); // go idle high - timer_set_oc_value(TIM(UART_SOFT_TX_TIMER), timer_oc[tx], timer_get_counter(TIM(UART_SOFT_TX_TIMER))+(rcc_ahb_frequency/uart_soft_tx_states[tx]->baudrate)); // wait for 1 stop bit - uart_soft_tx_states[tx]->bit++; // go to next bit - } else { // UART frame is complete - timer_disable_irq(TIM(UART_SOFT_TX_TIMER), timer_interrupt[tx]);// enable timer IRQ for TX for this UART - uart_soft_tx_states[tx]->transmit = false; // transmission finished - uart_soft_transmit(tx); // start next transmission (if there is) - } - } // compare match - } // go through UARTs -} -#endif - -/** central function handling receive signal activity */ -static void uart_soft_receive_activity(void) -{ - for (uint8_t rx=0; rx<4; rx++) { - if (!uart_soft_rx_states[rx]) { // verify if receive port is not configured - continue; // skip if receive port is not defined it - } - if (uart_soft_rx_states[rx]->state!=gpio_get(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin)) { // only do something if state changed - uart_soft_rx_states[rx]->state = gpio_get(uart_soft_rx_states[rx]->port, uart_soft_rx_states[rx]->pin); // save new state - if (uart_soft_rx_states[rx]->bit==0) { // start bit edge detected - if (uart_soft_rx_states[rx]->state==0) { // start bit has to be low - timer_set_oc_value(TIM(UART_SOFT_RX_TIMER), timer_oc[rx], timer_get_counter(TIM(UART_SOFT_RX_TIMER))+(rcc_ahb_frequency/uart_soft_rx_states[rx]->baudrate)*1.5); // set timer to sample data bit 0 in 1.5 bits - timer_clear_flag(TIM(UART_SOFT_RX_TIMER), timer_flags[rx]); // clear flag before enabling interrupt - timer_enable_irq(TIM(UART_SOFT_RX_TIMER), timer_interrupt[rx]);// enable timer IRQ for RX for this UART - uart_soft_rx_states[rx]->byte = 0; // reset byte value - uart_soft_rx_states[rx]->bit++; // wait for first bit - } - } else { // data bit detected - timer_set_oc_value(TIM(UART_SOFT_RX_TIMER), timer_oc[rx], timer_get_counter(TIM(UART_SOFT_RX_TIMER))+(rcc_ahb_frequency/uart_soft_rx_states[rx]->baudrate)/2); // resync timer to half a bit (good for drifting transmission, bad if the line is noisy) - } - } - } -} - -/** GPIO interrupt service routine to detect UART receive activity */ -#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==0) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==0) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==0) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==0) -void exti0_isr(void) -{ - exti_reset_request(EXTI0); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently) - uart_soft_receive_activity(); // check which GPIO changed -} -#endif -#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==1) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==1) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==1) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==1) -void exti1_isr(void) -{ - exti_reset_request(EXTI1); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently) - uart_soft_receive_activity(); // check which GPIO changed -} -#endif -#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==2) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==2) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==2) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==2) -void exti2_isr(void) -{ - exti_reset_request(EXTI2); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently) - uart_soft_receive_activity(); // check which GPIO changed -} -#endif -#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==3) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==3) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==3) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==3) -void exti3_isr(void) -{ - exti_reset_request(EXTI3); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently) - uart_soft_receive_activity(); // check which GPIO changed -} -#endif -#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && UART_SOFT_RX_PIN0==4) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && UART_SOFT_RX_PIN1==4) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && UART_SOFT_RX_PIN2==4) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && UART_SOFT_RX_PIN3==4) -void exti4_isr(void) -{ - exti_reset_request(EXTI4); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently) - uart_soft_receive_activity(); // check which GPIO changed -} -#endif -#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && (UART_SOFT_RX_PIN0==5 || UART_SOFT_RX_PIN0==6 || UART_SOFT_RX_PIN0==7 || UART_SOFT_RX_PIN0==8 || UART_SOFT_RX_PIN0==9)) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && (UART_SOFT_RX_PIN1==5 || UART_SOFT_RX_PIN1==6 || UART_SOFT_RX_PIN1==7 || UART_SOFT_RX_PIN1==8 || UART_SOFT_RX_PIN1==9)) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && (UART_SOFT_RX_PIN2==5 || UART_SOFT_RX_PIN2==6 || UART_SOFT_RX_PIN2==7 || UART_SOFT_RX_PIN2==8 || UART_SOFT_RX_PIN2==9)) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && (UART_SOFT_RX_PIN3==5 || UART_SOFT_RX_PIN3==6 || UART_SOFT_RX_PIN3==7 || UART_SOFT_RX_PIN3==8 || UART_SOFT_RX_PIN3==9)) -void exti9_5_isr(void) -{ - exti_reset_request(EXTI5|EXTI6|EXTI7|EXTI8|EXTI9); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently) - uart_soft_receive_activity(); // check which GPIO changed -} -#endif -#if (defined(UART_SOFT_RX_PORT0) && defined(UART_SOFT_RX_PIN0) && (UART_SOFT_RX_PIN0==10 || UART_SOFT_RX_PIN0==11 || UART_SOFT_RX_PIN0==12 || UART_SOFT_RX_PIN0==13 || UART_SOFT_RX_PIN0==14 || UART_SOFT_RX_PIN0==15)) || (defined(UART_SOFT_RX_PORT1) && defined(UART_SOFT_RX_PIN1) && (UART_SOFT_RX_PIN1==10 || UART_SOFT_RX_PIN1==11 || UART_SOFT_RX_PIN1==12 || UART_SOFT_RX_PIN1==13 || UART_SOFT_RX_PIN1==14 || UART_SOFT_RX_PIN1==15)) || (defined(UART_SOFT_RX_PORT2) && defined(UART_SOFT_RX_PIN2) && (UART_SOFT_RX_PIN2==10 || UART_SOFT_RX_PIN2==11 || UART_SOFT_RX_PIN2==12 || UART_SOFT_RX_PIN2==13 || UART_SOFT_RX_PIN2==14 || UART_SOFT_RX_PIN2==15)) || (defined(UART_SOFT_RX_PORT3) && defined(UART_SOFT_RX_PIN3) && (UART_SOFT_RX_PIN3==10 || UART_SOFT_RX_PIN3==11 || UART_SOFT_RX_PIN3==12 || UART_SOFT_RX_PIN3==13 || UART_SOFT_RX_PIN3==14 || UART_SOFT_RX_PIN3==15)) -void exti15_10_isr(void) -{ - exti_reset_request(EXTI10|EXTI11|EXTI12|EXTI13|EXTI14|EXTI15); // clear interrupt flag for pin triggers this ISR (pin state will be checked independently) - uart_soft_receive_activity(); // check which GPIO changed -} -#endif - diff --git a/lib/uart_soft.h b/lib/uart_soft.h deleted file mode 100644 index 677ea8c..0000000 --- a/lib/uart_soft.h +++ /dev/null @@ -1,52 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to control up to 4 independent receive and transmit software UART ports (API) - * @file uart_soft.h - * @author King Kévin - * @date 2016 - * @note peripherals used: GPIO @ref uart_soft_gpio, timer @ref uart_soft_timer - */ - -/** if data has been received from UART port and is available to be read */ -extern volatile bool uart_soft_received[4]; - -/** setup software UART ports - * @param[in] rx_baudrates baud rates of the 4 UART RX ports (0 if unused) - * @param[in] tx_baudrates baud rates of the 4 UART TX ports (0 if unused) - * @return is setup succeeded, else the configuration is wrong - */ -bool uart_soft_setup(uint32_t *rx_baudrates, uint32_t *tx_baudrates); -/** get received byte from UART port - * @param[in] uart UART receive port to read byte from - * @return received byte (0 if no byte is available) - */ -uint8_t uart_soft_getbyte(uint8_t uart); -/** ensure all bytes are transmitted for the UART - * @param[in] uart UART port to flush - */ -void uart_soft_flush(uint8_t uart); -/** put byte in buffer to be transmitted on UART port - * @note blocking if buffer is full - * @param[in] uart UART port to transmit the byte from - * @param[in] byte byte to put in transmit buffer - */ -void uart_soft_putbyte_nonblocking(uint8_t uart, uint8_t byte); -/** transmit byte on UART port - * @note blocks until all buffered byte and this byte are transmitted - * @param[in] uart UART port to transmit the byte from - * @param[in] byte byte to transmit - */ -void uart_soft_putbyte_blocking(uint8_t uart, uint8_t byte); - diff --git a/lib/usart_enhanced.c b/lib/usart_enhanced.c deleted file mode 100644 index 9ca9b47..0000000 --- a/lib/usart_enhanced.c +++ /dev/null @@ -1,180 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library for enhanced USART communication (code) - * @file usart_enhanced.c - * @author King Kévin - * @date 2018 - * @details the USART peripherals only support 8 or 9-bit word and even or odd parity (included in the data bits). The library adds support for 5 to 8-bit words, none/even/odd/mark/space parity (on top of the data bits) - * @note since parity is handled in software, the parity error (PE) flag is unused and should be replaced by the value return by usart_enhanced_parity_error - * @remark 9-bit raw communication is not supported since this is not common and can be done without this library - */ -/* standard libraries */ -#include // standard integer types - -/* STM32 (including CM3) libraries */ -#include // USART utilities - -/* own libraries */ -#include "usart_enhanced.h" // utilities for USART enhancements - -/** number of available USART peripherals */ -#define USART_PERIPHERALS_NB 3 -/** configured enhanced USART word size */ -static uint8_t usart_enhanced_databits[USART_PERIPHERALS_NB]; -/** configured enhanced USART parity */ -static enum usart_enhanced_parity_t usart_enhanced_parity[USART_PERIPHERALS_NB]; -/** last enhanced USART parity error status */ -static bool usart_enhanced_parity_errors[USART_PERIPHERALS_NB]; - -const bool usart_enhanced_even_parity_lut[256] = { true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, true, false, false, true, false, true, true, false, true, false, false, true, false, true, true, false, false, true, true, false, true, false, false, true, }; - -/** get index of corresponding enhanced USART configurations - * @param[in] usart USART peripheral base address - * @return index used for the individual USART configurations - * @note the returned value is valid only is less than USART_PERIPHERALS_NB - */ -static uint8_t usart_enhanced_id(uint32_t usart) -{ - uint8_t to_return = USART_PERIPHERALS_NB; - switch (usart) { - case USART1: - to_return = 0; - break; - case USART2: - to_return = 1; - break; - case USART3: - to_return = 2; - break; - default: - to_return = USART_PERIPHERALS_NB; - break; - } - return to_return; -} - -bool usart_enhanced_config(uint32_t usart, uint8_t databits, enum usart_enhanced_parity_t parity) -{ - /* sanity check */ - uint8_t id = usart_enhanced_id(usart); - if (id>=USART_PERIPHERALS_NB) { - return false; - } - if (databits<5 || databits>8) { - return false; - } - if (parity>USART_ENHANCED_PARITY_SPACE) { - return false; - } - - // save configuration for later use - usart_enhanced_databits[id] = databits; - usart_enhanced_parity[id] = parity; - // configure USART peripheral - if (8 == databits && USART_ENHANCED_PARITY_NONE != parity) { // the parity bit is additional to the data bits - usart_set_databits(usart, 9); - } else { - usart_set_databits(usart, 8); - } - usart_set_parity(usart, USART_PARITY_NONE); // set no parity since we will take care of it ourselves - // we could also lower the number of stop bits when less than 8 bits are used, for higher throughput, but this is not a good idea since most UART transceiver parse 8 bits even is less is used - return true; -} - -void usart_enhanced_send(uint32_t usart, uint8_t data) -{ - /* sanity check */ - uint8_t id = usart_enhanced_id(usart); - if (id >= USART_PERIPHERALS_NB) { - return; - } - - data &= ~(0xff << usart_enhanced_databits[id]); // only keep the data bits - uint16_t output = data; // put value in output buffer - switch (usart_enhanced_parity[id]) { - case USART_ENHANCED_PARITY_NONE: // a mark is also decoded as idle/stop - case USART_ENHANCED_PARITY_MARK: - output |= (1 << usart_enhanced_databits[id]); // add idle state - break; - case USART_ENHANCED_PARITY_EVEN: - if (!usart_enhanced_even_parity_lut[data]) { - output |= (1 << usart_enhanced_databits[id]); - } - // no need to clear a bit if the parity is even - break; - case USART_ENHANCED_PARITY_ODD: - if (usart_enhanced_even_parity_lut[data]) { - output |= (1 << usart_enhanced_databits[id]); - } - // no need to clear a bit if the parity is odd - break; - case USART_ENHANCED_PARITY_SPACE: - // no need to clear the bit - break; - } - output |= (0xffff << (usart_enhanced_databits[id] + 1)); // set additional bits to idle (high) - usart_send(usart, output); // transmit character -} - -uint8_t usart_enhanced_recv(uint32_t usart) -{ - /* sanity check */ - uint8_t id = usart_enhanced_id(usart); - if (id >= USART_PERIPHERALS_NB) { - return 0xff; - } - - uint16_t input = usart_recv(usart); // read received character (also clears the error flags) - uint8_t data = input & ~(0xffff << usart_enhanced_databits[id]); // only keep the data bits - // check the parity - uint16_t parity = input & (1 << usart_enhanced_databits[id]); // only keep the parity bit - usart_enhanced_parity_errors[id] = false; - switch (usart_enhanced_parity[id]) { - case USART_ENHANCED_PARITY_NONE: - usart_enhanced_parity_errors[id] = false; - break; - case USART_ENHANCED_PARITY_EVEN: - if (parity) { - usart_enhanced_parity_errors[id] = !usart_enhanced_even_parity_lut[data]; - } else { - usart_enhanced_parity_errors[id] = usart_enhanced_even_parity_lut[data]; - } - break; - case USART_ENHANCED_PARITY_ODD: - if (parity) { - usart_enhanced_parity_errors[id] = usart_enhanced_even_parity_lut[data]; - } else { - usart_enhanced_parity_errors[id] = !usart_enhanced_even_parity_lut[data]; - } - break; - case USART_ENHANCED_PARITY_MARK: - usart_enhanced_parity_errors[id] = !parity; - break; - case USART_ENHANCED_PARITY_SPACE: - usart_enhanced_parity_errors[id] = parity; - break; - } - return data; -} - -bool usart_enhanced_parity_error(uint32_t usart) -{ - /* sanity check */ - uint8_t id = usart_enhanced_id(usart); - if (id >= USART_PERIPHERALS_NB) { - return false; - } - return usart_enhanced_parity_errors[id]; -} diff --git a/lib/usart_enhanced.h b/lib/usart_enhanced.h deleted file mode 100644 index 85bd363..0000000 --- a/lib/usart_enhanced.h +++ /dev/null @@ -1,77 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library for enhanced USART communication (API) - * @file usart_enhanced.h - * @author King Kévin - * @date 2018 - * @details the USART peripherals only support 8 or 9-bit word and even or odd parity (included in the data bits). The library adds support for 5 to 8-bit words, none/even/odd/mark/space parity (on top of the data bits) - * @note since parity is handled in software, the parity error (PE) flag is unused and should be replaced by the value return by usart_enhanced_parity_error - * @remark 9-bit raw communication is not supported since this is not common and can be done without this library - */ -#pragma once - -/** enhanced USART setting for the additional parity bit*/ -enum usart_enhanced_parity_t { - /** no parity */ - USART_ENHANCED_PARITY_NONE, - /** even parity - * @note the number of 1's is even - */ - USART_ENHANCED_PARITY_EVEN, - /** odd parity - * @note the number of 1's is odd - */ - USART_ENHANCED_PARITY_ODD, - /** mark parity - * @note the parity bit is 1 - */ - USART_ENHANCED_PARITY_MARK, - /** space parity - * @note the parity bit is 0 - */ - USART_ENHANCED_PARITY_SPACE, -}; - -/** know if there is an even number of 1's in a integer - * @note this look up table is only useful for up to 8-bit words, else use __builtin_parity - * @remark a look-up is a lot faster than making the calculation and doesn't use a lot of (flash) memory - */ -extern const bool usart_enhanced_even_parity_lut[256]; - -/** configure enhanced USART - * @param[in] usart USART peripheral base address - * @param[in] databits word size in bits (5 to 8) - * @param[in] parity additional parity bit - * @return if the input settings are valid and the configuration is successful - */ -bool usart_enhanced_config(uint32_t usart, uint8_t databits, enum usart_enhanced_parity_t parity); -/** send data over the enhanced USART using the configuration - * @param[in] usart USART peripheral base address - * @param[in] data data to be sent - * @note uses usart_send - */ -void usart_enhanced_send(uint32_t usart, uint8_t data); -/** receive data over the enhanced USART using the configuration - * @param[in] usart USART peripheral base address - * @return data received - * @note uses usart_recv - */ -uint8_t usart_enhanced_recv(uint32_t usart); -/** get the parity status of the received data - * @param[in] usart USART peripheral base address - * @return if there is a parity error - * @note the check only applies to the last data retrieved using usart_enhanced_recv - */ -bool usart_enhanced_parity_error(uint32_t usart); diff --git a/lib/vfd_hv518.c b/lib/vfd_hv518.c deleted file mode 100644 index 371ca8f..0000000 --- a/lib/vfd_hv518.c +++ /dev/null @@ -1,497 +0,0 @@ -/* This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ -/** library to drive vacuum fluorescent display using supertex HV518 shift register VFD drivers (code) - * @details the current configuration is for a VFD extracted from a Samsung SER-6500 cash register - * @file vfd_hv518.c - * @author King Kévin - * @date 2016 - * @note peripherals used: SPI @ref vfd_hv518_spi , GPIO @ref vfd_hv518_gpio , timer @ref vfd_hv518_timer - */ -/* standard libraries */ -#include // standard integer types -#include // general utilities - -/* STM32 (including CM3) libraries */ -#include // real-time control clock library -#include // general purpose input output library -#include // SPI library -#include // timer library -#include // interrupt handler - -#include "global.h" // global definitions -#include "vfd_hv518.h" // VFD library API - -/** @defgroup vfd_hv518_gpio GPIO to control supertex HV518 VFD drivers - * @{ - */ -#define VFD_PORT GPIOA /**< GPIO port */ -#define VFD_PORT_RCC RCC_GPIOA /**< GPIO port peripheral clock */ -#define VFD_STR GPIO6 /**< strobe pin to enable high voltage output, high voltage is output on low */ -#define VFD_NLE GPIO4 /**< latch enable pin, stores the shifted data on low, output the parallel data on high */ -/** @} */ - -/** @defgroup vfd_hv518_spi SPI to send data to supertex HV518 VFD drivers - * @{ - */ -#define VFD_SPI_RCC RCC_SPI1 /**< SPI peripheral */ -#define VFD_SPI_PORT GPIOA /**< GPIO port */ -#define VFD_SPI_PORT_RCC RCC_GPIOA /**< GPIO port peripheral clock */ -#define VFD_SPI_IRQ NVIC_SPI1_IRQ /**< SPI peripheral interrupt signal */ -#define VFD_SPI_ISR spi1_isr /**< SPI interrupt service routine */ -#define VFD_CLK GPIO_SPI1_SCK /**< clock signal */ -#define VFD_DIN GPIO_SPI1_MOSI /**< data input, where the data is shifted to */ -/** @} */ - -/** @defgroup vfd_hv518_timer timer for automatic display blocks refresh - * @{ - */ -#define VFD_TIMER_RCC RCC_TIM2 /**< timer peripheral clock */ -#define VFD_TIMER_IRQ NVIC_TIM2_IRQ /**< timer interrupt signal */ -#define VFD_TIMER_ISR tim2_isr /**< timer interrupt service routine */ -/** @} */ - -/** ASCII characters encoded for the 7 segments digit block - * @note starts with space - */ -static const uint8_t ascii_7segments[] = { - 0b00000000, // space - 0b00110000, // ! (I) - 0b00100010, // " - 0b01011100, // # (o) - 0b01101101, // $ (s) - 0b01010010, // % (/) - 0b01111101, // & (6) - 0b00100000, // ' - 0b00111001, // ( ([) - 0b00001111, // ) - 0b01110000, // * - 0b01000110, // + - 0b00010000, // , - 0b01000000, // - - 0b00010000, // . (,) - 0b01010010, // / - 0b00111111, // 0 - 0b00000110, // 1 - 0b01011011, // 2 - 0b01001111, // 3 - 0b01100110, // 4 - 0b01101101, // 5 - 0b01111101, // 6 - 0b00000111, // 7 - 0b01111111, // 8 - 0b01101111, // 9 - 0b01001000, // : (=) - 0b01001000, // ; (=) - 0b01011000, // < - 0b01001000, // = - 0b01001100, // > - 0b01010011, // ? - 0b01111011, // @ - 0b01110111, // A - 0b01111111, // B - 0b00111001, // C - 0b01011110, // D - 0b01111001, // E - 0b01110001, // F - 0b00111101, // G - 0b01110110, // H - 0b00110000, // I - 0b00011110, // J - 0b01110110, // K - 0b00111000, // L - 0b00110111, // M - 0b00110111, // N - 0b00111111, // O - 0b01110011, // P - 0b01101011, // Q - 0b00110011, // R - 0b01101101, // S - 0b01111000, // T - 0b00111110, // U - 0b00111110, // V (U) - 0b00111110, // W (U) - 0b01110110, // X (H) - 0b01101110, // Y - 0b01011011, // Z - 0b00111001, // [ - 0b01100100, // '\' - 0b00001111, // / - 0b00100011, // ^ - 0b00001000, // _ - 0b00000010, // ` - 0b01011111, // a - 0b01111100, // b - 0b01011000, // c - 0b01011110, // d - 0b01111011, // e - 0b01110001, // f - 0b01101111, // g - 0b01110100, // h - 0b00010000, // i - 0b00001100, // j - 0b01110110, // k - 0b00110000, // l - 0b01010100, // m - 0b01010100, // n - 0b01011100, // o - 0b01110011, // p - 0b01100111, // q - 0b01010000, // r - 0b01101101, // s - 0b01111000, // t - 0b00011100, // u - 0b00011100, // v (u) - 0b00011100, // w (u) - 0b01110110, // x - 0b01101110, // y - 0b01011011, // z - 0b00111001, // { ([) - 0b00110000, // | - 0b00001111, // } ([) - 0b01000000, // ~ -}; - -/** font for the 5x7 dot matrix block - * @details first value is left-most line, LSB is top dot, MSB is not used - * @note from http://sunge.awardspace.com/glcd-sd/node4.html - */ -static const uint8_t font5x7[][5] = { - {0x00, 0x00, 0x00, 0x00, 0x00}, // (space) - {0x00, 0x00, 0x5F, 0x00, 0x00}, // ! - {0x00, 0x07, 0x00, 0x07, 0x00}, // " - {0x14, 0x7F, 0x14, 0x7F, 0x14}, // # - {0x24, 0x2A, 0x7F, 0x2A, 0x12}, // $ - {0x23, 0x13, 0x08, 0x64, 0x62}, // % - {0x36, 0x49, 0x55, 0x22, 0x50}, // & - {0x00, 0x05, 0x03, 0x00, 0x00}, // ' - {0x00, 0x1C, 0x22, 0x41, 0x00}, // ( - {0x00, 0x41, 0x22, 0x1C, 0x00}, // ) - {0x08, 0x2A, 0x1C, 0x2A, 0x08}, // * - {0x08, 0x08, 0x3E, 0x08, 0x08}, // + - {0x00, 0x50, 0x30, 0x00, 0x00}, // , - {0x08, 0x08, 0x08, 0x08, 0x08}, // - - {0x00, 0x60, 0x60, 0x00, 0x00}, // . - {0x20, 0x10, 0x08, 0x04, 0x02}, // / - {0x3E, 0x51, 0x49, 0x45, 0x3E}, // 0 - {0x00, 0x42, 0x7F, 0x40, 0x00}, // 1 - {0x42, 0x61, 0x51, 0x49, 0x46}, // 2 - {0x21, 0x41, 0x45, 0x4B, 0x31}, // 3 - {0x18, 0x14, 0x12, 0x7F, 0x10}, // 4 - {0x27, 0x45, 0x45, 0x45, 0x39}, // 5 - {0x3C, 0x4A, 0x49, 0x49, 0x30}, // 6 - {0x01, 0x71, 0x09, 0x05, 0x03}, // 7 - {0x36, 0x49, 0x49, 0x49, 0x36}, // 8 - {0x06, 0x49, 0x49, 0x29, 0x1E}, // 9 - {0x00, 0x36, 0x36, 0x00, 0x00}, // : - {0x00, 0x56, 0x36, 0x00, 0x00}, // ; - {0x00, 0x08, 0x14, 0x22, 0x41}, // < - {0x14, 0x14, 0x14, 0x14, 0x14}, // = - {0x41, 0x22, 0x14, 0x08, 0x00}, // > - {0x02, 0x01, 0x51, 0x09, 0x06}, // ? - {0x32, 0x49, 0x79, 0x41, 0x3E}, // @ - {0x7E, 0x11, 0x11, 0x11, 0x7E}, // A - {0x7F, 0x49, 0x49, 0x49, 0x36}, // B - {0x3E, 0x41, 0x41, 0x41, 0x22}, // C - {0x7F, 0x41, 0x41, 0x22, 0x1C}, // D - {0x7F, 0x49, 0x49, 0x49, 0x41}, // E - {0x7F, 0x09, 0x09, 0x01, 0x01}, // F - {0x3E, 0x41, 0x41, 0x51, 0x32}, // G - {0x7F, 0x08, 0x08, 0x08, 0x7F}, // H - {0x00, 0x41, 0x7F, 0x41, 0x00}, // I - {0x20, 0x40, 0x41, 0x3F, 0x01}, // J - {0x7F, 0x08, 0x14, 0x22, 0x41}, // K - {0x7F, 0x40, 0x40, 0x40, 0x40}, // L - {0x7F, 0x02, 0x04, 0x02, 0x7F}, // M - {0x7F, 0x04, 0x08, 0x10, 0x7F}, // N - {0x3E, 0x41, 0x41, 0x41, 0x3E}, // O - {0x7F, 0x09, 0x09, 0x09, 0x06}, // P - {0x3E, 0x41, 0x51, 0x21, 0x5E}, // Q - {0x7F, 0x09, 0x19, 0x29, 0x46}, // R - {0x46, 0x49, 0x49, 0x49, 0x31}, // S - {0x01, 0x01, 0x7F, 0x01, 0x01}, // T - {0x3F, 0x40, 0x40, 0x40, 0x3F}, // U - {0x1F, 0x20, 0x40, 0x20, 0x1F}, // V - {0x7F, 0x20, 0x18, 0x20, 0x7F}, // W - {0x63, 0x14, 0x08, 0x14, 0x63}, // X - {0x03, 0x04, 0x78, 0x04, 0x03}, // Y - {0x61, 0x51, 0x49, 0x45, 0x43}, // Z - {0x00, 0x00, 0x7F, 0x41, 0x41}, // [ - {0x02, 0x04, 0x08, 0x10, 0x20}, // '\' - {0x41, 0x41, 0x7F, 0x00, 0x00}, // ] - {0x04, 0x02, 0x01, 0x02, 0x04}, // ^ - {0x40, 0x40, 0x40, 0x40, 0x40}, // _ - {0x00, 0x01, 0x02, 0x04, 0x00}, // ` - {0x20, 0x54, 0x54, 0x54, 0x78}, // a - {0x7F, 0x48, 0x44, 0x44, 0x38}, // b - {0x38, 0x44, 0x44, 0x44, 0x20}, // c - {0x38, 0x44, 0x44, 0x48, 0x7F}, // d - {0x38, 0x54, 0x54, 0x54, 0x18}, // e - {0x08, 0x7E, 0x09, 0x01, 0x02}, // f - {0x08, 0x14, 0x54, 0x54, 0x3C}, // g - {0x7F, 0x08, 0x04, 0x04, 0x78}, // h - {0x00, 0x44, 0x7D, 0x40, 0x00}, // i - {0x20, 0x40, 0x44, 0x3D, 0x00}, // j - {0x00, 0x7F, 0x10, 0x28, 0x44}, // k - {0x00, 0x41, 0x7F, 0x40, 0x00}, // l - {0x7C, 0x04, 0x18, 0x04, 0x78}, // m - {0x7C, 0x08, 0x04, 0x04, 0x78}, // n - {0x38, 0x44, 0x44, 0x44, 0x38}, // o - {0x7C, 0x14, 0x14, 0x14, 0x08}, // p - {0x08, 0x14, 0x14, 0x18, 0x7C}, // q - {0x7C, 0x08, 0x04, 0x04, 0x08}, // r - {0x48, 0x54, 0x54, 0x54, 0x20}, // s - {0x04, 0x3F, 0x44, 0x40, 0x20}, // t - {0x3C, 0x40, 0x40, 0x20, 0x7C}, // u - {0x1C, 0x20, 0x40, 0x20, 0x1C}, // v - {0x3C, 0x40, 0x30, 0x40, 0x3C}, // w - {0x44, 0x28, 0x10, 0x28, 0x44}, // x - {0x0C, 0x50, 0x50, 0x50, 0x3C}, // y - {0x44, 0x64, 0x54, 0x4C, 0x44}, // z - {0x00, 0x08, 0x36, 0x41, 0x00}, // { - {0x00, 0x00, 0x7F, 0x00, 0x00}, // | - {0x00, 0x41, 0x36, 0x08, 0x00}, // } - {0b00001000, 0b00000100, 0b00001100, 0b00001000, 0b00000100} // ~ -}; - -/** pictures for the 5x7 dot matrix block - * @details first value is left-most line, LSB is top dot, MSB is not used - */ -static const uint8_t pict5x7[][5] = { - {0x08, 0x08, 0x2A, 0x1C, 0x08}, // -> - {0x08, 0x1C, 0x2A, 0x08, 0x08}, // <- - {0b01110000, 0b01110000, 0b01111010, 0b01111100, 0b01011000}, // bunny side 1 - {0b00100000, 0b01110000, 0b01110010, 0b01111100, 0b01011000}, // bunny side 2 - {0b00111110, 0b01001001, 0b01010110, 0b01001001, 0b00111110}, // bunny face 1 - {0b00111110, 0b01010001, 0b01100110, 0b01010001, 0b00111110}, // bunny face 2 - {0b00111000, 0b01010111, 0b01100100, 0b01010111, 0b00111000}, // bunny face 3 - {0b00111000, 0b01001111, 0b01010100, 0b01001111, 0b00111000}, // bunny face 4 - {0b00111000, 0b01011110, 0b01101000, 0b01011110, 0b00111000}, // bunny face 5 - {0b01000001, 0b00110110, 0b00001000, 0b00110110, 0b01000001}, // cross 1 - {~0b01000001, ~0b00110110, ~0b00001000, ~0b00110110, ~0b01000001}, // cross 1 negated - {0b00100010, 0b00010100, 0b00001000, 0b00010100, 0b00100010}, // cross 2 - {~0b00100010, ~0b00010100, ~0b00001000, ~0b00010100, ~0b00100010}, // cross 2 negated - {0x00, 0x00, 0x00, 0x00, 0x00} // nothing -}; - -/** the 32 bits values to be shifted out to the VFD driver - * @note split into 16 bit for SPI transfer - * @note since the bits for digits and matrix are independent, they can be combined - * @note we have more matrix (12) than digits (10) - */ -static uint16_t driver_data[VFD_MATRIX][VFD_DRIVERS*2] = {0}; -/** which driver data is being transmitted */ -static volatile uint8_t spi_i = 0; -/** which grid/part to activate - * @note digits and matrix can be combined - */ -static volatile uint8_t vfd_grid = 0; -/** the bits used for selecting then digit and 7 segment anodes - * @note for the second driver - */ -static const uint32_t digit_mask = 0x00fffff0; - -void vfd_digit(uint8_t nb, char c) -{ - if (!(nb=' ') { // only take printable characters - uint8_t i = c-' '; // get index for character - if (i>16); // write back data (most significant half) -} - -void vfd_matrix(uint8_t nb, char c) -{ - // check the matrix exists - if (!(nb=' ')) { // only take printable characters - uint8_t i = c-' '; // get index for character - if (i0x7f) { // the non ASCII character are used for pictures - uint8_t i = c-0x80; // get index for character - if (i>16; - } -} - -void vfd_clear(void) -{ - for (uint8_t i=0; i. - * - */ -/** library to drive vacuum fluorescent display using supertex HV518 shift register VFD drivers (API) - * @details the current configuration is for a VFD extracted from a Samsung SER-6500 cash register - * @file vfd_hv518.h - * @author King Kévin - * @date 2016 - * @note peripherals used: USART @ref usart - */ - -/** number HV518 VFD drivers */ -#define VFD_DRIVERS 3 -/** number of digits blocks on SER-6500 VFD */ -#define VFD_DIGITS 10 -/** number of dot matrix blocks on SER-6500 VFD */ -#define VFD_MATRIX 12 - -/** set character to digit block - * @param[in] nb digit block to set - * @param[in] c ASCII character to set - * @note use the MSB of @p nb to enable the dot - */ -void vfd_digit(uint8_t nb, char c); -/** set character to matrix block - * @param[in] nb matrix block to set - * @param[in] c ASCII character to set - * @note on ASCII characters are used for pictures - */ -void vfd_matrix(uint8_t nb, char c); -/** clear VFD display */ -void vfd_clear(void); -/** test VFD display (light up all segments) */ -void vfd_test(void); -/** switch VFD on */ -void vfd_on(void); -/** switch VFD display off */ -void vfd_off(void); -/** setup VFD */ -void vfd_setup(void);