Started working on the fw. Created LTR329 light sensor library. Fixed the error in SHT4x library.
This commit is contained in:
		
							
								
								
									
										145
									
								
								fw/Core/Inc/config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								fw/Core/Inc/config.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,145 @@ | ||||
| /* | ||||
|  * config.h | ||||
|  * | ||||
|  *  Created on: Sep 5, 2021 | ||||
|  *      Author: david | ||||
|  */ | ||||
|  | ||||
| #ifndef INC_CONFIG_H_ | ||||
| #define INC_CONFIG_H_ | ||||
|  | ||||
| /* TODO: add comments to everything */ | ||||
|  | ||||
| /* EXAMPLE of USAGE */ | ||||
| /* | ||||
| config_t new_config; | ||||
| new_config.led_co2_alert_limit1 = 1000; | ||||
| new_config.led_co2_alert_limit2 = 2000; | ||||
| new_config.led_on = 1; | ||||
| new_config.modbus_addr = 0x11; | ||||
| config_write(&new_config); | ||||
|  | ||||
| config_t config; | ||||
| config_read(&config); | ||||
| */ | ||||
|  | ||||
| #include "stdint.h" | ||||
| #include "stm32l0xx.h" | ||||
| /* DESCRIPTION OF THE DATA STRUCTURE */ | ||||
| /* | ||||
|  * Data are divided into two groups: | ||||
|  * A) DEVICE DESCRIPTION | ||||
|  * 		Can not be changed by the user. | ||||
|  * 		These data can be only read. | ||||
|  * 		These data are: | ||||
|  * 		* VENDOR NAME | ||||
|  * 		* PRODUCT CODE | ||||
|  * 		* PRODUCT NAME | ||||
|  * 		* REVISION | ||||
|  * 		* SERIAL NUMBER | ||||
|  * B) DEVICE CONFIGURATION | ||||
|  * 		Can be changed by the user. | ||||
|  * 		These data are: | ||||
|  * 		* MODBUS ADDRESS - Modbus Address of the device. Default is 254 | ||||
|  * 		* LED ON - Whether the CO2 Level Indication LED should be on or off | ||||
|  * 		* LED CO2 ALERT LIMIT 1 - CO2 Level when the LED color changes Green<->Yellow | ||||
|  * 		* LED CO2 ALERT LIMIT 2 - CO2 Level when the LED color changes Yellow<->Red | ||||
|  * | ||||
|  * Device description data can be accessed using direct readout from the memory | ||||
|  * Device configuration data can be accessed using config_t struct. | ||||
|  */ | ||||
| #define CONFIG_DEFAULT_LED_ON					1 | ||||
| #define CONFIG_DEFAULT_LED_BRIGHTNESS			100 | ||||
| #define CONFIG_DEFAULT_LED_ALERT1_LIMIT			1500 | ||||
| #define CONFIG_DEFAULT_LED_ALERT2_LIMIT			3000 | ||||
| #define CONFIG_DEFAULT_LED_SMOOTH				1 | ||||
| #define CONFIG_DEFAULT_SCD4x_T_OFFSET			0 | ||||
| #define CONFIG_DEFAULT_BAUDRATE_INDEX			0 | ||||
|  | ||||
| #define	CONFIG_MODBUS_ADDR_LENGTH				2 | ||||
| #define CONFIG_BAUDRATE_INDEX_LENGTH			2 | ||||
| #define CONFIG_LED_ON_LENGTH					2 | ||||
| #define CONFIG_LED_BRIGHTNESS_LENGTH			2 | ||||
| #define CONFIG_LED_SMOOTH_LENGTH				2 | ||||
| #define CONFIG_LED_ALERT1_LENGTH				2 | ||||
| #define CONFIG_LED_ALERT2_LENGTH				2 | ||||
| #define CONFIG_LED_ALERT2_LENGTH				2 | ||||
| #define CONFIG_SCD4x_T_OFFSET_LENGTH			2 | ||||
| #define VENDOR_NAME_LENGTH						64 | ||||
| #define	PRODUCT_CODE_LENGTH						64 | ||||
| #define	PRODUCT_NAME_LENGTH						64 | ||||
| #define REVISION_LENGTH							16 | ||||
| #define SERIAL_NUMBER_LENGTH					64 | ||||
|  | ||||
| #define EEPROM_EMPTY_BYTE						0x00 | ||||
|  | ||||
| #define EEPROM_ADDR_START 						((uint32_t)0x08080000) | ||||
| #define EEPROM_ADDR_END 						((uint32_t)0x080803FF) | ||||
|  | ||||
| #define CONFIG_EEPROM_ADDR_MODBUS_ADDR			EEPROM_ADDR_START | ||||
| #define CONFIG_EEPROM_ADDR_BAUDRATE_INDEX		(CONFIG_EEPROM_ADDR_MODBUS_ADDR 		+ CONFIG_MODBUS_ADDR_LENGTH) | ||||
| #define CONFIG_EEPROM_ADDR_LED_ON				(CONFIG_EEPROM_ADDR_BAUDRATE_INDEX 		+ CONFIG_BAUDRATE_INDEX_LENGTH) | ||||
| #define CONFIG_EEPROM_ADDR_LED_BRIGHTNESS		(CONFIG_EEPROM_ADDR_LED_ON				+ CONFIG_LED_ON_LENGTH) | ||||
| #define CONFIG_EEPROM_ADDR_LED_SMOOTH			(CONFIG_EEPROM_ADDR_LED_BRIGHTNESS		+ CONFIG_LED_BRIGHTNESS_LENGTH) | ||||
| #define CONFIG_EEPROM_ADDR_LED_ALERT1			(CONFIG_EEPROM_ADDR_LED_SMOOTH			+ CONFIG_LED_SMOOTH_LENGTH) | ||||
| #define CONFIG_EEPROM_ADDR_LED_ALERT2			(CONFIG_EEPROM_ADDR_LED_ALERT1 			+ CONFIG_LED_ALERT1_LENGTH) | ||||
| #define CONFIG_EEPROM_ADDR_SCD4x_T_OFFSET		(CONFIG_EEPROM_ADDR_LED_ALERT2 			+ CONFIG_LED_ALERT2_LENGTH) | ||||
|  | ||||
| #define CONFIG_EEPROM_ADDR_VENDOR_NAME			(CONFIG_EEPROM_ADDR_SCD4x_T_OFFSET 		+ CONFIG_SCD4x_T_OFFSET_LENGTH) | ||||
| #define CONFIG_EEPROM_ADDR_PRODUCT_CODE			(CONFIG_EEPROM_ADDR_VENDOR_NAME 		+ VENDOR_NAME_LENGTH) | ||||
| #define CONFIG_EEPROM_ADDR_PRODUCT_NAME			(CONFIG_EEPROM_ADDR_PRODUCT_CODE 		+ PRODUCT_CODE_LENGTH) | ||||
| #define CONFIG_EEPROM_ADDR_REVISION				(CONFIG_EEPROM_ADDR_PRODUCT_NAME 		+ PRODUCT_NAME_LENGTH) | ||||
| #define CONFIG_EEPROM_ADDR_SERIAL_NUMBER		(CONFIG_EEPROM_ADDR_REVISION			+ REVISION_LENGTH) | ||||
|  | ||||
| #define FLASH_PEKEY1							((uint32_t)0x89ABCDEF) | ||||
| #define FLASH_PEKEY2							((uint32_t)0x02030405) | ||||
|  | ||||
| #define CONFIG_OK								0 | ||||
| #define CONFIG_ERROR							-1 | ||||
|  | ||||
| #define EEPROM_OK								0 | ||||
| #define EEPROM_ERROR							-1 | ||||
| #define EEPROM_UNLOCK_ERROR						-2 | ||||
| #define EEPROM_LOCK_ERROR						-3 | ||||
| #define EEPROM_WRITE_ERROR						-4 | ||||
| #define EEPROM_ADDR_ERROR						-5 | ||||
|  | ||||
| #define SYSTICK_FREQ_HZ							12000000 | ||||
| #define EEPROM_TIMEOUT_MAX_MS_INV				200 | ||||
|  | ||||
| /* | ||||
|  * Variables | ||||
|  */ | ||||
|  | ||||
| extern const uint32_t config_baudrates[]; | ||||
| extern const uint8_t config_baudrates_length; | ||||
|  | ||||
| /* | ||||
|  * Type definitions | ||||
|  */ | ||||
|  | ||||
| typedef struct | ||||
| { | ||||
| 	/* LED CONFIG */ | ||||
| 	uint8_t led_on; | ||||
| 	uint16_t led_brightness; | ||||
| 	uint8_t led_smooth; | ||||
| 	uint16_t led_co2_alert_limit1; | ||||
| 	uint16_t led_co2_alert_limit2; | ||||
|  | ||||
| 	/* SCD4x Temperature sensor offset */ | ||||
| 	int16_t scd4x_t_offset; | ||||
|  | ||||
| 	/* MODBUS CONFIG */ | ||||
| 	uint16_t modbus_addr; | ||||
| 	uint32_t baudrate_index; | ||||
| } config_t; | ||||
|  | ||||
|  | ||||
| /* const uint32_t baudrates [] = {19200, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 57600, 76800, 115200}; */ | ||||
|  | ||||
| int8_t config_read(config_t *config); | ||||
| int8_t config_write(config_t *config); | ||||
|  | ||||
| #endif /* INC_CONFIG_H_ */ | ||||
|  | ||||
							
								
								
									
										22
									
								
								fw/Core/Inc/crc8.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								fw/Core/Inc/crc8.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| /* | ||||
|  * crc.h | ||||
|  * | ||||
|  *  Created on: Jun 9, 2021 | ||||
|  *      Author: user | ||||
|  */ | ||||
|  | ||||
| #ifndef INC_CRC8_H_ | ||||
| #define INC_CRC8_H_ | ||||
|  | ||||
| #include "stdint.h" | ||||
|  | ||||
| /* | ||||
|  * Definitions & macros | ||||
|  */ | ||||
|  | ||||
| #define CRC8_POLYNOMIAL ((uint8_t)0x31) | ||||
| #define CRC8_INIT ((uint8_t)0xFF) | ||||
|  | ||||
| uint8_t crc8_calculate(const uint8_t *data, uint16_t count); | ||||
|  | ||||
| #endif /* INC_CRC8_H_ */ | ||||
							
								
								
									
										46
									
								
								fw/Core/Inc/i2c.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								fw/Core/Inc/i2c.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| /* | ||||
|  * i2c.h | ||||
|  * | ||||
|  *  Created on: Jun 8, 2021 | ||||
|  *      Author: user | ||||
|  */ | ||||
|  | ||||
| #ifndef INC_I2C_H_ | ||||
| #define INC_I2C_H_ | ||||
|  | ||||
| #include "stdint.h" | ||||
| #include "stm32l0xx_ll_i2c.h" | ||||
|  | ||||
| /* | ||||
|  * Defines & macros | ||||
|  */ | ||||
|  | ||||
| #define NULL 0 | ||||
|  | ||||
| /* | ||||
|  * Return values for I2C functions | ||||
|  */ | ||||
|  | ||||
| #define I2C_OK 0 | ||||
| #define I2C_ERROR -1 // generic error | ||||
| #define I2C_ERROR_NACK -2 // NACK was received during transfer | ||||
| #define I2C_ERROR_TX_INCOMPLETE -3 // number of TXed bytes != buffer length | ||||
| #define I2C_ERROR_RX_INCOMPLETE -4 // number of RXed bytes != buffer length | ||||
|  | ||||
| /* | ||||
|  * Type definitions | ||||
|  */ | ||||
|  | ||||
| typedef struct { | ||||
| 	I2C_TypeDef *i2c; | ||||
| } i2c_context_t; | ||||
|  | ||||
| /* | ||||
|  * Function declarations | ||||
|  */ | ||||
|  | ||||
| int8_t i2c_init(i2c_context_t *context); | ||||
| int8_t i2c_transmit(uint8_t address, uint8_t *buffer, int len); | ||||
| int8_t i2c_receive(uint8_t address, uint8_t *buffer, int len); | ||||
|  | ||||
| #endif /* INC_I2C_H_ */ | ||||
							
								
								
									
										93
									
								
								fw/Core/Inc/ltr329.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								fw/Core/Inc/ltr329.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,93 @@ | ||||
| /* | ||||
|  * ltr329.h | ||||
|  * | ||||
|  *  Created on: May 22, 2022 | ||||
|  *      Author: david | ||||
|  */ | ||||
|  | ||||
| #ifndef INC_LTR329_H_ | ||||
| #define INC_LTR329_H_ | ||||
|  | ||||
| #include "stdint.h" | ||||
| #include "stm32l0xx_ll_i2c.h" | ||||
| #include "stm32l0xx_ll_utils.h" | ||||
| #include "i2c.h" | ||||
|  | ||||
| #define LTR329_I2C_ADDRESS 0x29 | ||||
|  | ||||
| /* | ||||
|  * Return values | ||||
|  */ | ||||
|  | ||||
| #define LTR329_OK 0 | ||||
| #define LTR329_ERROR -1 // generic error | ||||
|  | ||||
| /* LTR-329ALS-01 Registers */ | ||||
| /* Datasheet: https://optoelectronics.liteon.com/upload/download/DS86-2014-0006/LTR-329ALS-01_DS_V1.pdf */ | ||||
| typedef enum | ||||
| { | ||||
| 	LTR329_ALS_CONTR 			= 0x80,	/* RW */ | ||||
| 	LTR329_ALS_MEAS_RATE 		= 0x85, /* RW */ | ||||
| 	LTR329_PART_ID 				= 0x86, /* R */ | ||||
| 	LTR329_MANUFAC_ID 			= 0x87, /* R */ | ||||
| 	LTR329_ALS_DATA_CH1_0 		= 0x88, /* R */ | ||||
| 	LTR329_ALS_DATA_CH1_1 		= 0x89, /* R */ | ||||
| 	LTR329_ALS_DATA_CH0_0 		= 0x8A, /* R */ | ||||
| 	LTR329_ALS_DATA_CH0_1 		= 0x8B, /* R */ | ||||
| 	LTR329_ALS_STATUS 			= 0x8C /* R */ | ||||
| } ltr329_cmd_t; | ||||
|  | ||||
| /* Bit masks for ALS Mode */ | ||||
| typedef enum | ||||
| { | ||||
| 	LTR329_MODE_STAND_BY		= 0b00000000, /* DEFAULT */ | ||||
| 	LTR329_MODE_ACTIVE			= 0b00000001 | ||||
| } ltr329_als_mode_t; | ||||
|  | ||||
| /* Bit masks for Gain settings */ | ||||
| typedef enum | ||||
| { | ||||
| 	LTR329_GAIN_1X 				= 0b00000000, /* DEFAULT */ | ||||
| 	LTR329_GAIN_2X				= 0b00000100, | ||||
| 	LTR329_GAIN_4X				= 0b00001000, | ||||
| 	LTR329_GAIN_8X				= 0b00001100, | ||||
| 	LTR329_GAIN_48X				= 0b00011000, | ||||
| 	LTR329_GAIN_96X				= 0b00011100, | ||||
| 	LTR329_GAIN_RESERVED1		= 0b00010000, | ||||
| 	LTR329_GAIN_RESERVED2		= 0b00010100 | ||||
| } ltr329_gain_t; | ||||
|  | ||||
| /* Bit masks for Integration Time settings */ | ||||
| typedef enum | ||||
| { | ||||
| 	LTR329_INTEGRATION_50MS		= 0b00001000, | ||||
| 	LTR329_INTEGRATION_100MS 	= 0b00000000, /* DEFAULT */ | ||||
| 	LTR329_INTEGRATION_150MS	= 0b00100000, | ||||
| 	LTR329_INTEGRATION_200MS	= 0b00010000, | ||||
| 	LTR329_INTEGRATION_250MS	= 0b00101000, | ||||
| 	LTR329_INTEGRATION_300MS	= 0b00110000, | ||||
| 	LTR329_INTEGRATION_350MS	= 0b00111000, | ||||
| 	LTR329_INTEGRATION_400MS	= 0b00011000 | ||||
| } ltr329_integration_time_t; | ||||
|  | ||||
| /* Bit masks for Measurement Rate settings */ | ||||
| typedef enum | ||||
| { | ||||
| 	LTR329_MEAS_RATE_50MS		= 0b00000000, | ||||
| 	LTR329_MEAS_RATE_100MS		= 0b00000001, | ||||
| 	LTR329_MEAS_RATE_200MS		= 0b00000010, | ||||
| 	LTR329_MEAS_RATE_500MS		= 0b00000011, /* DEFAULT */ | ||||
| 	LTR329_MEAS_RATE_1000MS		= 0b00000100, | ||||
| 	LTR329_MEAS_RATE_2000MS		= 0b00000111 | ||||
| } ltr329_measurement_rate_t; | ||||
|  | ||||
| static int8_t ltr329_read_register (ltr329_cmd_t register_addr, uint8_t *register_data ); | ||||
| static int8_t ltr329_write_register (ltr329_cmd_t register_addr, uint8_t register_data); | ||||
| int8_t ltr329_write_settings (ltr329_gain_t gain, ltr329_als_mode_t mode, ltr329_integration_time_t integ_time, ltr329_measurement_rate_t meas_rate); | ||||
| int8_t ltr329_read_settings (ltr329_gain_t *gain, ltr329_als_mode_t *mode, ltr329_integration_time_t *integ_time, ltr329_measurement_rate_t *meas_rate); | ||||
| int8_t ltr329_sw_reset( void ); | ||||
| int8_t ltr329_measure (uint16_t *data_ch0, uint16_t *data_ch1); | ||||
| int8_t ltr329_read_status_register(uint8_t *data_valid, uint8_t *new_data, ltr329_gain_t *gain); | ||||
| int8_t ltr329_read_device_info (uint8_t *manufacturer_id, uint8_t *part_id); | ||||
|  | ||||
| #endif /* INC_LTR329_H_ */ | ||||
							
								
								
									
										118
									
								
								fw/Core/Inc/main.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								fw/Core/Inc/main.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,118 @@ | ||||
| /* USER CODE BEGIN Header */ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * @file           : main.h | ||||
|   * @brief          : Header for main.c file. | ||||
|   *                   This file contains the common defines of the application. | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under BSD 3-Clause license, | ||||
|   * the "License"; You may not use this file except in compliance with the | ||||
|   * License. You may obtain a copy of the License at: | ||||
|   *                        opensource.org/licenses/BSD-3-Clause | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
| /* USER CODE END Header */ | ||||
|  | ||||
| /* Define to prevent recursive inclusion -------------------------------------*/ | ||||
| #ifndef __MAIN_H | ||||
| #define __MAIN_H | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| extern "C" { | ||||
| #endif | ||||
|  | ||||
| /* Includes ------------------------------------------------------------------*/ | ||||
| #include "stm32l0xx_ll_i2c.h" | ||||
| #include "stm32l0xx_ll_iwdg.h" | ||||
| #include "stm32l0xx_ll_crs.h" | ||||
| #include "stm32l0xx_ll_rcc.h" | ||||
| #include "stm32l0xx_ll_bus.h" | ||||
| #include "stm32l0xx_ll_system.h" | ||||
| #include "stm32l0xx_ll_exti.h" | ||||
| #include "stm32l0xx_ll_cortex.h" | ||||
| #include "stm32l0xx_ll_utils.h" | ||||
| #include "stm32l0xx_ll_pwr.h" | ||||
| #include "stm32l0xx_ll_dma.h" | ||||
| #include "stm32l0xx_ll_tim.h" | ||||
| #include "stm32l0xx_ll_usart.h" | ||||
| #include "stm32l0xx_ll_gpio.h" | ||||
|  | ||||
| #if defined(USE_FULL_ASSERT) | ||||
| #include "stm32_assert.h" | ||||
| #endif /* USE_FULL_ASSERT */ | ||||
|  | ||||
| /* Private includes ----------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN Includes */ | ||||
| #include "i2c.h" | ||||
| #include "sht4x.h" | ||||
| #include "ltr329.h" | ||||
| #include "modbus.h" | ||||
| #include "config.h" | ||||
| /* USER CODE END Includes */ | ||||
|  | ||||
| /* Exported types ------------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN ET */ | ||||
|  | ||||
| /* USER CODE END ET */ | ||||
|  | ||||
| /* Exported constants --------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN EC */ | ||||
|  | ||||
| /* USER CODE END EC */ | ||||
|  | ||||
| /* Exported macro ------------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN EM */ | ||||
|  | ||||
| /* USER CODE END EM */ | ||||
|  | ||||
| /* Exported functions prototypes ---------------------------------------------*/ | ||||
| void Error_Handler(void); | ||||
|  | ||||
| /* USER CODE BEGIN EFP */ | ||||
| int8_t uart_disable_interrupts(void); | ||||
| int8_t uart_enable_interrupts(void); | ||||
|  | ||||
| /* USER CODE END EFP */ | ||||
|  | ||||
| /* Private defines -----------------------------------------------------------*/ | ||||
| #ifndef NVIC_PRIORITYGROUP_0 | ||||
| #define NVIC_PRIORITYGROUP_0         ((uint32_t)0x00000007) /*!< 0 bit  for pre-emption priority, | ||||
|                                                                  4 bits for subpriority */ | ||||
| #define NVIC_PRIORITYGROUP_1         ((uint32_t)0x00000006) /*!< 1 bit  for pre-emption priority, | ||||
|                                                                  3 bits for subpriority */ | ||||
| #define NVIC_PRIORITYGROUP_2         ((uint32_t)0x00000005) /*!< 2 bits for pre-emption priority, | ||||
|                                                                  2 bits for subpriority */ | ||||
| #define NVIC_PRIORITYGROUP_3         ((uint32_t)0x00000004) /*!< 3 bits for pre-emption priority, | ||||
|                                                                  1 bit  for subpriority */ | ||||
| #define NVIC_PRIORITYGROUP_4         ((uint32_t)0x00000003) /*!< 4 bits for pre-emption priority, | ||||
|                                                                  0 bit  for subpriority */ | ||||
| #endif | ||||
| /* USER CODE BEGIN Private defines */ | ||||
| #define SYSTEM_CLOCK_HZ				12000000 | ||||
| #define MEASUREMENT_PERIOD_S		6 | ||||
| #define RESET_MAGIC_NUMBER 			0xABCD | ||||
| #define MODBUS_ASSERT(x)				if (x == 0) return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED | ||||
| #define CELSIUS_TO_FAHRENHEIT(x)		(x * 9 / 5 + 320) | ||||
|  | ||||
| extern uint16_t usart2_rx_message_index; | ||||
| extern uint16_t usart2_rx_message_len; | ||||
| extern uint8_t usart2_rx_done; | ||||
| extern uint8_t usart2_rx_message_too_long; | ||||
|  | ||||
| extern uint8_t tim21_elapsed_period; | ||||
|  | ||||
| /* USER CODE END Private defines */ | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif /* __MAIN_H */ | ||||
|  | ||||
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ | ||||
							
								
								
									
										231
									
								
								fw/Core/Inc/modbus.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										231
									
								
								fw/Core/Inc/modbus.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,231 @@ | ||||
| /* | ||||
|  * modbus.h | ||||
|  * | ||||
|  *  Created on: Jul 18, 2021 | ||||
|  *      Author: user | ||||
|  * | ||||
|  *  Modbus slave RTU library (does NOT support ASCII and TCP) | ||||
|  * | ||||
|  *  Useful links: | ||||
|  *  https://www.picotech.com/library/oscilloscopes/modbus-serial-protocol-decoding | ||||
|  *  https://ipc2u.com/articles/knowledge-base/modbus-rtu-made-simple-with-detailed-descriptions-and-examples/ | ||||
|  *  https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf | ||||
|  *  https://www.modbus.org/docs/Modbus_Application_Protocol_V1_1b.pdf | ||||
|  * | ||||
|  *  Note that byte order is big endian. | ||||
|  * | ||||
|  * USAGE: | ||||
|  * | ||||
|  * 1) Implement functions modbus_callback_function() and modbus_uart_transmit_function() | ||||
|  *      - modbus_uart_transmit_function() sends data via UART | ||||
|  *      - modbus_callback_function() does the real work: read sensors, set outputs... | ||||
|  *        note that when filling buffers (e.g. input_registers[]) user must | ||||
|  *        ensure that all data is big-endian | ||||
|  *    These functions are implementation-specific. | ||||
|  * 2) Set device address (variable modbus_device_address); you can do this either | ||||
|  *      - setting modbus_device_address directly (modbus.h needs to be included, duh) | ||||
|  *      - using modbus_set_device_address(uint8_t address) function | ||||
|  *    Or you can leave address as-is (MODBUS_DEFAULT_SLAVE_ADDRESS) and set it via | ||||
|  *    Modbus during runtime | ||||
|  * 3) Call modbus_process_msg() after message reception; you need to observe Modbus RTU timing: | ||||
|  *      - pauses between chars in frame are less or equal to 1.5 char | ||||
|  *      - pauses between frames are at least 3.5 chars (of silence) | ||||
|  *    For more information see section 2.5.1.1 (MODBUS Message RTU Framing) | ||||
|  *    in "MODBUS over Serial Line: Specification and Implementation Guide" | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #ifndef SRC_MODBUS_H_ | ||||
| #define SRC_MODBUS_H_ | ||||
|  | ||||
| #include "stdint.h" | ||||
| #include "stddef.h" | ||||
| #include "string.h" | ||||
|  | ||||
| /* | ||||
|  * Defines & macros | ||||
|  */ | ||||
|  | ||||
| #define MODBUS_BROADCAST_ADDR 0 | ||||
| #define MODBUS_DEFAULT_SLAVE_ADDRESS 247 /* 255 may be used for bridge device */ | ||||
| /* minimal frame length is 4 bytes: 1 B slave address, 1 B function code, 2 B CRC */ | ||||
| #define MODBUS_MINIMAL_FRAME_LEN 4 | ||||
| #define MODBUS_MINIMAL_READWRITE_LEN 4 | ||||
| #define MODBUS_MINIMAL_WRITE_MULTIPLE_LEN 5 | ||||
| #define MODBUS_READ_DEVICE_ID_REQUEST_LEN 4 | ||||
| #define MODBUS_READ_DEVICE_ID_RESPONSE_HEADER_LEN 4 | ||||
| #define MODBUS_READ_DEVICE_ID_RESPONSE_OFFSET 3 | ||||
| #define MODBUS_MAX_RTU_FRAME_SIZE 256 | ||||
| #define MODBUS_BUFFER_SIZE MODBUS_MAX_RTU_FRAME_SIZE /* alias */ | ||||
| #define MODBUS_ERROR_FLAG 0x80 | ||||
| #define MODBUS_MAX_REGISTERS 125 | ||||
| /* read device id constants */ | ||||
| #define MODBUS_MEI 0x0E | ||||
| #define MODBUS_DEVICE_ID_INDIVIDUAL_ACCESS_FLAG 0x80 | ||||
| #define MODBUS_MORE_FOLLOWS 0xFF | ||||
| #define MODBUS_NO_MORE_FOLLOWS 0x00 | ||||
| #define MODBUS_BASIC_OBJECT_COUNT 3 | ||||
| #define MODBUS_REGULAR_OBJECT_COUNT 7 | ||||
|  | ||||
| /* | ||||
|  * Return values | ||||
|  */ | ||||
|  | ||||
| #define MODBUS_OK 0 | ||||
| #define MODBUS_ERROR -1 // generic error | ||||
| #define MODBUS_ERROR_CRC -2 // checksum failed | ||||
| #define MODBUS_ERROR_FRAME_INVALID -3 // invalid frame format / length | ||||
| #define MODBUS_ERROR_OUT_OF_BOUNDS -4 // requested register is out of bounds | ||||
| #define MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED -5 // function not implemented in callback | ||||
| #define MODBUS_ERROR_REGISTER_NOT_IMPLEMENTED -6 // register not implemented in callback | ||||
| #define MODBUS_ERROR_DEVICE_ID_NOT_IMPLEMENTED -7 | ||||
|  | ||||
| /* | ||||
|  * Data types | ||||
|  */ | ||||
|  | ||||
| /* Public functions codes (Modbus Application protocol specification, section 5.1) */ | ||||
| typedef enum { | ||||
| 	/* single bit access functions */ | ||||
| 	MODBUS_READ_COILS = 1, | ||||
| 	MODBUS_READ_DO = 1, // alias | ||||
| 	MODBUS_READ_DISCRETE_INPUTS = 2, | ||||
| 	MODBUS_READ_DI = 2, // alias | ||||
| 	MODBUS_WRITE_SINGLE_COIL = 5, | ||||
| 	MODBUS_WRITE_SINGLE_DO = 5, // alias | ||||
| 	MODBUS_WRITE_MULTIPLE_COILS = 15, | ||||
| 	MODBUS_WRITE_MULTIPLE_DO = 15, // alias | ||||
| 	/* 16-bit access functions */ | ||||
| 	MODBUS_READ_HOLDING_REGISTERS = 3, | ||||
| 	MODBUS_READ_AO = 3, // alias | ||||
| 	MODBUS_READ_INPUT_REGISTERS = 4, | ||||
| 	MODBUS_READ_AI = 4, // alias | ||||
| 	MODBUS_WRITE_SINGLE_REGISTER = 6, | ||||
| 	MODBUS_WRITE_SINGLE_AO = 6, // alias | ||||
| 	MODBUS_WRITE_MULTIPLE_REGISTERS = 16, | ||||
| 	MODBUS_WRITE_MULTIPLE_AO = 16, // alias | ||||
| 	MODBUS_MASK_WRITE_REGISTER = 22, | ||||
| 	MODBUS_READ_WRITE_MULTIPLE_REGISTERS = 23, | ||||
| 	MODBUS_READ_FIFO_QUEUE = 24, | ||||
| 	/* file record access */ | ||||
| 	MODBUS_READ_FILE_RECORD = 20, | ||||
| 	MODBUS_WRITE_FILE_RECORD = 21, | ||||
| 	/* diagnostics */ | ||||
| 	MODBUS_READ_EXCEPTION_STATUS = 7, | ||||
| 	MODBUS_DIAGNOSTIC = 8, /* sub codes: 00-18,20 */ | ||||
| 	MODBUS_GET_COM_EVENT_COUNTER = 11, | ||||
| 	MODBUS_GET_COM_EVENT_LOG = 12, | ||||
| 	MODBUS_REPORT_SLAVE_ID = 17, | ||||
| 	MODBUS_READ_DEVICE_IDENTIFICATION = 43, /* sub codes: 14 */ | ||||
| } modbus_function_code_t; | ||||
|  | ||||
| typedef enum { | ||||
| 	MODBUS_EXCEPTION_ILLEGAL_FUNCTION = 1, | ||||
| 	MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS = 2, | ||||
| 	MODBUS_EXCEPTION_ILLEGAL_REGISTER_QUANTITY = 2, | ||||
| 	MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE = 3, | ||||
| 	MODBUS_EXCEPTION_SLAVE_DEVICE_FAILURE = 4, | ||||
| 	MODBUS_EXCEPTION_ACKNOWLEDGE = 5, | ||||
| 	MODBUS_EXCEPTION_SLAVE_DEVICE_BUSY = 6, | ||||
| 	MODBUS_EXCEPTION_MEMORY_PARITY_ERROR = 8, | ||||
| 	MODBUS_EXCEPTION_GATEWAY_PATH_UNAVAILABLE = 10, | ||||
| 	MODBUS_EXCEPTION_GATEWAY_TARGET_DEVICE_FAILED_TO_RESPOND = 11, | ||||
| 	MODBUS_EXCEPTION_ILLEGAL_DEVICE_ID_CODE = 3 | ||||
| } modbus_exception_code_t; | ||||
|  | ||||
| typedef struct { | ||||
| 	modbus_function_code_t function_code : 8; | ||||
| 	uint16_t register_address; // e.g. first register of A0: 0 | ||||
| 	uint16_t register_number;  // e.g. first register of A0: 40001 | ||||
| 	uint8_t  register_count; // number of registers to be read/written | ||||
|  | ||||
| 	modbus_exception_code_t exception; | ||||
|  | ||||
| 	uint8_t broadcast; // 1 if broadcast, 0 otherwise | ||||
|  | ||||
| 	union { | ||||
| 		uint8_t buffer8b[MODBUS_MAX_RTU_FRAME_SIZE]; | ||||
| 		uint16_t buffer16b[MODBUS_MAX_RTU_FRAME_SIZE/2]; | ||||
| 		uint16_t input_registers[MODBUS_MAX_REGISTERS]; | ||||
| 		uint16_t holding_registers[MODBUS_MAX_REGISTERS]; | ||||
| 		int16_t  input_registers_signed[MODBUS_MAX_REGISTERS]; | ||||
| 		int16_t  holding_registers_signed[MODBUS_MAX_REGISTERS]; | ||||
| 	}; | ||||
|  | ||||
| 	/* process device id */ | ||||
| 	uint8_t read_device_id_code; | ||||
| 	uint8_t object_id; | ||||
| } modbus_transaction_t; | ||||
|  | ||||
| typedef enum { | ||||
| 	MODBUS_DO_START_NUMBER = 1, // Discrete output coils | ||||
| 	MODBUS_DO_END_NUMBER = 9999, | ||||
| 	MODBUS_DI_START_NUMBER = 10001, // Discrete input contacts | ||||
| 	MODBUS_DI_END_NUMBER = 19999, | ||||
| 	MODBUS_AI_START_NUMBER = 30001, // Analog input registers | ||||
| 	MODBUS_AI_END_NUMBER = 39999, | ||||
| 	MODBUS_AO_START_NUMBER = 40001, // Analog output (holding registers) | ||||
| 	MODBUS_AO_END_NUMBER = 49999 | ||||
| } modbus_register_number_t; | ||||
|  | ||||
| typedef enum { | ||||
| 	MODBUS_CONFORMITY_BASIC = 1, | ||||
| 	MODBUS_CONFORMITY_REGULAR = 2, | ||||
| 	MODBUS_CONFORMITY_EXTENDED = 3, | ||||
| 	MODBUS_INDIVIDUAL_ACCESS = 4 /* not actually part of conformity, but I'll keep it here anyway */ | ||||
| } modbus_conformity_level_t; | ||||
|  | ||||
| /* Device ID datatypes */ | ||||
| #define MODBUS_DEVICE_ID_OBJECT_NUM 7 | ||||
| typedef struct { | ||||
| 	union { | ||||
| 		struct { | ||||
| 			/* Basic category (mandatory part) */ | ||||
| 			char *VendorName; | ||||
| 			char *ProductCode; | ||||
| 			char *MajorMinorRevision; | ||||
| 			/* Regular category (optional part) */ | ||||
| 			char *VendorUrl; | ||||
| 			char *ProductName; | ||||
| 			char *ModelName; | ||||
| 			char *UserApplicationName; | ||||
| 			/* Extended category (optional part) */ | ||||
| 			// Nothing here yet! | ||||
| 		} object_name; | ||||
| 		char *object_id[MODBUS_DEVICE_ID_OBJECT_NUM]; | ||||
| 	}; | ||||
| 	uint8_t conformity_level; | ||||
| } modbus_device_id_t; | ||||
|  | ||||
| /* | ||||
|  * Global variables | ||||
|  */ | ||||
|  | ||||
| /* device address: declared in modbus.c */ | ||||
| extern uint8_t modbus_slave_address; | ||||
|  | ||||
| /* shared modbus buffer; defined in modbus.c; may be used elsewhere in code */ | ||||
| extern uint8_t modbus_buffer[]; | ||||
|  | ||||
| /* modbus device id struct */ | ||||
| extern modbus_device_id_t *modbus_device_id; | ||||
|  | ||||
| /* | ||||
|  * Function prototypes | ||||
|  */ | ||||
|  | ||||
| /* process message: should be called in when modbus message was received (e.g. in main.c) | ||||
|  * modbus_process_msg() may call following functions: | ||||
|  *     - modbus_callback_function() if data readout is requested | ||||
|  *     - modbus_uart_transmit_function() if response is required | ||||
|  * Both functions have to be implemented by user. | ||||
|  */ | ||||
| int8_t modbus_slave_process_msg(const uint8_t *buffer, int len); | ||||
| int8_t modbus_slave_init_device_id(modbus_device_id_t *device_id); | ||||
| int8_t modbus_slave_set_address(uint8_t address); | ||||
| /* modbus callback function type - should be implemented by user (e.g. in main.c) */ | ||||
| int8_t modbus_slave_callback(modbus_transaction_t *transaction); | ||||
| /* UART transmit function type - should be implemented by user (e.g. in main.c) */ | ||||
| int8_t modbus_transmit_function(uint8_t *buffer, uint16_t data_len); | ||||
|  | ||||
| #endif /* SRC_MODBUS_H_ */ | ||||
							
								
								
									
										57
									
								
								fw/Core/Inc/sht4x.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								fw/Core/Inc/sht4x.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| /* | ||||
|  * sht4x.h | ||||
|  * | ||||
|  *  Created on: Jun 8, 2021 | ||||
|  *      Author: user | ||||
|  */ | ||||
|  | ||||
| #ifndef INC_SHT4X_H_ | ||||
| #define INC_SHT4X_H_ | ||||
|  | ||||
| #include "stdint.h" | ||||
| #include "stm32l0xx_ll_i2c.h" | ||||
| #include "stm32l0xx_ll_utils.h" | ||||
| #include "i2c.h" | ||||
| #include "crc8.h" | ||||
|  | ||||
| /* | ||||
|  * Defines & macros | ||||
|  */ | ||||
|  | ||||
| #define SHT4X_I2C_ADDRESS 0x44 | ||||
|  | ||||
| /* | ||||
|  * Return values | ||||
|  */ | ||||
|  | ||||
| #define SHT4X_OK 0 | ||||
| #define SHT4X_ERROR -1 // generic error | ||||
| #define SHT4X_CRC8_ERROR -2 // checksum failed | ||||
|  | ||||
| /* | ||||
|  * Data types | ||||
|  */ | ||||
|  | ||||
| typedef enum { | ||||
| 	SHT4X_START_MEAS_HIGH_PRECISION = 0xFD, | ||||
| 	SHT4X_START_MEAS_MEDIUM_PRECISION = 0xF6, | ||||
| 	SHT4X_START_MEAS_LOW_PRECISION = 0xE0, | ||||
| 	SHT4X_READ_SERIAL = 0x89, | ||||
| 	SHT4X_SOFT_RESET = 0x94, | ||||
| 	SHT4X_HEATER_200_mW_1_s = 0x39, | ||||
| 	SHT4X_HEATER_200_mW_01_s = 0x32, | ||||
| 	SHT4X_HEATER_110_mW_1_s = 0x2F, | ||||
| 	SHT4X_HEATER_110_mW_01_s = 0x24, | ||||
| 	SHT4X_HEATER_20_mW_1_s = 0x1E, | ||||
| 	SHT4X_HEATER_20_mW_01_s = 0x15 | ||||
| } sht4x_cmd_t; | ||||
|  | ||||
| /* | ||||
|  * Function prototypes | ||||
|  */ | ||||
|  | ||||
| int8_t sht4x_send_cmd(sht4x_cmd_t cmd); | ||||
| int8_t sht4x_read_data(uint8_t *buffer, int len); | ||||
| int8_t sht4x_measure(int16_t *temperature, uint16_t *relative_humidity); | ||||
|  | ||||
| #endif /* INC_SHT4X_H_ */ | ||||
							
								
								
									
										53
									
								
								fw/Core/Inc/stm32_assert.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								fw/Core/Inc/stm32_assert.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,53 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * @file    stm32_assert.h | ||||
|   * @brief   STM32 assert file. | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2018 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under BSD 3-Clause license, | ||||
|   * the "License"; You may not use this file except in compliance with the | ||||
|   * License. You may obtain a copy of the License at: | ||||
|   *                        opensource.org/licenses/BSD-3-Clause | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| /* Define to prevent recursive inclusion -------------------------------------*/ | ||||
| #ifndef __STM32_ASSERT_H | ||||
| #define __STM32_ASSERT_H | ||||
|  | ||||
| #ifdef __cplusplus | ||||
|  extern "C" { | ||||
| #endif | ||||
|  | ||||
| /* Exported types ------------------------------------------------------------*/ | ||||
| /* Exported constants --------------------------------------------------------*/ | ||||
| /* Includes ------------------------------------------------------------------*/ | ||||
| /* Exported macro ------------------------------------------------------------*/ | ||||
| #ifdef  USE_FULL_ASSERT | ||||
| /** | ||||
|   * @brief  The assert_param macro is used for function's parameters check. | ||||
|   * @param  expr: If expr is false, it calls assert_failed function | ||||
|   *         which reports the name of the source file and the source | ||||
|   *         line number of the call that failed. | ||||
|   *         If expr is true, it returns no value. | ||||
|   * @retval None | ||||
|   */ | ||||
|  #define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__)) | ||||
| /* Exported functions ------------------------------------------------------- */ | ||||
|   void assert_failed(uint8_t* file, uint32_t line); | ||||
| #else | ||||
|   #define assert_param(expr) ((void)0U) | ||||
| #endif /* USE_FULL_ASSERT */ | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif /* __STM32_ASSERT_H */ | ||||
|  | ||||
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ | ||||
							
								
								
									
										65
									
								
								fw/Core/Inc/stm32l0xx_it.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								fw/Core/Inc/stm32l0xx_it.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,65 @@ | ||||
| /* USER CODE BEGIN Header */ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * @file    stm32l0xx_it.h | ||||
|   * @brief   This file contains the headers of the interrupt handlers. | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under BSD 3-Clause license, | ||||
|   * the "License"; You may not use this file except in compliance with the | ||||
|   * License. You may obtain a copy of the License at: | ||||
|   *                        opensource.org/licenses/BSD-3-Clause | ||||
|   * | ||||
|  ****************************************************************************** | ||||
|   */ | ||||
| /* USER CODE END Header */ | ||||
|  | ||||
| /* Define to prevent recursive inclusion -------------------------------------*/ | ||||
| #ifndef __STM32L0xx_IT_H | ||||
| #define __STM32L0xx_IT_H | ||||
|  | ||||
| #ifdef __cplusplus | ||||
|  extern "C" { | ||||
| #endif | ||||
|  | ||||
| /* Private includes ----------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN Includes */ | ||||
|  | ||||
| /* USER CODE END Includes */ | ||||
|  | ||||
| /* Exported types ------------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN ET */ | ||||
|  | ||||
| /* USER CODE END ET */ | ||||
|  | ||||
| /* Exported constants --------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN EC */ | ||||
| /* USER CODE END EC */ | ||||
|  | ||||
| /* Exported macro ------------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN EM */ | ||||
|  | ||||
| /* USER CODE END EM */ | ||||
|  | ||||
| /* Exported functions prototypes ---------------------------------------------*/ | ||||
| void NMI_Handler(void); | ||||
| void HardFault_Handler(void); | ||||
| void SVC_Handler(void); | ||||
| void PendSV_Handler(void); | ||||
| void SysTick_Handler(void); | ||||
| void TIM21_IRQHandler(void); | ||||
| void USART2_IRQHandler(void); | ||||
| /* USER CODE BEGIN EFP */ | ||||
| /* USER CODE END EFP */ | ||||
|  | ||||
| #ifdef __cplusplus | ||||
| } | ||||
| #endif | ||||
|  | ||||
| #endif /* __STM32L0xx_IT_H */ | ||||
|  | ||||
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ | ||||
							
								
								
									
										243
									
								
								fw/Core/Src/config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										243
									
								
								fw/Core/Src/config.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,243 @@ | ||||
| /* | ||||
|  * config.c | ||||
|  * | ||||
|  *  Created on: Sep 5, 2021 | ||||
|  *      Author: david | ||||
|  */ | ||||
|  | ||||
| #include "config.h" | ||||
|  | ||||
| /* | ||||
|  * Variables | ||||
|  */ | ||||
| /* Baudrates - STM32L0xx can do baudrates from 1200 to 115200 | ||||
|  * - default value has index 0 */ | ||||
| const uint32_t config_baudrates[] = { | ||||
| 			19200,	// 0 | ||||
| 			4800,	// 1 | ||||
| 			9600,	// 2 | ||||
| 			14400,	// 3 | ||||
| 			19200,	// 4 | ||||
| 			28800,	// 5 | ||||
| 			38400,	// 6 | ||||
| 			57600,	// 7 | ||||
| 			76800,	// 8 | ||||
| 			115200	// 9 | ||||
| }; | ||||
| const uint8_t config_baudrates_length = 10; | ||||
|  | ||||
| /* Function to lock the EEPROM */ | ||||
| static int8_t eeprom_lock(void); | ||||
| /* Function to unlock the EEPROM */ | ||||
| static int8_t eeprom_unlock(void); | ||||
| /* Function to write one byte to the EEPROM */ | ||||
| /* IMPORTANT: EEPROM must be unlocked first */ | ||||
| static int8_t eeprom_program_byte(uint32_t addr, uint8_t ee_data); | ||||
| /* Function to write two bytes to the EEPROM */ | ||||
| /* IMPORTANT: EEPROM must be unlocked first */ | ||||
| static int8_t eeprom_program_halfword(uint32_t addr, uint16_t ee_data); | ||||
| /* Function to write four bytes to the EEPROM */ | ||||
| /* IMPORTANT: EEPROM must be unlocked first */ | ||||
| static int8_t eeprom_program_word(uint32_t addr, uint32_t ee_data); | ||||
|  | ||||
| int8_t config_read(config_t *config) | ||||
| { | ||||
| 	config->modbus_addr = *(uint16_t *) (CONFIG_EEPROM_ADDR_MODBUS_ADDR); | ||||
| 	config->baudrate_index = *(uint16_t *) (CONFIG_EEPROM_ADDR_BAUDRATE_INDEX); | ||||
| 	config->led_on = *(uint16_t *) (CONFIG_EEPROM_ADDR_LED_ON); | ||||
| 	config->led_brightness = *(uint16_t *) (CONFIG_EEPROM_ADDR_LED_BRIGHTNESS); | ||||
| 	config->led_smooth = *(uint16_t *) (CONFIG_EEPROM_ADDR_LED_SMOOTH); | ||||
| 	config->led_co2_alert_limit1 = *(uint16_t *) (CONFIG_EEPROM_ADDR_LED_ALERT1); | ||||
| 	config->led_co2_alert_limit2 = *(uint16_t *) (CONFIG_EEPROM_ADDR_LED_ALERT2); | ||||
| 	config->scd4x_t_offset = *(int16_t *) (CONFIG_EEPROM_ADDR_SCD4x_T_OFFSET); | ||||
|  | ||||
| 	/* Check if the EEPROM is initialized - do not check: | ||||
| 	 *  LED ON | ||||
| 	 *  LED SMOOTH | ||||
| 	 *  SCD4x T OFFSET | ||||
| 	 *  BAUDRATE INDEX | ||||
| 	 *  those can be 0 */ | ||||
| 	if ((config->modbus_addr 			== EEPROM_EMPTY_BYTE) || | ||||
| 		(config->led_co2_alert_limit1 	== EEPROM_EMPTY_BYTE) || | ||||
| 		(config->led_co2_alert_limit2 	== EEPROM_EMPTY_BYTE) || | ||||
| 		(config->led_brightness 		== EEPROM_EMPTY_BYTE)) | ||||
| 	{ | ||||
| 		return CONFIG_ERROR; | ||||
| 	} | ||||
| 	return CONFIG_OK; | ||||
| } | ||||
|  | ||||
| int8_t config_write(config_t *config) | ||||
| { | ||||
| 	/* Unlock the EEPROM */ | ||||
| 	if (eeprom_unlock() != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_UNLOCK_ERROR; | ||||
| 	} | ||||
| 	/* Reset the ERASE and DATA bits in the FLASH_PECR register to disable any residual erase */ | ||||
| 	FLASH->PECR = FLASH->PECR & ~(FLASH_PECR_ERASE | FLASH_PECR_DATA); | ||||
|  | ||||
| 	/* Write MODBUS ADDRESS */ | ||||
| 	if (eeprom_program_halfword(CONFIG_EEPROM_ADDR_MODBUS_ADDR, config->modbus_addr)  != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
| 	/* Write BAUDRATE */ | ||||
| 	if (eeprom_program_halfword(CONFIG_EEPROM_ADDR_BAUDRATE_INDEX, config->baudrate_index)  != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write LED ON */ | ||||
| 	if (eeprom_program_byte(CONFIG_EEPROM_ADDR_LED_ON, config->led_on)  != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write LED BRIGHTNESS */ | ||||
| 	if (eeprom_program_halfword(CONFIG_EEPROM_ADDR_LED_BRIGHTNESS, config->led_brightness)  != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write LED SMOOTH */ | ||||
| 	if (eeprom_program_byte(CONFIG_EEPROM_ADDR_LED_SMOOTH, config->led_smooth)  != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write LED CO2 ALERT LIMIT 1 */ | ||||
| 	if (eeprom_program_halfword(CONFIG_EEPROM_ADDR_LED_ALERT1, config->led_co2_alert_limit1) != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write LED CO2 ALERT LIMIT 2 */ | ||||
| 	if (eeprom_program_halfword(CONFIG_EEPROM_ADDR_LED_ALERT2, config->led_co2_alert_limit2) != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Write LED SCD4x TEMPERATURE OFFSET */ | ||||
| 	if (eeprom_program_halfword(CONFIG_EEPROM_ADDR_SCD4x_T_OFFSET, config->scd4x_t_offset) != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_WRITE_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Lock EEPROM*/ | ||||
| 	if (eeprom_lock() != EEPROM_OK) | ||||
| 	{ | ||||
| 		return EEPROM_LOCK_ERROR; | ||||
| 	} | ||||
| 	return CONFIG_OK; | ||||
| } | ||||
|  | ||||
| static int8_t eeprom_lock(void) | ||||
| { | ||||
| 	uint32_t tick_start = SysTick->VAL; | ||||
|   	while ((FLASH->SR & FLASH_SR_BSY) != 0) /* Wait for FLASH to be free */ | ||||
|   	{ | ||||
|  	   /* Timeout test */ | ||||
|  	   /* The maximum writing time is 3.94ms (half-word) */ | ||||
|   		uint32_t tick_last = SysTick->VAL; | ||||
|   		uint32_t tick_diff; | ||||
|   		if (tick_start <= tick_last) | ||||
|   		{ | ||||
|   			tick_diff = tick_last - tick_start; | ||||
|   		} else | ||||
|   		{ | ||||
|   			tick_diff = (0xFFFFFFFF - tick_last) + tick_start; | ||||
|   		} | ||||
|  | ||||
|  		/* If the time difference is more than 5ms */ | ||||
|   		if (tick_diff >= (uint32_t)((uint32_t)SYSTICK_FREQ_HZ*(uint32_t)EEPROM_TIMEOUT_MAX_MS_INV)) | ||||
|   		{ | ||||
|   			return EEPROM_LOCK_ERROR; | ||||
|   		} | ||||
|   	} | ||||
|  | ||||
|   	FLASH->PECR = FLASH->PECR & ~(FLASH_PECR_ERRIE | FLASH_PECR_EOPIE); /* disable flash interrupts */ | ||||
|   	FLASH->PECR = FLASH->PECR | FLASH_PECR_PELOCK; /* Lock memory with PELOCK */ | ||||
|  | ||||
|   	return EEPROM_OK; | ||||
| } | ||||
|  | ||||
| static int8_t eeprom_unlock(void) | ||||
| { | ||||
| 	uint32_t tick_start = SysTick->VAL; | ||||
|    while ((FLASH->SR & FLASH_SR_BSY) != 0) /* Wait for FLASH to be free */ | ||||
|    { | ||||
| 	   /* Timeout test */ | ||||
| 	   /* The maximum writing time is 3.94ms (half-word) */ | ||||
|  		uint32_t tick_last = SysTick->VAL; | ||||
|  		uint32_t tick_diff; | ||||
|  		if (tick_start <= tick_last) | ||||
|  		{ | ||||
|  			tick_diff = tick_last - tick_start; | ||||
|  		} else | ||||
|  		{ | ||||
|  			tick_diff = (0xFFFFFFFF - tick_last) + tick_start; | ||||
|  		} | ||||
|  | ||||
|  		/* If the time difference is more than 5ms */ | ||||
|   		if (tick_diff >= (uint32_t)((uint32_t)SYSTICK_FREQ_HZ*(uint32_t)EEPROM_TIMEOUT_MAX_MS_INV)) | ||||
|  		{ | ||||
|  			return EEPROM_UNLOCK_ERROR; | ||||
|  		} | ||||
|    } | ||||
|    if ((FLASH->PECR & FLASH_PECR_PELOCK) != 0) /* If PELOCK is locked */ | ||||
|    { | ||||
| 	   /* Unlock PELOCK */ | ||||
|       FLASH->PEKEYR = FLASH_PEKEY1; /* PEKEY1 */ | ||||
|       FLASH->PEKEYR = FLASH_PEKEY2; /* PEKEY2 */ | ||||
|    } | ||||
|    FLASH->PECR = FLASH->PECR | (FLASH_PECR_ERRIE | FLASH_PECR_EOPIE); /* enable flash interrupts */ | ||||
|    return EEPROM_OK; | ||||
| } | ||||
|  | ||||
| static int8_t eeprom_program_byte(uint32_t addr, uint8_t ee_data) | ||||
| { | ||||
| 	if ((EEPROM_ADDR_START <= addr) && (addr <= EEPROM_ADDR_END - 1)) | ||||
| 	{ | ||||
| 		*(uint8_t *)(addr) = ee_data; /* write data to EEPROM */ | ||||
| 		if (*(uint8_t *)(addr) != ee_data) | ||||
| 		{ | ||||
| 			return EEPROM_WRITE_ERROR; | ||||
| 		} | ||||
| 		return EEPROM_OK; | ||||
| 	} else | ||||
| 	{ | ||||
| 		return EEPROM_ADDR_ERROR; | ||||
| 	} | ||||
| } | ||||
| static int8_t eeprom_program_halfword(uint32_t addr, uint16_t ee_data) | ||||
| { | ||||
| 	if ((EEPROM_ADDR_START <= addr) && (addr <= EEPROM_ADDR_END - 2)) | ||||
| 	{ | ||||
| 		*(uint16_t *)(addr) = ee_data; /* write data to EEPROM */ | ||||
| 		if (*(uint16_t *)(addr) != ee_data) | ||||
| 		{ | ||||
| 			return EEPROM_WRITE_ERROR; | ||||
| 		} | ||||
| 		return EEPROM_OK; | ||||
| 	} else | ||||
| 	{ | ||||
| 		return EEPROM_ADDR_ERROR; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int8_t eeprom_program_word(uint32_t addr, uint32_t ee_data) | ||||
| { | ||||
| 	if ((EEPROM_ADDR_START <= addr) && (addr <= EEPROM_ADDR_END - 4)) | ||||
| 	{ | ||||
| 		*(uint32_t *)(addr) = ee_data; /* write data to EEPROM */ | ||||
| 		if (*(uint32_t *)(addr) != ee_data) | ||||
| 		{ | ||||
| 			return EEPROM_WRITE_ERROR; | ||||
| 		} | ||||
| 		return EEPROM_OK; | ||||
| 	} else | ||||
| 	{ | ||||
| 		return EEPROM_ADDR_ERROR; | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										28
									
								
								fw/Core/Src/crc8.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								fw/Core/Src/crc8.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| /* | ||||
|  * crc.c | ||||
|  * | ||||
|  *  Created on: Jun 9, 2021 | ||||
|  *      Author: user | ||||
|  */ | ||||
|  | ||||
| #include "crc8.h" | ||||
|  | ||||
| /* Stolen from Sensirion SCD4x datasheet, section 3.11 */ | ||||
| uint8_t crc8_calculate(const uint8_t *data, uint16_t count) | ||||
| { | ||||
| 	uint16_t current_byte; | ||||
| 	uint8_t crc = CRC8_INIT; | ||||
| 	uint8_t crc_bit; | ||||
| 	/* calculates 8-Bit checksum with given polynomial */ | ||||
| 	for (current_byte = 0; current_byte < count; ++current_byte) { | ||||
| 		crc ^= (data[current_byte]); | ||||
| 		for(crc_bit = 8; crc_bit > 0; --crc_bit) { | ||||
| 			if (crc & 0x80) { | ||||
| 				crc =(crc << 1) ^ CRC8_POLYNOMIAL; | ||||
| 			} else { | ||||
| 				crc = (crc << 1); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return crc; | ||||
| } | ||||
							
								
								
									
										69
									
								
								fw/Core/Src/i2c.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										69
									
								
								fw/Core/Src/i2c.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,69 @@ | ||||
| /* | ||||
|  * i2c.c | ||||
|  * | ||||
|  *  Created on: Jun 8, 2021 | ||||
|  *      Author: user | ||||
|  */ | ||||
|  | ||||
| #include "i2c.h" | ||||
| #include "stm32l0xx_ll_usart.h" | ||||
|  | ||||
| static i2c_context_t *i2c_context; | ||||
|  | ||||
| int8_t i2c_init(i2c_context_t *context) | ||||
| { | ||||
| 	if (context == NULL) { | ||||
| 		return I2C_ERROR; | ||||
| 	} | ||||
| 	i2c_context = context; | ||||
| 	return I2C_OK; | ||||
| } | ||||
|  | ||||
| int8_t i2c_transmit(uint8_t address, uint8_t *buffer, int len) | ||||
| { | ||||
| 	LL_I2C_HandleTransfer(i2c_context->i2c, address, LL_I2C_ADDRSLAVE_7BIT, len, | ||||
| 			LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_START_WRITE); | ||||
| 	int i = 0; | ||||
| 	/* Autoend mode will raise STOP flag if NACK is detected | ||||
| 	 * (or if desired number of bytes is transmitted) */ | ||||
| 	while (!LL_I2C_IsActiveFlag_STOP(i2c_context->i2c)) { | ||||
| 		if (LL_I2C_IsActiveFlag_TXE(i2c_context->i2c)) { | ||||
| 			if (i < len) { | ||||
| 				LL_I2C_TransmitData8(i2c_context->i2c, buffer[i++]); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	LL_I2C_ClearFlag_STOP(i2c_context->i2c); | ||||
| 	LL_I2C_ClearFlag_TXE(i2c_context->i2c); | ||||
| 	if (len != i) { | ||||
| 		/* If we detect NACK during transaction (before the end of the last byte) */ | ||||
| 		return I2C_ERROR_TX_INCOMPLETE; | ||||
| 	} | ||||
| 	/* NACK after last byte is ok */ | ||||
| //	if (LL_I2C_IsActiveFlag_NACK(i2c_context->i2c)) { | ||||
| // | ||||
| //		return I2C_ERROR_NACK; | ||||
| //	} | ||||
| 	return I2C_OK; | ||||
| } | ||||
|  | ||||
| int8_t i2c_receive(uint8_t address, uint8_t *buffer, int len) | ||||
| { | ||||
| 	LL_I2C_HandleTransfer(i2c_context->i2c, address, LL_I2C_ADDRSLAVE_7BIT, len, | ||||
| 				LL_I2C_MODE_AUTOEND, LL_I2C_GENERATE_START_READ); | ||||
| 	LL_I2C_ClearFlag_STOP(i2c_context->i2c); | ||||
| 	int i = 0; | ||||
| 	while (!LL_I2C_IsActiveFlag_STOP(i2c_context->i2c)) { | ||||
| 		if (LL_I2C_IsActiveFlag_RXNE(i2c_context->i2c)) { | ||||
| 			if (i < len) { | ||||
| 				buffer[i++] = LL_I2C_ReceiveData8(i2c_context->i2c); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	LL_I2C_ClearFlag_STOP(i2c_context->i2c); | ||||
| 	if (len != i) { | ||||
| 		return I2C_ERROR_RX_INCOMPLETE; | ||||
| 	} | ||||
|  | ||||
| 	return I2C_OK; | ||||
| } | ||||
							
								
								
									
										202
									
								
								fw/Core/Src/ltr329.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								fw/Core/Src/ltr329.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | ||||
| /* | ||||
|  * ltr329.c | ||||
|  * | ||||
|  *  Created on: May 22, 2022 | ||||
|  *      Author: david | ||||
|  */ | ||||
| #include "ltr329.h" | ||||
| #include "main.h" /* for uart_disable_interrupts() */ | ||||
|  | ||||
| static int8_t ltr329_read_register (ltr329_cmd_t register_addr, uint8_t *register_data ) | ||||
| { | ||||
| 	uint8_t tx_buffer[1]; | ||||
| 	uint8_t rx_buffer[1]; | ||||
| 	int result; | ||||
|  | ||||
| 	// start measurement | ||||
| 	tx_buffer[0] = register_addr; | ||||
| 	/* disable interrupts to prevent modbus/i2c conflict */ | ||||
| 	uart_disable_interrupts(); | ||||
| 	result = i2c_transmit(LTR329_I2C_ADDRESS<<1, tx_buffer, 1); | ||||
| 	uart_enable_interrupts(); | ||||
| 	if (result != I2C_OK) { | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	LL_mDelay(10); /* 10 ms should be enough */ | ||||
| 	/* read out */ | ||||
| 	uart_disable_interrupts(); | ||||
| 	result = i2c_receive(LTR329_I2C_ADDRESS<<1, rx_buffer, 1); | ||||
| 	uart_enable_interrupts(); | ||||
| 	if (result != I2C_OK) { | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	*register_data = rx_buffer[0]; | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| static int8_t ltr329_write_register (ltr329_cmd_t register_addr, uint8_t register_data) | ||||
| { | ||||
| 	uint8_t tx_buffer[2]; | ||||
| 	int result; | ||||
|  | ||||
| 	// start measurement | ||||
| 	tx_buffer[0] = register_addr; | ||||
| 	tx_buffer[1] = register_data; | ||||
| 	/* disable interrupts to prevent modbus/i2c conflict */ | ||||
| 	uart_disable_interrupts(); | ||||
| 	result = i2c_transmit(LTR329_I2C_ADDRESS<<1, tx_buffer, 2); | ||||
| 	uart_enable_interrupts(); | ||||
| 	if (result != I2C_OK) { | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| int8_t ltr329_write_settings (ltr329_gain_t gain, ltr329_als_mode_t mode, ltr329_integration_time_t integ_time, ltr329_measurement_rate_t meas_rate) | ||||
| { | ||||
| 	int8_t result; | ||||
| 	/* Write Gain and ALS Mode */ | ||||
| 	result = ltr329_write_register(LTR329_ALS_CONTR, (gain | mode)); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	/* Write Integration Time and Measurement Rate */ | ||||
|  | ||||
| 	result = ltr329_write_register(LTR329_ALS_MEAS_RATE, (integ_time | meas_rate)); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| int8_t ltr329_read_settings (ltr329_gain_t *gain, ltr329_als_mode_t *mode, ltr329_integration_time_t *integ_time, ltr329_measurement_rate_t *meas_rate) | ||||
| { | ||||
| 	int8_t result; | ||||
| 	uint8_t control_register_data; | ||||
| 	uint8_t rate_register_data; | ||||
|  | ||||
| 	result = ltr329_read_register(LTR329_ALS_CONTR, &control_register_data); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	result = ltr329_read_register(LTR329_ALS_MEAS_RATE, &rate_register_data); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	uint8_t control_register_gain_mask = 0b00011100; | ||||
| 	uint8_t control_register_mode_mask = 0b00000001; | ||||
| 	uint8_t rate_register_int_time_mask = 0b00111000; | ||||
| 	uint8_t rate_register_rate_mask = 0b00000111; | ||||
|  | ||||
| 	/* Return Registers Values */ | ||||
| 	/* TODO: This might not be safe */ | ||||
| 	*gain = control_register_data & control_register_gain_mask; | ||||
| 	*mode = control_register_data & control_register_mode_mask; | ||||
| 	*integ_time = rate_register_data & rate_register_int_time_mask; | ||||
| 	*meas_rate = rate_register_data & rate_register_rate_mask; | ||||
|  | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| int8_t ltr329_sw_reset( void ) | ||||
| { | ||||
| 	int8_t result; | ||||
| 	/* Write Gain and ALS Mode */ | ||||
| 	result = ltr329_write_register(LTR329_ALS_CONTR, 0b00000010); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| int8_t ltr329_measure (uint16_t *data_ch0, uint16_t *data_ch1) | ||||
| { | ||||
| 	uint8_t ch0_l, ch0_h, ch1_l, ch1_h; | ||||
| 	int result; | ||||
| 	result = ltr329_read_register(LTR329_ALS_DATA_CH0_0, &ch0_l); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	result = ltr329_read_register(LTR329_ALS_DATA_CH0_1, &ch0_h); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	result = ltr329_read_register(LTR329_ALS_DATA_CH1_0, &ch1_l); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	result = ltr329_read_register(LTR329_ALS_DATA_CH1_1, &ch1_h); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	*data_ch0 = (ch0_h << 8) + ch0_l; | ||||
| 	*data_ch1 = (ch1_h << 8) + ch1_l; | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| int8_t ltr329_read_status_register(uint8_t *data_valid, uint8_t *new_data, ltr329_gain_t *gain) | ||||
| { | ||||
| 	int8_t result; | ||||
| 	uint8_t status_register_data; | ||||
| 	result = ltr329_read_register(LTR329_ALS_STATUS, &status_register_data); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
|  | ||||
| 	/* Check data valid */ | ||||
| 	uint8_t data_invalid_mask = 0b10000000; | ||||
| 	if ((status_register_data & data_invalid_mask) == data_invalid_mask) | ||||
| 	{ | ||||
| 		*data_valid = 0; | ||||
| 	} else | ||||
| 	{ | ||||
| 		*data_valid = 1; | ||||
| 	} | ||||
|  | ||||
| 	/* Check if there is new data */ | ||||
| 	uint8_t data_status_mask = 0b00000100; | ||||
| 	if ((status_register_data & data_status_mask) == data_status_mask) | ||||
| 	{ | ||||
| 		*new_data = 1; | ||||
| 	} else | ||||
| 	{ | ||||
| 		*new_data = 0; | ||||
| 	} | ||||
|  | ||||
| 	/* Check Gain */ | ||||
| 	/* TODO: This might not be safe */ | ||||
| 	uint8_t gain_mask = 0b01110000; | ||||
| 	*gain = status_register_data & gain_mask; | ||||
|  | ||||
| 	return LTR329_OK; | ||||
| } | ||||
|  | ||||
| int8_t ltr329_read_device_info (uint8_t *manufacturer_id, uint8_t *part_id) | ||||
| { | ||||
| 	int8_t result; | ||||
| 	result = ltr329_read_register(LTR329_MANUFAC_ID, manufacturer_id); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	result = ltr329_read_register(LTR329_PART_ID, part_id); | ||||
| 	if (result != LTR329_OK) | ||||
| 	{ | ||||
| 		return LTR329_ERROR; | ||||
| 	} | ||||
| 	return LTR329_OK; | ||||
| } | ||||
							
								
								
									
										749
									
								
								fw/Core/Src/main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										749
									
								
								fw/Core/Src/main.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,749 @@ | ||||
| /* USER CODE BEGIN Header */ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * @file           : main.c | ||||
|   * @brief          : Main program body | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under BSD 3-Clause license, | ||||
|   * the "License"; You may not use this file except in compliance with the | ||||
|   * License. You may obtain a copy of the License at: | ||||
|   *                        opensource.org/licenses/BSD-3-Clause | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
| /* USER CODE END Header */ | ||||
| /* Includes ------------------------------------------------------------------*/ | ||||
| #include "main.h" | ||||
|  | ||||
| /* Private includes ----------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN Includes */ | ||||
|  | ||||
| /* USER CODE END Includes */ | ||||
|  | ||||
| /* Private typedef -----------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN PTD */ | ||||
|  | ||||
| /* USER CODE END PTD */ | ||||
|  | ||||
| /* Private define ------------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN PD */ | ||||
| /* USER CODE END PD */ | ||||
|  | ||||
| /* Private macro -------------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN PM */ | ||||
|  | ||||
| /* USER CODE END PM */ | ||||
|  | ||||
| /* Private variables ---------------------------------------------------------*/ | ||||
|  | ||||
| /* USER CODE BEGIN PV */ | ||||
| /* | ||||
|  * BASE CLOCK 12MHz | ||||
|  * Desired interrupt period 60s | ||||
|  */ | ||||
| const uint16_t tim21_prescaler = 60000-1; // 100Hz | ||||
| //const uint16_t tim21_period = 12000-1; // 60s | ||||
| //const uint16_t tim21_period = 1200-1; // 6s | ||||
| const uint16_t tim21_period = MEASUREMENT_PERIOD_S * (SYSTEM_CLOCK_HZ / tim21_prescaler) - 1; | ||||
| //const uint16_t tim21_period = 200-1; // 1s | ||||
|  | ||||
| /* Input register memory map | ||||
|  * (All registers are 16-bit) | ||||
|  * ------------------------- | ||||
|  * | ||||
|  * 30010 : CO2  [ppm]        Unsigned value in range [0,40000] | ||||
|  * 30011 : T    [deg_C * 10] From SHT4x; unsigned value in range [0; 1250]; e.g. 21.5 C => 215 | ||||
|  * 30012 : RH   [%]          From SHT4x; unsigned value in range [0; 100] | ||||
|  * | ||||
|  * Backup T and RH sensor: | ||||
|  * 30013 : T    [deg_C * 10] From SCD4x; unsigned value in range [0; 600]; e.g. 21.5 C => 215 | ||||
|  * 30014 : RH   [%]          From SCD4x; unsigned value in range [0; 100] | ||||
|  * | ||||
|  * Extended temperature range (signed values): | ||||
|  * 30015 : T    [deg_C * 10] From SHT4x; signed value (two's complement) in range [-400;1250] | ||||
|  * 30016 : T    [deg_C * 10] From SCD4x; signed value (two's complement) in range [-100;600]; e.g. -12.3 C => -123 | ||||
|  * | ||||
|  */ | ||||
| /* Input registers memory map implementation */ | ||||
| enum | ||||
| { | ||||
| 	REGISTER_NUM_T = 30010, /* deg C */ | ||||
| 	REGISTER_NUM_T_F = 30011, /* deg F */ | ||||
| 	REGISTER_NUM_RH = 30012 /* %, from SHT4x */ | ||||
|  | ||||
| 	/* VOC Index has initial blackout beriod, when the data is not ready. VOC index is 0 during this period */ | ||||
| } data_registers_numbers; | ||||
|  | ||||
| enum | ||||
| { | ||||
| 	REGISTER_NUM_MODBUS_ADDR = 40007, | ||||
| 	REGISTER_NUM_BAUDRATE = 40008, | ||||
| 	REGISTER_NUM_RESET_DEVICE = 40100 | ||||
| } config_registers_numbers; | ||||
|  | ||||
| enum | ||||
| { | ||||
| 	REGISTER_NUM_VENDOR_NAME = 30010, | ||||
| 	REGISTER_NUM_PRODUCT_CODE = 30011, | ||||
| 	REGISTER_NUM_REVISION = 30012, | ||||
| 	REGISTER_NUM_PRODUCT_NAME = 30013, | ||||
| 	REGISTER_NUM_SERIAL_NUMBER = 30014 | ||||
| } identification_registers_numbers; | ||||
|  | ||||
| /* Variables to store the measured data */ | ||||
| int16_t T_SHT4x, TF_SHT4x; | ||||
| uint16_t RH_SHT4x; | ||||
| uint16_t light_ch0, light_ch1; | ||||
|  | ||||
| /* Struct to store the sensor config */ | ||||
| config_t sensor_config; | ||||
| /* Device ID struct */ | ||||
| modbus_device_id_t device_id; | ||||
| uint8_t sensor_config_pending_write = 0; | ||||
| uint8_t baudrate_changed = 0; | ||||
| uint8_t modbus_address_changed = 0; | ||||
| uint8_t co2_valid = 0; | ||||
|  | ||||
|  | ||||
| /* USER CODE END PV */ | ||||
|  | ||||
| /* Private function prototypes -----------------------------------------------*/ | ||||
| void SystemClock_Config(void); | ||||
| static void MX_GPIO_Init(void); | ||||
| static void MX_I2C1_Init(void); | ||||
| static void MX_USART2_UART_Init(void); | ||||
| static void MX_TIM21_Init(void); | ||||
| static void MX_IWDG_Init(void); | ||||
| /* USER CODE BEGIN PFP */ | ||||
| void USART2_TX_Buffer(uint8_t* buffer_tx, uint16_t buffer_tx_len); | ||||
| /* USER CODE END PFP */ | ||||
|  | ||||
| /* Private user code ---------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN 0 */ | ||||
|  | ||||
| /* USER CODE END 0 */ | ||||
|  | ||||
| /** | ||||
|   * @brief  The application entry point. | ||||
|   * @retval int | ||||
|   */ | ||||
| int main(void) | ||||
| { | ||||
|   /* USER CODE BEGIN 1 */ | ||||
|  | ||||
|   /* USER CODE END 1 */ | ||||
|  | ||||
|   /* MCU Configuration--------------------------------------------------------*/ | ||||
|  | ||||
|   /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ | ||||
|  | ||||
|   LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_SYSCFG); | ||||
|   LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_PWR); | ||||
|  | ||||
|   /* System interrupt init*/ | ||||
|   /* SysTick_IRQn interrupt configuration */ | ||||
|   NVIC_SetPriority(SysTick_IRQn, 3); | ||||
|  | ||||
|   /* USER CODE BEGIN Init */ | ||||
|  | ||||
|   /* USER CODE END Init */ | ||||
|  | ||||
|   /* Configure the system clock */ | ||||
|   SystemClock_Config(); | ||||
|  | ||||
|   /* USER CODE BEGIN SysInit */ | ||||
|   /* Read config from EEPROM - if unsuccessful, set the default values*/ | ||||
|   int8_t config_read_status = config_read(&sensor_config); | ||||
|   if (config_read_status != CONFIG_OK) | ||||
|   { | ||||
| 	  sensor_config.modbus_addr = MODBUS_DEFAULT_SLAVE_ADDRESS; | ||||
| 	  sensor_config.led_co2_alert_limit1 = CONFIG_DEFAULT_LED_ALERT1_LIMIT; | ||||
| 	  sensor_config.led_co2_alert_limit2 = CONFIG_DEFAULT_LED_ALERT2_LIMIT; | ||||
| 	  sensor_config.led_on = CONFIG_DEFAULT_LED_ON; | ||||
| 	  sensor_config.led_brightness = CONFIG_DEFAULT_LED_BRIGHTNESS; | ||||
| 	  sensor_config.led_smooth = CONFIG_DEFAULT_LED_SMOOTH; | ||||
| 	  sensor_config.scd4x_t_offset = CONFIG_DEFAULT_SCD4x_T_OFFSET; | ||||
| 	  sensor_config.baudrate_index = CONFIG_DEFAULT_BAUDRATE_INDEX; | ||||
|   } | ||||
|   /* USER CODE END SysInit */ | ||||
|  | ||||
|   /* Initialize all configured peripherals */ | ||||
|   MX_GPIO_Init(); | ||||
|   MX_I2C1_Init(); | ||||
|   MX_USART2_UART_Init(); | ||||
|   MX_TIM21_Init(); | ||||
|   MX_IWDG_Init(); | ||||
|   /* USER CODE BEGIN 2 */ | ||||
|  | ||||
|   /* Turn on MAGENTA LED to signal startup state */ | ||||
| //  LL_GPIO_ResetOutputPin(LED_R_GPIO_Port, LED_R_Pin); | ||||
| //  LL_GPIO_ResetOutputPin(LED_G_GPIO_Port, LED_G_Pin); | ||||
| //  LL_GPIO_ResetOutputPin(LED_B_GPIO_Port, LED_B_Pin); | ||||
|  | ||||
|   /* Enable I2C for sensors */ | ||||
|   LL_I2C_Enable(I2C1); | ||||
|  | ||||
|  | ||||
|   /* Set the modbus address */ | ||||
|   modbus_slave_set_address(sensor_config.modbus_addr); | ||||
|  | ||||
|   /* Enable UART for RS485 */ | ||||
|   LL_USART_Enable(USART2); | ||||
|  | ||||
|   /* Start the timer for measurement triggering */ | ||||
|   LL_TIM_EnableCounter(TIM21); | ||||
|   LL_TIM_EnableIT_UPDATE(TIM21); | ||||
|  | ||||
|   /* I2C context init (for SHT4x and SCD4x) */ | ||||
|   i2c_context_t i2c_context; | ||||
|   i2c_context.i2c = I2C1; | ||||
|   i2c_init(&i2c_context); | ||||
|  | ||||
|   /* Fill device ID struct */ | ||||
|   device_id.object_name.VendorName         = "Veles Labs"; | ||||
|   device_id.object_name.ProductCode        = "124C41"; | ||||
|   device_id.object_name.MajorMinorRevision = "1.2"; | ||||
|   device_id.object_name.VendorUrl          = "https://veles-labs.com"; | ||||
|   device_id.object_name.ProductName        = "RHT_Wired"; | ||||
|   device_id.object_name.ModelName          = "Hugo"; | ||||
|   modbus_slave_init_device_id(&device_id); | ||||
|   /* USER CODE END 2 */ | ||||
|  | ||||
|   /* Infinite loop */ | ||||
|   /* USER CODE BEGIN WHILE */ | ||||
|  | ||||
|   /* Wait 1000ms for sensors initialization */ | ||||
|   /* SHT4x Init Time:  max 1 	ms 	(datasheet pg. 8) */ | ||||
|   /* LTR329 Init Time: max 100 	ms 	(datasheet pg. 9) */ | ||||
|   LL_mDelay(1000); | ||||
|  | ||||
|   /* Initiualize LTR329 */ | ||||
|   int8_t ltr_ret; | ||||
|   uint8_t ltr_cont_reg_val; | ||||
|   uint8_t ltr_rate_reg_val; | ||||
|   do | ||||
|   { | ||||
| 	  ltr_ret = ltr329_write_settings(LTR329_GAIN_48X, LTR329_MODE_ACTIVE, LTR329_INTEGRATION_100MS, LTR329_MEAS_RATE_100MS); | ||||
| 	  /* TODO: Check register status */ | ||||
|   } while (ltr_ret != 0); | ||||
|  | ||||
|  | ||||
|   static uint32_t new_baud; | ||||
|   /* Enter the main loop */ | ||||
|   while (1) | ||||
|   { | ||||
| 	  /* Watchdog is set to 28 s (37 kHz, prescaler 256, 4095 reload value) */ | ||||
| 	  LL_IWDG_ReloadCounter(IWDG); | ||||
| 	  if (usart2_rx_done == 1) | ||||
| 	  { | ||||
| 	  /* Process the message */ | ||||
| 		  if (usart2_rx_message_too_long) | ||||
| 		  { | ||||
| 			  /* Do nothing, just delete the buffer and set the flag back to zero*/ | ||||
| 			  usart2_rx_message_too_long = 0; | ||||
| 		  } else | ||||
| 		  { | ||||
| 			  /* Process the message: | ||||
| 			   * message is stored in modbus_buffer[], no copying necessary; | ||||
| 			   * but we need to make sure that modbus_buffer[] will not be used while | ||||
| 			   * processing the message: this can be done by disabling RX interrupt */ | ||||
| 			  LL_USART_DisableIT_RXNE(USART2); | ||||
| 			  modbus_slave_process_msg(modbus_buffer, usart2_rx_message_len); | ||||
| 			  /* Reset the RX DONE flag */ | ||||
| 			  usart2_rx_done = 0; | ||||
| 			  LL_USART_EnableIT_RXNE(USART2); | ||||
| 		  } | ||||
| 		  /* Reset the RX DONE flag */ | ||||
| 		  usart2_rx_done = 0; | ||||
| 	  } | ||||
| 	  /* if config changed (MODBUS write), reflect changes to EEPROM */ | ||||
| 	  if (sensor_config_pending_write) { | ||||
| 		  config_write(&sensor_config); | ||||
| 		  sensor_config_pending_write = 0; | ||||
| 	  } | ||||
| 	  if (modbus_address_changed) | ||||
| 	  { | ||||
| 		  modbus_slave_set_address(sensor_config.modbus_addr); | ||||
| 		  modbus_address_changed = 0; | ||||
| 	  } | ||||
| 	  if (baudrate_changed) | ||||
| 	  { | ||||
| 		  while (!LL_USART_IsActiveFlag_TXE(USART2)); | ||||
| 		  uart_disable_interrupts(); | ||||
|  | ||||
| 		  LL_USART_SetBaudRate(USART2, SYSTICK_FREQ_HZ, LL_USART_OVERSAMPLING_16, config_baudrates[sensor_config.baudrate_index]); | ||||
| 		  uart_enable_interrupts(); | ||||
| 		  LL_USART_EnableDirectionRx(USART2); | ||||
| 		  LL_USART_EnableDirectionTx(USART2); | ||||
| 		  baudrate_changed = 0; | ||||
|  | ||||
| 		  new_baud = LL_USART_GetBaudRate(USART2, SYSTICK_FREQ_HZ, LL_USART_OVERSAMPLING_16); | ||||
| 	  } | ||||
| 	  /* It is time for measurement */ | ||||
| 	  if (tim21_elapsed_period == 1) | ||||
| 	  { | ||||
| 		  /* TODO: Check the data */ | ||||
| 		  /* Read SHT4x data (always connected) */ | ||||
| 		  sht4x_measure(&T_SHT4x, &RH_SHT4x); | ||||
| 		  TF_SHT4x = CELSIUS_TO_FAHRENHEIT(T_SHT4x); | ||||
|  | ||||
| 		  /* Red LTR329 values */ | ||||
| 		  uint8_t data_valid, new_data; | ||||
| 		  ltr329_gain_t set_gain; | ||||
| 		  ltr_ret = ltr329_read_status_register(&data_valid, &new_data, &set_gain); | ||||
| 		  /* TODO: Do something with the flags */ | ||||
| 		  ltr_ret = ltr329_measure(&light_ch0, &light_ch1); | ||||
|  | ||||
| 		  /* Reset the TIM21 Elapsed Period Flag */ | ||||
| 		  tim21_elapsed_period = 0; | ||||
| 	  } | ||||
|     /* USER CODE END WHILE */ | ||||
|  | ||||
|     /* USER CODE BEGIN 3 */ | ||||
|   } | ||||
|   /* USER CODE END 3 */ | ||||
| } | ||||
|  | ||||
| /** | ||||
|   * @brief System Clock Configuration | ||||
|   * @retval None | ||||
|   */ | ||||
| void SystemClock_Config(void) | ||||
| { | ||||
|   LL_FLASH_SetLatency(LL_FLASH_LATENCY_0); | ||||
|   while(LL_FLASH_GetLatency()!= LL_FLASH_LATENCY_0) | ||||
|   { | ||||
|   } | ||||
|   LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE1); | ||||
|   LL_RCC_HSI_Enable(); | ||||
|  | ||||
|    /* Wait till HSI is ready */ | ||||
|   while(LL_RCC_HSI_IsReady() != 1) | ||||
|   { | ||||
|  | ||||
|   } | ||||
|   LL_RCC_HSI_SetCalibTrimming(16); | ||||
|   LL_RCC_LSI_Enable(); | ||||
|  | ||||
|    /* Wait till LSI is ready */ | ||||
|   while(LL_RCC_LSI_IsReady() != 1) | ||||
|   { | ||||
|  | ||||
|   } | ||||
|   LL_RCC_PLL_ConfigDomain_SYS(LL_RCC_PLLSOURCE_HSI, LL_RCC_PLL_MUL_3, LL_RCC_PLL_DIV_4); | ||||
|   LL_RCC_PLL_Enable(); | ||||
|  | ||||
|    /* Wait till PLL is ready */ | ||||
|   while(LL_RCC_PLL_IsReady() != 1) | ||||
|   { | ||||
|  | ||||
|   } | ||||
|   LL_RCC_SetAHBPrescaler(LL_RCC_SYSCLK_DIV_1); | ||||
|   LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); | ||||
|   LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); | ||||
|   LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_PLL); | ||||
|  | ||||
|    /* Wait till System clock is ready */ | ||||
|   while(LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL) | ||||
|   { | ||||
|  | ||||
|   } | ||||
|  | ||||
|   LL_Init1msTick(12000000); | ||||
|  | ||||
|   LL_SetSystemCoreClock(12000000); | ||||
|   LL_RCC_SetUSARTClockSource(LL_RCC_USART2_CLKSOURCE_PCLK1); | ||||
|   LL_RCC_SetI2CClockSource(LL_RCC_I2C1_CLKSOURCE_PCLK1); | ||||
| } | ||||
|  | ||||
| /** | ||||
|   * @brief I2C1 Initialization Function | ||||
|   * @param None | ||||
|   * @retval None | ||||
|   */ | ||||
| static void MX_I2C1_Init(void) | ||||
| { | ||||
|  | ||||
|   /* USER CODE BEGIN I2C1_Init 0 */ | ||||
|  | ||||
|   /* USER CODE END I2C1_Init 0 */ | ||||
|  | ||||
|   LL_I2C_InitTypeDef I2C_InitStruct = {0}; | ||||
|  | ||||
|   LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; | ||||
|  | ||||
|   LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA); | ||||
|   /**I2C1 GPIO Configuration | ||||
|   PA9   ------> I2C1_SCL | ||||
|   PA10   ------> I2C1_SDA | ||||
|   */ | ||||
|   GPIO_InitStruct.Pin = LL_GPIO_PIN_9; | ||||
|   GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; | ||||
|   GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; | ||||
|   GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN; | ||||
|   GPIO_InitStruct.Pull = LL_GPIO_PULL_UP; | ||||
|   GPIO_InitStruct.Alternate = LL_GPIO_AF_1; | ||||
|   LL_GPIO_Init(GPIOA, &GPIO_InitStruct); | ||||
|  | ||||
|   GPIO_InitStruct.Pin = LL_GPIO_PIN_10; | ||||
|   GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; | ||||
|   GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; | ||||
|   GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_OPENDRAIN; | ||||
|   GPIO_InitStruct.Pull = LL_GPIO_PULL_UP; | ||||
|   GPIO_InitStruct.Alternate = LL_GPIO_AF_1; | ||||
|   LL_GPIO_Init(GPIOA, &GPIO_InitStruct); | ||||
|  | ||||
|   /* Peripheral clock enable */ | ||||
|   LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_I2C1); | ||||
|  | ||||
|   /* USER CODE BEGIN I2C1_Init 1 */ | ||||
|  | ||||
|   /* USER CODE END I2C1_Init 1 */ | ||||
|   /** I2C Initialization | ||||
|   */ | ||||
|   LL_I2C_EnableAutoEndMode(I2C1); | ||||
|   LL_I2C_DisableOwnAddress2(I2C1); | ||||
|   LL_I2C_DisableGeneralCall(I2C1); | ||||
|   LL_I2C_EnableClockStretching(I2C1); | ||||
|   I2C_InitStruct.PeripheralMode = LL_I2C_MODE_I2C; | ||||
|   I2C_InitStruct.Timing = 0x40000A0B; | ||||
|   I2C_InitStruct.AnalogFilter = LL_I2C_ANALOGFILTER_ENABLE; | ||||
|   I2C_InitStruct.DigitalFilter = 0; | ||||
|   I2C_InitStruct.OwnAddress1 = 0; | ||||
|   I2C_InitStruct.TypeAcknowledge = LL_I2C_ACK; | ||||
|   I2C_InitStruct.OwnAddrSize = LL_I2C_OWNADDRESS1_7BIT; | ||||
|   LL_I2C_Init(I2C1, &I2C_InitStruct); | ||||
|   LL_I2C_SetOwnAddress2(I2C1, 0, LL_I2C_OWNADDRESS2_NOMASK); | ||||
|   /* USER CODE BEGIN I2C1_Init 2 */ | ||||
|   /* USER CODE END I2C1_Init 2 */ | ||||
|  | ||||
| } | ||||
|  | ||||
| /** | ||||
|   * @brief IWDG Initialization Function | ||||
|   * @param None | ||||
|   * @retval None | ||||
|   */ | ||||
| static void MX_IWDG_Init(void) | ||||
| { | ||||
|  | ||||
|   /* USER CODE BEGIN IWDG_Init 0 */ | ||||
|  | ||||
|   /* USER CODE END IWDG_Init 0 */ | ||||
|  | ||||
|   /* USER CODE BEGIN IWDG_Init 1 */ | ||||
|  | ||||
|   /* USER CODE END IWDG_Init 1 */ | ||||
|   LL_IWDG_Enable(IWDG); | ||||
|   LL_IWDG_EnableWriteAccess(IWDG); | ||||
|   LL_IWDG_SetPrescaler(IWDG, LL_IWDG_PRESCALER_256); | ||||
|   LL_IWDG_SetReloadCounter(IWDG, 4095); | ||||
|   while (LL_IWDG_IsReady(IWDG) != 1) | ||||
|   { | ||||
|   } | ||||
|  | ||||
|   LL_IWDG_ReloadCounter(IWDG); | ||||
|   /* USER CODE BEGIN IWDG_Init 2 */ | ||||
|  | ||||
|   /* USER CODE END IWDG_Init 2 */ | ||||
|  | ||||
| } | ||||
|  | ||||
| /** | ||||
|   * @brief TIM21 Initialization Function | ||||
|   * @param None | ||||
|   * @retval None | ||||
|   */ | ||||
| static void MX_TIM21_Init(void) | ||||
| { | ||||
|  | ||||
|   /* USER CODE BEGIN TIM21_Init 0 */ | ||||
|  | ||||
|   /* USER CODE END TIM21_Init 0 */ | ||||
|  | ||||
|   LL_TIM_InitTypeDef TIM_InitStruct = {0}; | ||||
|  | ||||
|   /* Peripheral clock enable */ | ||||
|   LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_TIM21); | ||||
|  | ||||
|   /* TIM21 interrupt Init */ | ||||
|   NVIC_SetPriority(TIM21_IRQn, 0); | ||||
|   NVIC_EnableIRQ(TIM21_IRQn); | ||||
|  | ||||
|   /* USER CODE BEGIN TIM21_Init 1 */ | ||||
|  | ||||
|   /* USER CODE END TIM21_Init 1 */ | ||||
|   TIM_InitStruct.Prescaler = tim21_prescaler; | ||||
|   TIM_InitStruct.CounterMode = LL_TIM_COUNTERMODE_UP; | ||||
|   TIM_InitStruct.Autoreload = tim21_period; | ||||
|   TIM_InitStruct.ClockDivision = LL_TIM_CLOCKDIVISION_DIV1; | ||||
|   LL_TIM_Init(TIM21, &TIM_InitStruct); | ||||
|   LL_TIM_EnableARRPreload(TIM21); | ||||
|   LL_TIM_SetClockSource(TIM21, LL_TIM_CLOCKSOURCE_INTERNAL); | ||||
|   LL_TIM_SetTriggerOutput(TIM21, LL_TIM_TRGO_RESET); | ||||
|   LL_TIM_DisableMasterSlaveMode(TIM21); | ||||
|   /* USER CODE BEGIN TIM21_Init 2 */ | ||||
|  | ||||
|   /* USER CODE END TIM21_Init 2 */ | ||||
|  | ||||
| } | ||||
|  | ||||
| /** | ||||
|   * @brief USART2 Initialization Function | ||||
|   * @param None | ||||
|   * @retval None | ||||
|   */ | ||||
| static void MX_USART2_UART_Init(void) | ||||
| { | ||||
|  | ||||
|   /* USER CODE BEGIN USART2_Init 0 */ | ||||
|  | ||||
|   /* USER CODE END USART2_Init 0 */ | ||||
|  | ||||
|   LL_USART_InitTypeDef USART_InitStruct = {0}; | ||||
|  | ||||
|   LL_GPIO_InitTypeDef GPIO_InitStruct = {0}; | ||||
|  | ||||
|   /* Peripheral clock enable */ | ||||
|   LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2); | ||||
|  | ||||
|   LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA); | ||||
|   /**USART2 GPIO Configuration | ||||
|   PA1   ------> USART2_DE | ||||
|   PA2   ------> USART2_TX | ||||
|   PA3   ------> USART2_RX | ||||
|   */ | ||||
|   GPIO_InitStruct.Pin = LL_GPIO_PIN_1; | ||||
|   GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; | ||||
|   GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; | ||||
|   GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; | ||||
|   GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; | ||||
|   GPIO_InitStruct.Alternate = LL_GPIO_AF_4; | ||||
|   LL_GPIO_Init(GPIOA, &GPIO_InitStruct); | ||||
|  | ||||
|   GPIO_InitStruct.Pin = LL_GPIO_PIN_2; | ||||
|   GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; | ||||
|   GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; | ||||
|   GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; | ||||
|   GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; | ||||
|   GPIO_InitStruct.Alternate = LL_GPIO_AF_4; | ||||
|   LL_GPIO_Init(GPIOA, &GPIO_InitStruct); | ||||
|  | ||||
|   GPIO_InitStruct.Pin = LL_GPIO_PIN_3; | ||||
|   GPIO_InitStruct.Mode = LL_GPIO_MODE_ALTERNATE; | ||||
|   GPIO_InitStruct.Speed = LL_GPIO_SPEED_FREQ_VERY_HIGH; | ||||
|   GPIO_InitStruct.OutputType = LL_GPIO_OUTPUT_PUSHPULL; | ||||
|   GPIO_InitStruct.Pull = LL_GPIO_PULL_NO; | ||||
|   GPIO_InitStruct.Alternate = LL_GPIO_AF_4; | ||||
|   LL_GPIO_Init(GPIOA, &GPIO_InitStruct); | ||||
|  | ||||
|   /* USART2 interrupt Init */ | ||||
|   NVIC_SetPriority(USART2_IRQn, 0); | ||||
|   NVIC_EnableIRQ(USART2_IRQn); | ||||
|  | ||||
|   /* USER CODE BEGIN USART2_Init 1 */ | ||||
|  | ||||
|   /* USER CODE END USART2_Init 1 */ | ||||
|   USART_InitStruct.BaudRate = config_baudrates[sensor_config.baudrate_index]; | ||||
|   USART_InitStruct.DataWidth = LL_USART_DATAWIDTH_9B; | ||||
|   USART_InitStruct.StopBits = LL_USART_STOPBITS_1; | ||||
|   USART_InitStruct.Parity = LL_USART_PARITY_EVEN; | ||||
|   USART_InitStruct.TransferDirection = LL_USART_DIRECTION_TX_RX; | ||||
|   USART_InitStruct.HardwareFlowControl = LL_USART_HWCONTROL_NONE; | ||||
|   USART_InitStruct.OverSampling = LL_USART_OVERSAMPLING_16; | ||||
|   LL_USART_Init(USART2, &USART_InitStruct); | ||||
|   LL_USART_EnableDEMode(USART2); | ||||
|   LL_USART_SetDESignalPolarity(USART2, LL_USART_DE_POLARITY_HIGH); | ||||
|   LL_USART_SetDEAssertionTime(USART2, 0); | ||||
|   LL_USART_SetDEDeassertionTime(USART2, 0); | ||||
|   LL_USART_ConfigAsyncMode(USART2); | ||||
|   LL_USART_Enable(USART2); | ||||
|   /* USER CODE BEGIN USART2_Init 2 */ | ||||
|  | ||||
|   /* Enable IDLE Interrupt */ | ||||
|   LL_USART_EnableIT_IDLE(USART2); | ||||
|  | ||||
|   /* Enable RX Not Empty Interrupt */ | ||||
|   LL_USART_EnableIT_RXNE(USART2); | ||||
|  | ||||
|   LL_USART_EnableDirectionRx(USART2); | ||||
|   LL_USART_EnableDirectionTx(USART2); | ||||
|   LL_USART_Enable(USART2); | ||||
|   /* USER CODE END USART2_Init 2 */ | ||||
|  | ||||
| } | ||||
|  | ||||
| /** | ||||
|   * @brief GPIO Initialization Function | ||||
|   * @param None | ||||
|   * @retval None | ||||
|   */ | ||||
| static void MX_GPIO_Init(void) | ||||
| { | ||||
|  | ||||
|   /* GPIO Ports Clock Enable */ | ||||
|   LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA); | ||||
|  | ||||
| } | ||||
|  | ||||
| /* USER CODE BEGIN 4 */ | ||||
| void USART2_TX_Buffer(uint8_t* buffer_tx, uint16_t buffer_tx_len) | ||||
| { | ||||
| 	__disable_irq(); | ||||
| 	for (uint16_t i = 0; i < buffer_tx_len; i++) | ||||
| 	{ | ||||
| 		  LL_USART_TransmitData9(USART2, buffer_tx[i]); | ||||
| 		  while (!LL_USART_IsActiveFlag_TXE(USART2)); | ||||
| 	} | ||||
| 	__enable_irq(); | ||||
| } | ||||
|  | ||||
| int8_t uart_disable_interrupts(void) | ||||
| { | ||||
| 	LL_USART_Disable(USART2); | ||||
| 	LL_USART_DisableIT_IDLE(USART2); | ||||
| 	LL_USART_EnableIT_RXNE(USART2); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int8_t uart_enable_interrupts(void) | ||||
| { | ||||
| 	LL_USART_Enable(USART2); | ||||
| 	LL_USART_EnableIT_IDLE(USART2); | ||||
| 	LL_USART_EnableIT_RXNE(USART2); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int8_t modbus_slave_callback(modbus_transaction_t *transaction) | ||||
| { | ||||
| 	uint16_t register_number = transaction->register_number; | ||||
| 	switch (transaction->function_code) | ||||
| 	{ | ||||
| 		case MODBUS_READ_INPUT_REGISTERS: | ||||
| 			for (int i = 0; i < transaction->register_count; i++, register_number++) | ||||
| 			{ | ||||
| 				switch (register_number) | ||||
| 				{ | ||||
| 					case REGISTER_NUM_T: | ||||
| 						transaction->input_registers_signed[i] = (int16_t)T_SHT4x; | ||||
| 						break; | ||||
| 					case REGISTER_NUM_T_F: | ||||
| 						transaction->input_registers_signed[i] = (int16_t)TF_SHT4x; | ||||
| 						break; | ||||
| 					case REGISTER_NUM_RH: | ||||
| 						transaction->input_registers[i] = (uint16_t)RH_SHT4x; | ||||
| 						break; | ||||
| 					default: | ||||
| 						return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED; | ||||
| 				} | ||||
| 			} | ||||
| 			return MODBUS_OK; | ||||
| 		case MODBUS_READ_HOLDING_REGISTERS: | ||||
| 			for (int i = 0; i < transaction->register_count; i++, register_number++) | ||||
| 			{ | ||||
| 				switch (register_number) | ||||
| 				{ | ||||
| 					case REGISTER_NUM_MODBUS_ADDR: | ||||
| 						transaction->holding_registers[i] = (uint16_t)(sensor_config.modbus_addr); | ||||
| 						break; | ||||
| 					case REGISTER_NUM_BAUDRATE: | ||||
| 						transaction->holding_registers[i] = (uint16_t)(sensor_config.baudrate_index); | ||||
| 						break; | ||||
| 					default: | ||||
| 						return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED; | ||||
| 				} | ||||
| 			} | ||||
| 			return MODBUS_OK; | ||||
| 		case MODBUS_WRITE_SINGLE_REGISTER: | ||||
| 			transaction->register_count = 1; | ||||
| 		case MODBUS_WRITE_MULTIPLE_REGISTERS: | ||||
| 			for (int i = 0; i < transaction->register_count; i++, register_number++) | ||||
| 			{ | ||||
| 				switch (register_number) | ||||
| 				{ | ||||
| 					case REGISTER_NUM_MODBUS_ADDR: | ||||
| 						/* allowed values: [1, 247] (from 1 to 247) as given in section 2.1 of document | ||||
| 						 * MODBUS over Serial Line: Specification and Implementation Guide (V1.02) */ | ||||
| 						if (transaction->holding_registers[i] < 1 || transaction->holding_registers[i] > 247) { | ||||
| 							return MODBUS_ERROR_OUT_OF_BOUNDS; | ||||
| 						} | ||||
| 						sensor_config.modbus_addr = (uint16_t) transaction->holding_registers[i]; | ||||
| 						modbus_address_changed = 1; | ||||
| 						break; | ||||
| 					case REGISTER_NUM_BAUDRATE: | ||||
| 						/* allowed values: [0, 9] (from 0 to 9, where 9 == config_baudrates_length) */ | ||||
| 						if (transaction->holding_registers[i] >= config_baudrates_length) { | ||||
| 							return MODBUS_ERROR_OUT_OF_BOUNDS; | ||||
| 						} | ||||
| 						sensor_config.baudrate_index = (uint16_t) (transaction->holding_registers[i]); | ||||
| 						baudrate_changed = 1; | ||||
| 						break; | ||||
| 					case REGISTER_NUM_RESET_DEVICE: | ||||
| 						if (transaction->holding_registers[i] == 0xABCD) { | ||||
| 							/* software reset */ | ||||
| 							NVIC_SystemReset(); | ||||
| 						} else { | ||||
| 							return MODBUS_ERROR_OUT_OF_BOUNDS; | ||||
| 						} | ||||
| 						break; | ||||
| 					default: | ||||
| 						return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED; | ||||
| 				} | ||||
| 			} | ||||
| 			sensor_config_pending_write = 1; | ||||
| 			return MODBUS_OK; | ||||
| 		default: | ||||
| 			return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED; | ||||
| 	} | ||||
| 	/* Catch-all error */ | ||||
| 	return MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED; /* nothing implemented yet! TODO */ | ||||
| } | ||||
|  | ||||
| int8_t modbus_transmit_function(uint8_t *buffer, uint16_t data_len) | ||||
| { | ||||
| 	 /* TODO */ | ||||
| 	USART2_TX_Buffer(buffer, data_len); | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
| /* USER CODE END 4 */ | ||||
|  | ||||
| /** | ||||
|   * @brief  This function is executed in case of error occurrence. | ||||
|   * @retval None | ||||
|   */ | ||||
| void Error_Handler(void) | ||||
| { | ||||
|   /* USER CODE BEGIN Error_Handler_Debug */ | ||||
|   /* User can add his own implementation to report the HAL error return state */ | ||||
|   __disable_irq(); | ||||
|   while (1) | ||||
|   { | ||||
|   } | ||||
|   /* USER CODE END Error_Handler_Debug */ | ||||
| } | ||||
|  | ||||
| #ifdef  USE_FULL_ASSERT | ||||
| /** | ||||
|   * @brief  Reports the name of the source file and the source line number | ||||
|   *         where the assert_param error has occurred. | ||||
|   * @param  file: pointer to the source file name | ||||
|   * @param  line: assert_param error line source number | ||||
|   * @retval None | ||||
|   */ | ||||
| void assert_failed(uint8_t *file, uint32_t line) | ||||
| { | ||||
|   /* USER CODE BEGIN 6 */ | ||||
|   /* User can add his own implementation to report the file name and line number, | ||||
|      ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ | ||||
|   /* USER CODE END 6 */ | ||||
| } | ||||
| #endif /* USE_FULL_ASSERT */ | ||||
|  | ||||
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ | ||||
							
								
								
									
										431
									
								
								fw/Core/Src/modbus.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										431
									
								
								fw/Core/Src/modbus.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,431 @@ | ||||
| /* | ||||
|  * modbus.c | ||||
|  * | ||||
|  *  Created on: Jul 18, 2021 | ||||
|  *      Author: user | ||||
|  */ | ||||
|  | ||||
| #include "modbus.h" | ||||
|  | ||||
| /* | ||||
|  * Global variables | ||||
|  */ | ||||
|  | ||||
| /* Modbus TX buffer; can be also used for RX in memory constrained systems (e.g. in main.c); | ||||
|  * NOTE if shared buffer is used for TX/RX, care must be taken to prevent writing into buffer | ||||
|  * during execution of modbus_process_message() */ | ||||
| uint8_t modbus_buffer[MODBUS_MAX_RTU_FRAME_SIZE]; | ||||
|  | ||||
| /* MODBUS device address */ | ||||
| uint8_t modbus_slave_address = MODBUS_DEFAULT_SLAVE_ADDRESS; | ||||
|  | ||||
| /* Device ID struct */ | ||||
| modbus_device_id_t *modbus_device_id = NULL; | ||||
|  | ||||
| /* | ||||
|  * CRC16 functions | ||||
|  * see https://modbus.org/docs/Modbus_over_serial_line_V1_02.pdf | ||||
|  * section 6.2.2 | ||||
|  */ | ||||
|  | ||||
| /* CRC16 (without memory mapped values) | ||||
|  * taken from https://ctlsys.com/support/how_to_compute_the_modbus_rtu_message_crc/ */ | ||||
| uint16_t modbus_CRC16(const uint8_t *buf, int len) | ||||
| { | ||||
| 	uint16_t crc = 0xFFFF; | ||||
|  | ||||
| 	for (int pos = 0; pos < len; pos++) { | ||||
| 		crc ^= (uint16_t)buf[pos];          // XOR byte into least sig. byte of crc | ||||
|         | ||||
| 		for (int i = 8; i != 0; i--) {    // Loop over each bit | ||||
| 			if ((crc & 0x0001) != 0) {      // If the LSB is set | ||||
| 				crc >>= 1;                    // Shift right and XOR 0xA001 | ||||
| 				crc ^= 0xA001; | ||||
| 			} else {                            // Else LSB is not set | ||||
| 				crc >>= 1;                    // Just shift right | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// Note, this number has low and high bytes swapped, so use it accordingly (or swap bytes) | ||||
| 	return crc;   | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Private functions | ||||
|  */ | ||||
|  | ||||
| static uint8_t modbus_fill_device_id_objects(uint8_t *buffer, modbus_transaction_t *transaction) | ||||
| { | ||||
| 	/* we assume buffer is 256 - MODBUS_READ_DEVICE_ID_RESPONSE_HEADER_LEN = 252 bytes long */ | ||||
| 	/* find out how many objects we copy to buffer */ | ||||
| 	int len; | ||||
| 	uint8_t object_index = transaction->object_id; | ||||
| 	uint8_t object_count; | ||||
| 	uint8_t more_follows = MODBUS_NO_MORE_FOLLOWS; | ||||
| 	uint8_t next_object_id; | ||||
| 	uint8_t last_object; | ||||
| 	const uint8_t max_len = 256 - MODBUS_READ_DEVICE_ID_RESPONSE_HEADER_LEN; | ||||
|  | ||||
| 	/* last object index */ | ||||
| 	if (transaction->read_device_id_code == MODBUS_CONFORMITY_BASIC) { | ||||
| 		last_object = MODBUS_BASIC_OBJECT_COUNT; | ||||
| 	} else if (transaction->read_device_id_code == MODBUS_CONFORMITY_REGULAR) { | ||||
| 		last_object = MODBUS_REGULAR_OBJECT_COUNT; | ||||
| 	/* extended not implemented */ | ||||
| //	} else if (transaction->read_device_id_code == MODBUS_CONFORMITY_EXTENDED){ | ||||
| //		last_object = MODBUS_EXTENDED_OBJECT_COUNT; | ||||
| 	} else { | ||||
| 		/* fallback: regular */ | ||||
| 		last_object = MODBUS_REGULAR_OBJECT_COUNT; | ||||
| 	} | ||||
| 	last_object--; // we need index | ||||
| 	/* copy as many objects as possible */ | ||||
| 	do { | ||||
| 		/* copy object */ | ||||
| 		int object_len = strlen(modbus_device_id->object_id[object_index]); | ||||
| 		if (len + object_len + 2 > max_len) { | ||||
| 			more_follows = MODBUS_MORE_FOLLOWS; | ||||
| 			next_object_id = object_index; | ||||
| 			break; | ||||
| 		} | ||||
| 		/* offset is for "more follows", "next object id", "object count" */ | ||||
| 		buffer[MODBUS_READ_DEVICE_ID_RESPONSE_OFFSET + len++] = object_index; | ||||
| 		buffer[MODBUS_READ_DEVICE_ID_RESPONSE_OFFSET + len++] = object_len; | ||||
| 		/* note that string copied to buffer is not null-terminated */ | ||||
| 		strncpy((char*)(buffer + len), (char*)modbus_device_id->object_id[object_index++], object_len); | ||||
| 		len += object_len; | ||||
| 		object_count++; | ||||
| 	} while (object_index < last_object); | ||||
| 	buffer[0] = more_follows; | ||||
| 	buffer[1] = next_object_id; | ||||
| 	buffer[2] = object_count; | ||||
| 	return MODBUS_READ_DEVICE_ID_RESPONSE_OFFSET + len; | ||||
| } | ||||
|  | ||||
| /* here we assume buffer has minimal size of MODBUS_MAX_RTU_FRAME_SIZE; | ||||
|  * this function is private, so hopefully it's going to be ok */ | ||||
| static int8_t modbus_transaction_to_buffer(uint8_t *buffer, uint8_t *msg_len, modbus_transaction_t *transaction) | ||||
| { | ||||
| 	uint16_t crc16; | ||||
| 	uint8_t byte_count; | ||||
| 	uint8_t buffer_pos = 0; | ||||
|  | ||||
| 	// TODO use relative indices (increments) instead of absolute | ||||
| 	buffer[buffer_pos++] = modbus_slave_address; | ||||
| 	buffer[buffer_pos++] = transaction->function_code; | ||||
| 	*msg_len = 5; | ||||
|  | ||||
| 	if (transaction->function_code & MODBUS_ERROR_FLAG) { | ||||
| 		/* sending error reply */ | ||||
| 		buffer[buffer_pos++] = transaction->exception; | ||||
| 	} else { | ||||
| 		switch (transaction->function_code) { | ||||
| 			case MODBUS_READ_HOLDING_REGISTERS: | ||||
| 			case MODBUS_READ_INPUT_REGISTERS: | ||||
| 				byte_count = transaction->register_count * 2; | ||||
| 				buffer[buffer_pos++] = byte_count; | ||||
| 				*msg_len = byte_count + 5; | ||||
| 				for (int i = 0; i < transaction->register_count; i++) { | ||||
| 					// TODO endianness handling | ||||
| 					/* buffer16b is alias for both holding and input register buffers */ | ||||
| 					buffer[buffer_pos++] = transaction->buffer16b[i] >> 8; | ||||
| 					buffer[buffer_pos++] = transaction->buffer16b[i] & 0xff; | ||||
| 				} | ||||
| 				break; | ||||
| 			case MODBUS_WRITE_SINGLE_REGISTER: | ||||
| 				buffer[buffer_pos++] = (uint8_t) (transaction->register_address >> 8); | ||||
| 				buffer[buffer_pos++] = (uint8_t) transaction->register_address; | ||||
| 				buffer[buffer_pos++] = (uint8_t) (transaction->holding_registers[0] >> 8); | ||||
| 				buffer[buffer_pos++] = (uint8_t) transaction->holding_registers[0]; | ||||
| 				*msg_len = 8; /* includes 2 bytes for CRC */ | ||||
| 				break; | ||||
| 			case MODBUS_WRITE_MULTIPLE_REGISTERS: | ||||
| 				buffer[buffer_pos++] = (uint8_t) (transaction->register_address >> 8); | ||||
| 				buffer[buffer_pos++] = (uint8_t) transaction->register_address; | ||||
| 				buffer[buffer_pos++] = (uint8_t) (transaction->register_count >> 8); | ||||
| 				buffer[buffer_pos++] = (uint8_t) transaction->register_count; | ||||
| 				*msg_len = 8; /* includes 2 bytes for CRC */ | ||||
| 				break; | ||||
| 			case MODBUS_READ_DEVICE_IDENTIFICATION: | ||||
| 				/* MEI type */ | ||||
| 				buffer[buffer_pos++] = MODBUS_MEI; | ||||
| 				/* read device id */ | ||||
| 				buffer[buffer_pos++] = transaction->read_device_id_code; | ||||
| 				/* conformity level */ | ||||
| 				buffer[buffer_pos++] = modbus_device_id->conformity_level; | ||||
| 				/* fill buffer with as many objects as possible  */ | ||||
| 				*msg_len = modbus_fill_device_id_objects(buffer+buffer_pos, transaction); | ||||
| 				*msg_len += 7; /* includes 2 bytes for CRC */ | ||||
| 				break; | ||||
| 		} | ||||
| 	} | ||||
| 	crc16 = modbus_CRC16(buffer, buffer_pos); /* last two bytes is the checksum itself */ | ||||
| 	buffer[buffer_pos++] = crc16 & 0xff; | ||||
| 	buffer[buffer_pos++] = crc16 >> 8; | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
|  | ||||
| static int8_t modbus_process_device_id_request(const uint8_t *buffer, int len, modbus_transaction_t *transaction) | ||||
| { | ||||
| 	uint8_t MEI_type; | ||||
| 	uint8_t read_device_id_code; | ||||
| 	uint8_t object_id; | ||||
| 	uint8_t buffer_pos = 0; | ||||
|  | ||||
| 	if (transaction->broadcast == 1) { | ||||
| 		/* Read device ID broadcast - invalid; ignore (master will get timeout) */ | ||||
| 		return MODBUS_ERROR; | ||||
| 	} | ||||
| 	if (modbus_device_id == NULL) { | ||||
| 		/* modbus_device_id not initialized; user should use modbus_slave_init_device_id() first */ | ||||
| 		transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DEVICE_ID_CODE; | ||||
| 		return MODBUS_OK; | ||||
| 	} | ||||
| 	if (len < MODBUS_READ_DEVICE_ID_REQUEST_LEN) { | ||||
| 		/* frame too short, ignore */ | ||||
| 		return MODBUS_ERROR; | ||||
| 	} | ||||
| 	/* next byte should be MEI = 0x0E */ | ||||
| 	MEI_type = buffer[buffer_pos++]; | ||||
| 	if (MEI_type != MODBUS_MEI) { | ||||
| 		/* invalid MEI, ignore. I have no idea what MEI does, but it should always be 0x0E */ | ||||
| 		return MODBUS_ERROR; | ||||
| 	} | ||||
| 	/* next byte is read device id code */ | ||||
| 	read_device_id_code = buffer[buffer_pos++]; | ||||
| 	/* read device id code can only have values 1,2,3,4 */ | ||||
| 	if (read_device_id_code < 1 || read_device_id_code > 4) { | ||||
| 		transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DEVICE_ID_CODE; | ||||
| 		return MODBUS_OK; | ||||
| 	} | ||||
| 	transaction->read_device_id_code = read_device_id_code; | ||||
| 	/* next byte is object id */ | ||||
| 	object_id = buffer[buffer_pos++]; | ||||
| 	transaction->object_id = object_id; | ||||
| 	if (object_id > MODBUS_DEVICE_ID_OBJECT_NUM) { | ||||
| 		/* illegal object ID */ | ||||
| 		transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS; | ||||
| 		return MODBUS_OK; | ||||
| 	} | ||||
| 	/* Message processed */ | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
|  | ||||
| /* returns ERROR only when no response to master is needed */ | ||||
| static int8_t modbus_process_read_write_request(const uint8_t *buffer, int len, modbus_transaction_t *transaction) | ||||
| { | ||||
| 	uint8_t byte_count; | ||||
| 	int8_t callback_result; | ||||
| 	uint8_t buffer_pos = 0; | ||||
|  | ||||
| 	/* set starting register number */ | ||||
| 	switch (transaction->function_code) { | ||||
| 	/* coils */ | ||||
| 	case MODBUS_READ_DO: | ||||
| 	case MODBUS_WRITE_SINGLE_DO: | ||||
| 	case MODBUS_WRITE_MULTIPLE_DO: | ||||
| 		transaction->register_number = MODBUS_DO_START_NUMBER; | ||||
| 		break; | ||||
| 	/* discrete inputs */ | ||||
| 	case MODBUS_READ_DI: | ||||
| 		transaction->register_number = MODBUS_DI_START_NUMBER; | ||||
| 		break; | ||||
| 	/* input registers */ | ||||
| 	case MODBUS_READ_AI: | ||||
| 		transaction->register_number = MODBUS_AI_START_NUMBER; | ||||
| 		break; | ||||
| 	/* holding registers */ | ||||
| 	case MODBUS_READ_AO: | ||||
| 	case MODBUS_WRITE_SINGLE_AO: | ||||
| 	case MODBUS_WRITE_MULTIPLE_AO: | ||||
| 	case MODBUS_READ_WRITE_MULTIPLE_REGISTERS: | ||||
| 		transaction->register_number = MODBUS_AO_START_NUMBER; | ||||
| 		break;	 | ||||
| 	} | ||||
|  | ||||
| 	#define MODBUS_FLAG_WRITE  0x01 | ||||
| 	#define MODBUS_FLAG_SINGLE 0x02 | ||||
| 	uint8_t flags = 0x00; | ||||
|  | ||||
| 	/* process message */ | ||||
| 	switch (transaction->function_code) { | ||||
| 	case MODBUS_WRITE_SINGLE_COIL: | ||||
| 	case MODBUS_WRITE_SINGLE_REGISTER: /* holding register */ | ||||
| 		flags |= MODBUS_FLAG_SINGLE; | ||||
| 	case MODBUS_WRITE_MULTIPLE_COILS: | ||||
| 	case MODBUS_WRITE_MULTIPLE_REGISTERS: | ||||
| 		flags |= MODBUS_FLAG_WRITE; | ||||
| 	case MODBUS_READ_DISCRETE_INPUTS: | ||||
| 	case MODBUS_READ_COILS: | ||||
| 	case MODBUS_READ_INPUT_REGISTERS: | ||||
| 	case MODBUS_READ_HOLDING_REGISTERS: | ||||
| 		if (len < MODBUS_MINIMAL_READWRITE_LEN) { | ||||
| 			/* buffer too short to contain everything we need */ | ||||
| 			return MODBUS_ERROR; | ||||
| 		} | ||||
| 		transaction->register_address = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1]; | ||||
| 		buffer += 2; | ||||
| 		// TODO check length! | ||||
| 		if (flags & MODBUS_FLAG_WRITE) { | ||||
| 			if (flags & MODBUS_FLAG_SINGLE) { | ||||
| 				transaction->holding_registers[0] = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1]; | ||||
| 				buffer_pos += 2; | ||||
| 			} else { | ||||
| 				/* Write multiple registers */ | ||||
| 				transaction->register_count = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1]; | ||||
| 				buffer_pos += 2; | ||||
| 				if (len < MODBUS_MINIMAL_WRITE_MULTIPLE_LEN) { | ||||
| 					return MODBUS_ERROR; | ||||
| 				} | ||||
| 				byte_count = buffer[buffer_pos++]; | ||||
| 				if (transaction->register_count > 123 || 2*transaction->register_count != byte_count) { | ||||
| 					/* Max number of register is defined by Modbus_Application_Protocol_V1_1b, section 6.12 */ | ||||
| 					transaction->exception = MODBUS_EXCEPTION_ILLEGAL_REGISTER_QUANTITY; | ||||
| 				} else { | ||||
| 					if (len < MODBUS_MINIMAL_WRITE_MULTIPLE_LEN + byte_count) { | ||||
| 						return MODBUS_ERROR; | ||||
| 					} | ||||
| 					for (uint8_t i = 0; i < transaction->register_count; i++) { | ||||
| 						transaction->holding_registers[i] = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1]; | ||||
| 						buffer_pos += 2; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} else { | ||||
| 			transaction->register_count = (buffer[buffer_pos] << 8) | buffer[buffer_pos + 1]; | ||||
| 			buffer_pos += 2; | ||||
| 			if ( | ||||
| 					transaction->register_count < 1 || | ||||
| 					transaction->register_count > MODBUS_MAX_REGISTERS | ||||
| 			   ) { | ||||
| 				transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DATA_VALUE; | ||||
| 			} | ||||
| 		} | ||||
| 		// add offset to register number | ||||
| 		transaction->register_number += transaction->register_address; | ||||
| 		break; | ||||
| 	default: | ||||
| 		/* function code not known / not implemented, reply with | ||||
| 		 * ExceptionCode 1 */ | ||||
| 		transaction->exception = MODBUS_EXCEPTION_ILLEGAL_FUNCTION; | ||||
| 		break; | ||||
| 	} | ||||
| 	/* data in modbus_buffer have been processed and buffer can be re-used for TX */ | ||||
| 	/* handle reply */ | ||||
| 	if (transaction->exception != 0) { | ||||
| 		/* indicate error */ | ||||
| 		transaction->function_code |= MODBUS_ERROR_FLAG; | ||||
| 	} else { | ||||
| 		callback_result = modbus_slave_callback(transaction); | ||||
| 		/* error handling */ | ||||
| 		if (callback_result != MODBUS_OK) { | ||||
| 			transaction->function_code |= MODBUS_ERROR_FLAG; | ||||
| 			if (callback_result == MODBUS_ERROR_FUNCTION_NOT_IMPLEMENTED) { | ||||
| 				transaction->exception = MODBUS_EXCEPTION_ILLEGAL_FUNCTION; | ||||
| 			} else if (callback_result == MODBUS_ERROR_REGISTER_NOT_IMPLEMENTED) { | ||||
| 				transaction->exception = MODBUS_EXCEPTION_ILLEGAL_DATA_ADDRESS; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Public function definitions | ||||
|  */ | ||||
|  | ||||
| int8_t modbus_slave_set_address(uint8_t address) | ||||
| { | ||||
| 	if (address == 0) { | ||||
| 		/* address 0 is broadcast address */ | ||||
| 		return MODBUS_ERROR; | ||||
| 	} | ||||
| 	modbus_slave_address = address; | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
|  | ||||
|  | ||||
| int8_t modbus_slave_process_msg(const uint8_t *buffer, int len) | ||||
| { | ||||
|  | ||||
|  | ||||
| 	/* | ||||
| 	 * TODO list: | ||||
| 	 * | ||||
| 	 * 1) check that errors and exceptions are handled according to Modbus_Application_Protocol_V1_1b.pdf | ||||
| 	 * 2) buffer overflow prevention: for each function code, check that buffer is long enough | ||||
| 	 */ | ||||
|  | ||||
|  | ||||
| 	/* transaction holds message context and content: | ||||
| 	 * it wraps all necessary buffers and variables */ | ||||
| 	modbus_transaction_t transaction; | ||||
| 	uint8_t buffer_pos = 0; | ||||
|  | ||||
| 	if (len < MODBUS_MINIMAL_FRAME_LEN) { | ||||
| 		/* frame too short; return error (no reply needed) */ | ||||
| 		return MODBUS_ERROR_FRAME_INVALID; | ||||
| 	} | ||||
| 	/* check CRC first */ | ||||
| 	uint16_t crc_received = (buffer[len - 1] << 8) | buffer[len - 2]; | ||||
| 	uint16_t crc_calculated = modbus_CRC16(buffer, len - 2); | ||||
| 	if (crc_received != crc_calculated) { | ||||
| 		/* CRC mismatch, return error (no reply needed) */ | ||||
| 		return MODBUS_ERROR_CRC; | ||||
| 	} | ||||
| 	/* check if address matches ours */ | ||||
| 	uint8_t address = buffer[buffer_pos++]; | ||||
| 	transaction.broadcast = (address == MODBUS_BROADCAST_ADDR); | ||||
| 	if (address != modbus_slave_address && transaction.broadcast != 1) { | ||||
| 		/* Message is not for us (no reply needed) */ | ||||
| 		return MODBUS_OK; | ||||
| 	} | ||||
| 	/* get function code */ | ||||
| 	transaction.function_code = buffer[buffer_pos++]; | ||||
| 	transaction.exception = 0; | ||||
| 	uint8_t request_processing_result; | ||||
| 	if (transaction.function_code == MODBUS_READ_DEVICE_IDENTIFICATION) { | ||||
| 		/* Read device ID request is quite complicated, therefore it has its own processing function */ | ||||
| 		request_processing_result = modbus_process_device_id_request(buffer + buffer_pos, len - buffer_pos, &transaction); | ||||
| 	} else { | ||||
| 		/* process other requests: input register read, holding register read/write */ | ||||
| 		request_processing_result = modbus_process_read_write_request(buffer + buffer_pos, len - buffer_pos, &transaction); | ||||
| 	} | ||||
| 	uint8_t msg_len; | ||||
| 	/* reply only if request was processed successfully and message was not broadcast */ | ||||
| 	if (request_processing_result == MODBUS_OK && transaction.broadcast == 0) { | ||||
| 		modbus_transaction_to_buffer(modbus_buffer, &msg_len, &transaction); | ||||
| 		/* send reply */ | ||||
| 		modbus_transmit_function(modbus_buffer, msg_len); | ||||
| 	} | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
|  | ||||
| int8_t modbus_slave_init_device_id(modbus_device_id_t *device_id) | ||||
| { | ||||
| 	if (device_id == NULL) { | ||||
| 		return MODBUS_ERROR; | ||||
| 	} | ||||
| 	/* at least basic category objects have to be implemented */ | ||||
| 	if (	device_id->object_name.VendorName == NULL  || | ||||
| 			device_id->object_name.ProductCode == NULL || | ||||
| 			device_id->object_name.MajorMinorRevision == NULL | ||||
| 	) { | ||||
| 		return MODBUS_ERROR; | ||||
| 	} | ||||
| 	/* set conformity level: currently only "basic" and "regular" is implemented */ | ||||
| 	if ( 	device_id->object_id[3] != NULL && | ||||
| 			device_id->object_id[4] != NULL && | ||||
| 			device_id->object_id[5] != NULL | ||||
|  | ||||
| 	) { | ||||
| 		/* strings are present in regular category (optional) */ | ||||
| 		device_id->conformity_level = MODBUS_CONFORMITY_REGULAR; | ||||
| 	} else { | ||||
| 		device_id->conformity_level = MODBUS_CONFORMITY_BASIC; | ||||
| 	} | ||||
| 	/* we support both stream and individual access to objects */ | ||||
| 	device_id->conformity_level |= MODBUS_DEVICE_ID_INDIVIDUAL_ACCESS_FLAG; | ||||
| 	modbus_device_id = device_id; | ||||
| 	return MODBUS_OK; | ||||
| } | ||||
							
								
								
									
										67
									
								
								fw/Core/Src/sht4x.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								fw/Core/Src/sht4x.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| /* | ||||
|  * sht4x.c | ||||
|  * | ||||
|  *  Created on: Jun 8, 2021 | ||||
|  *      Author: user | ||||
|  */ | ||||
|  | ||||
| #include "sht4x.h" | ||||
| #include "main.h" /* for uart_disable_interrupts() */ | ||||
|  | ||||
| int8_t sht4x_send_cmd(sht4x_cmd_t cmd) | ||||
| { | ||||
| 	return SHT4X_OK; | ||||
| } | ||||
|  | ||||
| int8_t sht4x_read_data(uint8_t *buffer, int len) | ||||
| { | ||||
| 	return SHT4X_OK; | ||||
| } | ||||
|  | ||||
| int8_t sht4x_measure(int16_t *temperature, uint16_t *relative_humidity) | ||||
| { | ||||
| 	uint8_t buffer[32]; | ||||
| 	int result; | ||||
|  | ||||
| 	/* disable interrupts to prevent modbus/i2c conflict */ | ||||
| 	// start measurement | ||||
| 	buffer[0] = SHT4X_START_MEAS_HIGH_PRECISION; | ||||
| 	uart_disable_interrupts(); | ||||
| 	result = i2c_transmit(SHT4X_I2C_ADDRESS<<1, buffer, 1); | ||||
| 	uart_enable_interrupts(); | ||||
| 	if (result != I2C_OK) { | ||||
| 		return SHT4X_ERROR; | ||||
| 	} | ||||
| 	LL_mDelay(10); /* 10 ms should be enough */ | ||||
| 	/* read out */ | ||||
| 	uart_disable_interrupts(); | ||||
| 	result = i2c_receive(SHT4X_I2C_ADDRESS<<1, buffer, 6); | ||||
| 	uart_enable_interrupts(); | ||||
| 	if (result != I2C_OK) { | ||||
| 		return SHT4X_ERROR; | ||||
| 	} | ||||
| 	/* Convert to T and RH; taken directly from pseudocode in SHT4x datasheet, page 3 */ | ||||
| 	uint32_t t_ticks = (buffer[0] << 8) + buffer[1]; | ||||
| 	uint8_t t_crc = buffer[2]; | ||||
| 	uint32_t rh_ticks = (buffer[3] << 8) + buffer[4]; | ||||
| 	uint8_t rh_crc = buffer[5]; | ||||
| 	/* check CRC-8 checksum */ | ||||
| 	uint8_t crc_correct = crc8_calculate(buffer, 2) == t_crc; | ||||
| 	crc_correct &= crc8_calculate(buffer + 3, 2) == rh_crc; | ||||
| 	if (!crc_correct) { | ||||
| 		return SHT4X_CRC8_ERROR; | ||||
| 	} | ||||
| 	/* copy data to output variables */ | ||||
| 	int t_degC = -450 + 10 * 175 * t_ticks / 65535; /* temperature * 10 */ | ||||
| 	int rh_pRH = -6 + 125 * rh_ticks / 65535; | ||||
| 	if (rh_pRH > 100) { | ||||
| 			rh_pRH = 100; | ||||
| 	} | ||||
| 	if (rh_pRH < 0) { | ||||
| 		rh_pRH = 0; | ||||
| 	} | ||||
| 	*temperature = t_degC; | ||||
| 	*relative_humidity = rh_pRH; | ||||
|  | ||||
| 	return SHT4X_OK; | ||||
| } | ||||
							
								
								
									
										207
									
								
								fw/Core/Src/stm32l0xx_it.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										207
									
								
								fw/Core/Src/stm32l0xx_it.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,207 @@ | ||||
| /* USER CODE BEGIN Header */ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * @file    stm32l0xx_it.c | ||||
|   * @brief   Interrupt Service Routines. | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2021 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under BSD 3-Clause license, | ||||
|   * the "License"; You may not use this file except in compliance with the | ||||
|   * License. You may obtain a copy of the License at: | ||||
|   *                        opensource.org/licenses/BSD-3-Clause | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
| /* USER CODE END Header */ | ||||
|  | ||||
| /* Includes ------------------------------------------------------------------*/ | ||||
| #include "main.h" | ||||
| #include "stm32l0xx_it.h" | ||||
| /* Private includes ----------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN Includes */ | ||||
| #include "modbus.h" | ||||
| /* USER CODE END Includes */ | ||||
|  | ||||
| /* Private typedef -----------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN TD */ | ||||
|  | ||||
| /* USER CODE END TD */ | ||||
|  | ||||
| /* Private define ------------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN PD */ | ||||
|  | ||||
| /* USER CODE END PD */ | ||||
|  | ||||
| /* Private macro -------------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN PM */ | ||||
|  | ||||
| /* USER CODE END PM */ | ||||
|  | ||||
| /* Private variables ---------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN PV */ | ||||
| uint16_t usart2_rx_message_index = 0; | ||||
| uint16_t usart2_rx_message_len = 0; | ||||
| uint8_t usart2_rx_done = 0; | ||||
| uint8_t usart2_rx_message_too_long = 0; | ||||
|  | ||||
| uint8_t tim21_elapsed_period = 0; | ||||
| /* USER CODE END PV */ | ||||
|  | ||||
| /* Private function prototypes -----------------------------------------------*/ | ||||
| /* USER CODE BEGIN PFP */ | ||||
| void USART2_CharReception_Callback( void ); | ||||
| /* USER CODE END PFP */ | ||||
|  | ||||
| /* Private user code ---------------------------------------------------------*/ | ||||
| /* USER CODE BEGIN 0 */ | ||||
|  | ||||
| /* USER CODE END 0 */ | ||||
|  | ||||
| /* External variables --------------------------------------------------------*/ | ||||
|  | ||||
| /* USER CODE BEGIN EV */ | ||||
|  | ||||
| /* USER CODE END EV */ | ||||
|  | ||||
| /******************************************************************************/ | ||||
| /*           Cortex-M0+ Processor Interruption and Exception Handlers          */ | ||||
| /******************************************************************************/ | ||||
| /** | ||||
|   * @brief This function handles Non maskable Interrupt. | ||||
|   */ | ||||
| void NMI_Handler(void) | ||||
| { | ||||
|   /* USER CODE BEGIN NonMaskableInt_IRQn 0 */ | ||||
|  | ||||
|   /* USER CODE END NonMaskableInt_IRQn 0 */ | ||||
|   /* USER CODE BEGIN NonMaskableInt_IRQn 1 */ | ||||
|   while (1) | ||||
|   { | ||||
|   } | ||||
|   /* USER CODE END NonMaskableInt_IRQn 1 */ | ||||
| } | ||||
|  | ||||
| /** | ||||
|   * @brief This function handles Hard fault interrupt. | ||||
|   */ | ||||
| void HardFault_Handler(void) | ||||
| { | ||||
|   /* USER CODE BEGIN HardFault_IRQn 0 */ | ||||
|  | ||||
|   /* USER CODE END HardFault_IRQn 0 */ | ||||
|   while (1) | ||||
|   { | ||||
|     /* USER CODE BEGIN W1_HardFault_IRQn 0 */ | ||||
|     /* USER CODE END W1_HardFault_IRQn 0 */ | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
|   * @brief This function handles System service call via SWI instruction. | ||||
|   */ | ||||
| void SVC_Handler(void) | ||||
| { | ||||
|   /* USER CODE BEGIN SVC_IRQn 0 */ | ||||
|  | ||||
|   /* USER CODE END SVC_IRQn 0 */ | ||||
|   /* USER CODE BEGIN SVC_IRQn 1 */ | ||||
|  | ||||
|   /* USER CODE END SVC_IRQn 1 */ | ||||
| } | ||||
|  | ||||
| /** | ||||
|   * @brief This function handles Pendable request for system service. | ||||
|   */ | ||||
| void PendSV_Handler(void) | ||||
| { | ||||
|   /* USER CODE BEGIN PendSV_IRQn 0 */ | ||||
|  | ||||
|   /* USER CODE END PendSV_IRQn 0 */ | ||||
|   /* USER CODE BEGIN PendSV_IRQn 1 */ | ||||
|  | ||||
|   /* USER CODE END PendSV_IRQn 1 */ | ||||
| } | ||||
|  | ||||
| /** | ||||
|   * @brief This function handles System tick timer. | ||||
|   */ | ||||
| void SysTick_Handler(void) | ||||
| { | ||||
|   /* USER CODE BEGIN SysTick_IRQn 0 */ | ||||
|  | ||||
|   /* USER CODE END SysTick_IRQn 0 */ | ||||
|  | ||||
|   /* USER CODE BEGIN SysTick_IRQn 1 */ | ||||
|   /* USER CODE END SysTick_IRQn 1 */ | ||||
| } | ||||
|  | ||||
| /******************************************************************************/ | ||||
| /* STM32L0xx Peripheral Interrupt Handlers                                    */ | ||||
| /* Add here the Interrupt Handlers for the used peripherals.                  */ | ||||
| /* For the available peripheral interrupt handler names,                      */ | ||||
| /* please refer to the startup file (startup_stm32l0xx.s).                    */ | ||||
| /******************************************************************************/ | ||||
|  | ||||
| /** | ||||
|   * @brief This function handles TIM21 global interrupt. | ||||
|   */ | ||||
| void TIM21_IRQHandler(void) | ||||
| { | ||||
|   /* USER CODE BEGIN TIM21_IRQn 0 */ | ||||
| 	LL_TIM_ClearFlag_UPDATE(TIM21); | ||||
| 	tim21_elapsed_period = 1; | ||||
|   /* USER CODE END TIM21_IRQn 0 */ | ||||
|   /* USER CODE BEGIN TIM21_IRQn 1 */ | ||||
|  | ||||
|   /* USER CODE END TIM21_IRQn 1 */ | ||||
| } | ||||
|  | ||||
| /** | ||||
|   * @brief This function handles USART2 global interrupt / USART2 wake-up interrupt through EXTI line 26. | ||||
|   */ | ||||
| void USART2_IRQHandler(void) | ||||
| { | ||||
|   /* USER CODE BEGIN USART2_IRQn 0 */ | ||||
|     /* Check RXNE flag value in SR register */ | ||||
| 	if(LL_USART_IsActiveFlag_RXNE(USART2) && LL_USART_IsEnabledIT_RXNE(USART2)) | ||||
| 	{ | ||||
| 		/* RXNE flag will be cleared by reading of DR register (done in call) */ | ||||
| 		/* Call function in charge of handling Character reception */ | ||||
| 		USART2_CharReception_Callback(); | ||||
| 	} | ||||
|   /* USER CODE END USART2_IRQn 0 */ | ||||
|   /* USER CODE BEGIN USART2_IRQn 1 */ | ||||
| 	/* If the IDLE flag is active */ | ||||
| 	if (LL_USART_IsActiveFlag_IDLE(USART2) && LL_USART_IsEnabledIT_IDLE(USART2)) | ||||
| 	{ | ||||
| 		/* Clear the IDLE flag */ | ||||
| 		LL_USART_ClearFlag_IDLE(USART2); | ||||
|  | ||||
| 		/* Reset the buffer index */ | ||||
| 		usart2_rx_message_len = usart2_rx_message_index; | ||||
| 		usart2_rx_message_index = 0; | ||||
| 		usart2_rx_done = 1; | ||||
| 		if (usart2_rx_message_len > MODBUS_MAX_RTU_FRAME_SIZE) | ||||
| 		{ | ||||
| 			usart2_rx_message_too_long = 1; | ||||
| 		} | ||||
| 	} | ||||
|   /* USER CODE END USART2_IRQn 1 */ | ||||
| } | ||||
|  | ||||
| /* USER CODE BEGIN 1 */ | ||||
| void USART2_CharReception_Callback( void ) | ||||
| { | ||||
| 	uint16_t usart2_rx_bit = LL_USART_ReceiveData9(USART2); | ||||
| 	if (usart2_rx_message_index < MODBUS_MAX_RTU_FRAME_SIZE) | ||||
| 	{ | ||||
| 		modbus_buffer[usart2_rx_message_index] = (uint8_t)usart2_rx_bit; | ||||
| 	} | ||||
|     usart2_rx_message_index++; | ||||
| } | ||||
| /* USER CODE END 1 */ | ||||
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ | ||||
							
								
								
									
										159
									
								
								fw/Core/Src/syscalls.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										159
									
								
								fw/Core/Src/syscalls.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,159 @@ | ||||
| /** | ||||
|  ****************************************************************************** | ||||
|  * @file      syscalls.c | ||||
|  * @author    Auto-generated by STM32CubeIDE | ||||
|  * @brief     STM32CubeIDE Minimal System calls file | ||||
|  * | ||||
|  *            For more information about which c-functions | ||||
|  *            need which of these lowlevel functions | ||||
|  *            please consult the Newlib libc-manual | ||||
|  ****************************************************************************** | ||||
|  * @attention | ||||
|  * | ||||
|  * <h2><center>© Copyright (c) 2020 STMicroelectronics. | ||||
|  * All rights reserved.</center></h2> | ||||
|  * | ||||
|  * This software component is licensed by ST under BSD 3-Clause license, | ||||
|  * the "License"; You may not use this file except in compliance with the | ||||
|  * License. You may obtain a copy of the License at: | ||||
|  *                        opensource.org/licenses/BSD-3-Clause | ||||
|  * | ||||
|  ****************************************************************************** | ||||
|  */ | ||||
|  | ||||
| /* Includes */ | ||||
| #include <sys/stat.h> | ||||
| #include <stdlib.h> | ||||
| #include <errno.h> | ||||
| #include <stdio.h> | ||||
| #include <signal.h> | ||||
| #include <time.h> | ||||
| #include <sys/time.h> | ||||
| #include <sys/times.h> | ||||
|  | ||||
|  | ||||
| /* Variables */ | ||||
| //#undef errno | ||||
| extern int errno; | ||||
| extern int __io_putchar(int ch) __attribute__((weak)); | ||||
| extern int __io_getchar(void) __attribute__((weak)); | ||||
|  | ||||
| register char * stack_ptr asm("sp"); | ||||
|  | ||||
| char *__env[1] = { 0 }; | ||||
| char **environ = __env; | ||||
|  | ||||
|  | ||||
| /* Functions */ | ||||
| void initialise_monitor_handles() | ||||
| { | ||||
| } | ||||
|  | ||||
| int _getpid(void) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int _kill(int pid, int sig) | ||||
| { | ||||
| 	errno = EINVAL; | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| void _exit (int status) | ||||
| { | ||||
| 	_kill(status, -1); | ||||
| 	while (1) {}		/* Make sure we hang here */ | ||||
| } | ||||
|  | ||||
| __attribute__((weak)) int _read(int file, char *ptr, int len) | ||||
| { | ||||
| 	int DataIdx; | ||||
|  | ||||
| 	for (DataIdx = 0; DataIdx < len; DataIdx++) | ||||
| 	{ | ||||
| 		*ptr++ = __io_getchar(); | ||||
| 	} | ||||
|  | ||||
| return len; | ||||
| } | ||||
|  | ||||
| __attribute__((weak)) int _write(int file, char *ptr, int len) | ||||
| { | ||||
| 	int DataIdx; | ||||
|  | ||||
| 	for (DataIdx = 0; DataIdx < len; DataIdx++) | ||||
| 	{ | ||||
| 		__io_putchar(*ptr++); | ||||
| 	} | ||||
| 	return len; | ||||
| } | ||||
|  | ||||
| int _close(int file) | ||||
| { | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
|  | ||||
| int _fstat(int file, struct stat *st) | ||||
| { | ||||
| 	st->st_mode = S_IFCHR; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int _isatty(int file) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
|  | ||||
| int _lseek(int file, int ptr, int dir) | ||||
| { | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int _open(char *path, int flags, ...) | ||||
| { | ||||
| 	/* Pretend like we always fail */ | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| int _wait(int *status) | ||||
| { | ||||
| 	errno = ECHILD; | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| int _unlink(char *name) | ||||
| { | ||||
| 	errno = ENOENT; | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| int _times(struct tms *buf) | ||||
| { | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| int _stat(char *file, struct stat *st) | ||||
| { | ||||
| 	st->st_mode = S_IFCHR; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| int _link(char *old, char *new) | ||||
| { | ||||
| 	errno = EMLINK; | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| int _fork(void) | ||||
| { | ||||
| 	errno = EAGAIN; | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| int _execve(char *name, char **argv, char **env) | ||||
| { | ||||
| 	errno = ENOMEM; | ||||
| 	return -1; | ||||
| } | ||||
							
								
								
									
										80
									
								
								fw/Core/Src/sysmem.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										80
									
								
								fw/Core/Src/sysmem.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,80 @@ | ||||
| /** | ||||
|  ****************************************************************************** | ||||
|  * @file      sysmem.c | ||||
|  * @author    Generated by STM32CubeIDE | ||||
|  * @brief     STM32CubeIDE System Memory calls file | ||||
|  * | ||||
|  *            For more information about which C functions | ||||
|  *            need which of these lowlevel functions | ||||
|  *            please consult the newlib libc manual | ||||
|  ****************************************************************************** | ||||
|  * @attention | ||||
|  * | ||||
|  * <h2><center>© Copyright (c) 2020 STMicroelectronics. | ||||
|  * All rights reserved.</center></h2> | ||||
|  * | ||||
|  * This software component is licensed by ST under BSD 3-Clause license, | ||||
|  * the "License"; You may not use this file except in compliance with the | ||||
|  * License. You may obtain a copy of the License at: | ||||
|  *                        opensource.org/licenses/BSD-3-Clause | ||||
|  * | ||||
|  ****************************************************************************** | ||||
|  */ | ||||
|  | ||||
| /* Includes */ | ||||
| #include <errno.h> | ||||
| #include <stdint.h> | ||||
|  | ||||
| /** | ||||
|  * Pointer to the current high watermark of the heap usage | ||||
|  */ | ||||
| static uint8_t *__sbrk_heap_end = NULL; | ||||
|  | ||||
| /** | ||||
|  * @brief _sbrk() allocates memory to the newlib heap and is used by malloc | ||||
|  *        and others from the C library | ||||
|  * | ||||
|  * @verbatim | ||||
|  * ############################################################################ | ||||
|  * #  .data  #  .bss  #       newlib heap       #          MSP stack          # | ||||
|  * #         #        #                         # Reserved by _Min_Stack_Size # | ||||
|  * ############################################################################ | ||||
|  * ^-- RAM start      ^-- _end                             _estack, RAM end --^ | ||||
|  * @endverbatim | ||||
|  * | ||||
|  * This implementation starts allocating at the '_end' linker symbol | ||||
|  * The '_Min_Stack_Size' linker symbol reserves a memory for the MSP stack | ||||
|  * The implementation considers '_estack' linker symbol to be RAM end | ||||
|  * NOTE: If the MSP stack, at any point during execution, grows larger than the | ||||
|  * reserved size, please increase the '_Min_Stack_Size'. | ||||
|  * | ||||
|  * @param incr Memory size | ||||
|  * @return Pointer to allocated memory | ||||
|  */ | ||||
| void *_sbrk(ptrdiff_t incr) | ||||
| { | ||||
|   extern uint8_t _end; /* Symbol defined in the linker script */ | ||||
|   extern uint8_t _estack; /* Symbol defined in the linker script */ | ||||
|   extern uint32_t _Min_Stack_Size; /* Symbol defined in the linker script */ | ||||
|   const uint32_t stack_limit = (uint32_t)&_estack - (uint32_t)&_Min_Stack_Size; | ||||
|   const uint8_t *max_heap = (uint8_t *)stack_limit; | ||||
|   uint8_t *prev_heap_end; | ||||
|  | ||||
|   /* Initialize heap end at first call */ | ||||
|   if (NULL == __sbrk_heap_end) | ||||
|   { | ||||
|     __sbrk_heap_end = &_end; | ||||
|   } | ||||
|  | ||||
|   /* Protect heap from growing into the reserved MSP stack */ | ||||
|   if (__sbrk_heap_end + incr > max_heap) | ||||
|   { | ||||
|     errno = ENOMEM; | ||||
|     return (void *)-1; | ||||
|   } | ||||
|  | ||||
|   prev_heap_end = __sbrk_heap_end; | ||||
|   __sbrk_heap_end += incr; | ||||
|  | ||||
|   return (void *)prev_heap_end; | ||||
| } | ||||
							
								
								
									
										275
									
								
								fw/Core/Src/system_stm32l0xx.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								fw/Core/Src/system_stm32l0xx.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,275 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * @file    system_stm32l0xx.c | ||||
|   * @author  MCD Application Team | ||||
|   * @brief   CMSIS Cortex-M0+ Device Peripheral Access Layer System Source File. | ||||
|   * | ||||
|   *   This file provides two functions and one global variable to be called from | ||||
|   *   user application: | ||||
|   *      - SystemInit(): This function is called at startup just after reset and | ||||
|   *                      before branch to main program. This call is made inside | ||||
|   *                      the "startup_stm32l0xx.s" file. | ||||
|   * | ||||
|   *      - SystemCoreClock variable: Contains the core clock (HCLK), it can be used | ||||
|   *                                  by the user application to setup the SysTick | ||||
|   *                                  timer or configure other parameters. | ||||
|   * | ||||
|   *      - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must | ||||
|   *                                 be called whenever the core clock is changed | ||||
|   *                                 during program execution. | ||||
|   * | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * <h2><center>© Copyright(c) 2016 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under BSD 3-Clause license, | ||||
|   * the "License"; You may not use this file except in compliance with the | ||||
|   * License. You may obtain a copy of the License at: | ||||
|   *                        opensource.org/licenses/BSD-3-Clause | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
| /** @addtogroup CMSIS | ||||
|   * @{ | ||||
|   */ | ||||
|  | ||||
| /** @addtogroup stm32l0xx_system | ||||
|   * @{ | ||||
|   */ | ||||
|  | ||||
| /** @addtogroup STM32L0xx_System_Private_Includes | ||||
|   * @{ | ||||
|   */ | ||||
|  | ||||
| #include "stm32l0xx.h" | ||||
|  | ||||
| #if !defined  (HSE_VALUE) | ||||
|   #define HSE_VALUE    ((uint32_t)8000000U) /*!< Value of the External oscillator in Hz */ | ||||
| #endif /* HSE_VALUE */ | ||||
|  | ||||
| #if !defined  (MSI_VALUE) | ||||
|   #define MSI_VALUE    ((uint32_t)2097152U) /*!< Value of the Internal oscillator in Hz*/ | ||||
| #endif /* MSI_VALUE */ | ||||
|  | ||||
| #if !defined  (HSI_VALUE) | ||||
|   #define HSI_VALUE    ((uint32_t)16000000U) /*!< Value of the Internal oscillator in Hz*/ | ||||
| #endif /* HSI_VALUE */ | ||||
|  | ||||
|  | ||||
| /** | ||||
|   * @} | ||||
|   */ | ||||
|  | ||||
| /** @addtogroup STM32L0xx_System_Private_TypesDefinitions | ||||
|   * @{ | ||||
|   */ | ||||
|  | ||||
| /** | ||||
|   * @} | ||||
|   */ | ||||
|  | ||||
| /** @addtogroup STM32L0xx_System_Private_Defines | ||||
|   * @{ | ||||
|   */ | ||||
| /************************* Miscellaneous Configuration ************************/ | ||||
|  | ||||
| /* Note: Following vector table addresses must be defined in line with linker | ||||
|          configuration. */ | ||||
| /*!< Uncomment the following line if you need to relocate the vector table | ||||
|      anywhere in Flash or Sram, else the vector table is kept at the automatic | ||||
|      remap of boot address selected */ | ||||
| /* #define USER_VECT_TAB_ADDRESS */ | ||||
|  | ||||
| #if defined(USER_VECT_TAB_ADDRESS) | ||||
| /*!< Uncomment the following line if you need to relocate your vector Table | ||||
|      in Sram else user remap will be done in Flash. */ | ||||
| /* #define VECT_TAB_SRAM */ | ||||
| #if defined(VECT_TAB_SRAM) | ||||
| #define VECT_TAB_BASE_ADDRESS   SRAM_BASE       /*!< Vector Table base address field. | ||||
|                                                      This value must be a multiple of 0x200. */ | ||||
| #define VECT_TAB_OFFSET         0x00000000U     /*!< Vector Table base offset field. | ||||
|                                                      This value must be a multiple of 0x200. */ | ||||
| #else | ||||
| #define VECT_TAB_BASE_ADDRESS   FLASH_BASE      /*!< Vector Table base address field. | ||||
|                                                      This value must be a multiple of 0x200. */ | ||||
| #define VECT_TAB_OFFSET         0x00000000U     /*!< Vector Table base offset field. | ||||
|                                                      This value must be a multiple of 0x200. */ | ||||
| #endif /* VECT_TAB_SRAM */ | ||||
| #endif /* USER_VECT_TAB_ADDRESS */ | ||||
|  | ||||
| /******************************************************************************/ | ||||
| /** | ||||
|   * @} | ||||
|   */ | ||||
|  | ||||
| /** @addtogroup STM32L0xx_System_Private_Macros | ||||
|   * @{ | ||||
|   */ | ||||
|  | ||||
| /** | ||||
|   * @} | ||||
|   */ | ||||
|  | ||||
| /** @addtogroup STM32L0xx_System_Private_Variables | ||||
|   * @{ | ||||
|   */ | ||||
|   /* This variable is updated in three ways: | ||||
|       1) by calling CMSIS function SystemCoreClockUpdate() | ||||
|       2) by calling HAL API function HAL_RCC_GetHCLKFreq() | ||||
|       3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency | ||||
|          Note: If you use this function to configure the system clock; then there | ||||
|                is no need to call the 2 first functions listed above, since SystemCoreClock | ||||
|                variable is updated automatically. | ||||
|   */ | ||||
|   uint32_t SystemCoreClock = 2097152U; /* 32.768 kHz * 2^6 */ | ||||
|   const uint8_t AHBPrescTable[16] = {0U, 0U, 0U, 0U, 0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U, 6U, 7U, 8U, 9U}; | ||||
|   const uint8_t APBPrescTable[8] = {0U, 0U, 0U, 0U, 1U, 2U, 3U, 4U}; | ||||
|   const uint8_t PLLMulTable[9] = {3U, 4U, 6U, 8U, 12U, 16U, 24U, 32U, 48U}; | ||||
|  | ||||
| /** | ||||
|   * @} | ||||
|   */ | ||||
|  | ||||
| /** @addtogroup STM32L0xx_System_Private_FunctionPrototypes | ||||
|   * @{ | ||||
|   */ | ||||
|  | ||||
| /** | ||||
|   * @} | ||||
|   */ | ||||
|  | ||||
| /** @addtogroup STM32L0xx_System_Private_Functions | ||||
|   * @{ | ||||
|   */ | ||||
|  | ||||
| /** | ||||
|   * @brief  Setup the microcontroller system. | ||||
|   * @param  None | ||||
|   * @retval None | ||||
|   */ | ||||
| void SystemInit (void) | ||||
| { | ||||
|   /* Configure the Vector Table location add offset address ------------------*/ | ||||
| #if defined (USER_VECT_TAB_ADDRESS) | ||||
|   SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ | ||||
| #endif /* USER_VECT_TAB_ADDRESS */ | ||||
| } | ||||
|  | ||||
| /** | ||||
|   * @brief  Update SystemCoreClock variable according to Clock Register Values. | ||||
|   *         The SystemCoreClock variable contains the core clock (HCLK), it can | ||||
|   *         be used by the user application to setup the SysTick timer or configure | ||||
|   *         other parameters. | ||||
|   * | ||||
|   * @note   Each time the core clock (HCLK) changes, this function must be called | ||||
|   *         to update SystemCoreClock variable value. Otherwise, any configuration | ||||
|   *         based on this variable will be incorrect. | ||||
|   * | ||||
|   * @note   - The system frequency computed by this function is not the real | ||||
|   *           frequency in the chip. It is calculated based on the predefined | ||||
|   *           constant and the selected clock source: | ||||
|   * | ||||
|   *           - If SYSCLK source is MSI, SystemCoreClock will contain the MSI | ||||
|   *             value as defined by the MSI range. | ||||
|   * | ||||
|   *           - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) | ||||
|   * | ||||
|   *           - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) | ||||
|   * | ||||
|   *           - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) | ||||
|   *             or HSI_VALUE(*) multiplied/divided by the PLL factors. | ||||
|   * | ||||
|   *         (*) HSI_VALUE is a constant defined in stm32l0xx_hal.h file (default value | ||||
|   *             16 MHz) but the real value may vary depending on the variations | ||||
|   *             in voltage and temperature. | ||||
|   * | ||||
|   *         (**) HSE_VALUE is a constant defined in stm32l0xx_hal.h file (default value | ||||
|   *              8 MHz), user has to ensure that HSE_VALUE is same as the real | ||||
|   *              frequency of the crystal used. Otherwise, this function may | ||||
|   *              have wrong result. | ||||
|   * | ||||
|   *         - The result of this function could be not correct when using fractional | ||||
|   *           value for HSE crystal. | ||||
|   * @param  None | ||||
|   * @retval None | ||||
|   */ | ||||
| void SystemCoreClockUpdate (void) | ||||
| { | ||||
|   uint32_t tmp = 0U, pllmul = 0U, plldiv = 0U, pllsource = 0U, msirange = 0U; | ||||
|  | ||||
|   /* Get SYSCLK source -------------------------------------------------------*/ | ||||
|   tmp = RCC->CFGR & RCC_CFGR_SWS; | ||||
|  | ||||
|   switch (tmp) | ||||
|   { | ||||
|     case 0x00U:  /* MSI used as system clock */ | ||||
|       msirange = (RCC->ICSCR & RCC_ICSCR_MSIRANGE) >> RCC_ICSCR_MSIRANGE_Pos; | ||||
|       SystemCoreClock = (32768U * (1U << (msirange + 1U))); | ||||
|       break; | ||||
|     case 0x04U:  /* HSI used as system clock */ | ||||
|       if ((RCC->CR & RCC_CR_HSIDIVF) != 0U) | ||||
|       { | ||||
|         SystemCoreClock = HSI_VALUE / 4U; | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         SystemCoreClock = HSI_VALUE; | ||||
|       } | ||||
|       break; | ||||
|     case 0x08U:  /* HSE used as system clock */ | ||||
|       SystemCoreClock = HSE_VALUE; | ||||
|       break; | ||||
|     default:  /* PLL used as system clock */ | ||||
|       /* Get PLL clock source and multiplication factor ----------------------*/ | ||||
|       pllmul = RCC->CFGR & RCC_CFGR_PLLMUL; | ||||
|       plldiv = RCC->CFGR & RCC_CFGR_PLLDIV; | ||||
|       pllmul = PLLMulTable[(pllmul >> RCC_CFGR_PLLMUL_Pos)]; | ||||
|       plldiv = (plldiv >> RCC_CFGR_PLLDIV_Pos) + 1U; | ||||
|  | ||||
|       pllsource = RCC->CFGR & RCC_CFGR_PLLSRC; | ||||
|  | ||||
|       if (pllsource == 0x00U) | ||||
|       { | ||||
|         /* HSI oscillator clock selected as PLL clock entry */ | ||||
|         if ((RCC->CR & RCC_CR_HSIDIVF) != 0U) | ||||
|         { | ||||
|           SystemCoreClock = (((HSI_VALUE / 4U) * pllmul) / plldiv); | ||||
|         } | ||||
|         else | ||||
|         { | ||||
|           SystemCoreClock = (((HSI_VALUE) * pllmul) / plldiv); | ||||
|         } | ||||
|       } | ||||
|       else | ||||
|       { | ||||
|         /* HSE selected as PLL clock entry */ | ||||
|         SystemCoreClock = (((HSE_VALUE) * pllmul) / plldiv); | ||||
|       } | ||||
|       break; | ||||
|   } | ||||
|   /* Compute HCLK clock frequency --------------------------------------------*/ | ||||
|   /* Get HCLK prescaler */ | ||||
|   tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> RCC_CFGR_HPRE_Pos)]; | ||||
|   /* HCLK clock frequency */ | ||||
|   SystemCoreClock >>= tmp; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| /** | ||||
|   * @} | ||||
|   */ | ||||
|  | ||||
| /** | ||||
|   * @} | ||||
|   */ | ||||
|  | ||||
| /** | ||||
|   * @} | ||||
|   */ | ||||
|  | ||||
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ | ||||
							
								
								
									
										261
									
								
								fw/Core/Startup/startup_stm32l031g6ux.s
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										261
									
								
								fw/Core/Startup/startup_stm32l031g6ux.s
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,261 @@ | ||||
| /** | ||||
|   ****************************************************************************** | ||||
|   * @file      startup_stm32l031xx.s | ||||
|   * @author    MCD Application Team | ||||
|   * @brief     STM32L031xx Devices vector table for GCC toolchain. | ||||
|   *            This module performs: | ||||
|   *                - Set the initial SP | ||||
|   *                - Set the initial PC == Reset_Handler, | ||||
|   *                - Set the vector table entries with the exceptions ISR address | ||||
|   *                - Branches to main in the C library (which eventually | ||||
|   *                  calls main()). | ||||
|   *            After Reset the Cortex-M0+ processor is in Thread mode, | ||||
|   *            priority is Privileged, and the Stack is set to Main. | ||||
|   ****************************************************************************** | ||||
|   * @attention | ||||
|   * | ||||
|   * <h2><center>© Copyright (c) 2016 STMicroelectronics. | ||||
|   * All rights reserved.</center></h2> | ||||
|   * | ||||
|   * This software component is licensed by ST under BSD 3-Clause license, | ||||
|   * the "License"; You may not use this file except in compliance with the | ||||
|   * License. You may obtain a copy of the License at: | ||||
|   *                        opensource.org/licenses/BSD-3-Clause | ||||
|   * | ||||
|   ****************************************************************************** | ||||
|   */ | ||||
|  | ||||
|   .syntax unified | ||||
|   .cpu cortex-m0plus | ||||
|   .fpu softvfp | ||||
|   .thumb | ||||
|  | ||||
| .global  g_pfnVectors | ||||
| .global  Default_Handler | ||||
|  | ||||
| /* start address for the initialization values of the .data section. | ||||
| defined in linker script */ | ||||
| .word  _sidata | ||||
| /* start address for the .data section. defined in linker script */ | ||||
| .word  _sdata | ||||
| /* end address for the .data section. defined in linker script */ | ||||
| .word  _edata | ||||
| /* start address for the .bss section. defined in linker script */ | ||||
| .word  _sbss | ||||
| /* end address for the .bss section. defined in linker script */ | ||||
| .word  _ebss | ||||
|  | ||||
|     .section  .text.Reset_Handler | ||||
|   .weak  Reset_Handler | ||||
|   .type  Reset_Handler, %function | ||||
| Reset_Handler:   | ||||
|    ldr   r0, =_estack | ||||
|    mov   sp, r0          /* set stack pointer */ | ||||
|  | ||||
| /* Copy the data segment initializers from flash to SRAM */ | ||||
|   ldr r0, =_sdata | ||||
|   ldr r1, =_edata | ||||
|   ldr r2, =_sidata | ||||
|   movs r3, #0 | ||||
|   b LoopCopyDataInit | ||||
|  | ||||
| CopyDataInit: | ||||
|   ldr r4, [r2, r3] | ||||
|   str r4, [r0, r3] | ||||
|   adds r3, r3, #4 | ||||
|  | ||||
| LoopCopyDataInit: | ||||
|   adds r4, r0, r3 | ||||
|   cmp r4, r1 | ||||
|   bcc CopyDataInit | ||||
|    | ||||
| /* Zero fill the bss segment. */ | ||||
|   ldr r2, =_sbss | ||||
|   ldr r4, =_ebss | ||||
|   movs r3, #0 | ||||
|   b LoopFillZerobss | ||||
|  | ||||
| FillZerobss: | ||||
|   str  r3, [r2] | ||||
|   adds r2, r2, #4 | ||||
|  | ||||
| LoopFillZerobss: | ||||
|   cmp r2, r4 | ||||
|   bcc FillZerobss | ||||
|  | ||||
| /* Call the clock system intitialization function.*/ | ||||
|   bl  SystemInit | ||||
| /* Call static constructors */ | ||||
|     bl __libc_init_array | ||||
| /* Call the application's entry point.*/ | ||||
|   bl  main | ||||
|  | ||||
| LoopForever: | ||||
|     b LoopForever | ||||
|  | ||||
|  | ||||
| .size  Reset_Handler, .-Reset_Handler | ||||
|  | ||||
| /** | ||||
|  * @brief  This is the code that gets called when the processor receives an | ||||
|  *         unexpected interrupt.  This simply enters an infinite loop, preserving | ||||
|  *         the system state for examination by a debugger. | ||||
|  * | ||||
|  * @param  None | ||||
|  * @retval : None | ||||
| */ | ||||
|     .section  .text.Default_Handler,"ax",%progbits | ||||
| Default_Handler: | ||||
| Infinite_Loop: | ||||
|   b  Infinite_Loop | ||||
|   .size  Default_Handler, .-Default_Handler | ||||
| /****************************************************************************** | ||||
| * | ||||
| * The minimal vector table for a Cortex M0.  Note that the proper constructs | ||||
| * must be placed on this to ensure that it ends up at physical address | ||||
| * 0x0000.0000. | ||||
| * | ||||
| ******************************************************************************/ | ||||
|    .section  .isr_vector,"a",%progbits | ||||
|   .type  g_pfnVectors, %object | ||||
|   .size  g_pfnVectors, .-g_pfnVectors | ||||
|  | ||||
|  | ||||
| g_pfnVectors: | ||||
|   .word  _estack | ||||
|   .word  Reset_Handler | ||||
|   .word  NMI_Handler | ||||
|   .word  HardFault_Handler | ||||
|   .word  0 | ||||
|   .word  0 | ||||
|   .word  0 | ||||
|   .word  0 | ||||
|   .word  0 | ||||
|   .word  0 | ||||
|   .word  0 | ||||
|   .word  SVC_Handler | ||||
|   .word  0 | ||||
|   .word  0 | ||||
|   .word  PendSV_Handler | ||||
|   .word  SysTick_Handler | ||||
|   .word     WWDG_IRQHandler                   /* Window WatchDog              */ | ||||
|   .word     PVD_IRQHandler                    /* PVD through EXTI Line detection */ | ||||
|   .word     RTC_IRQHandler                    /* RTC through the EXTI line     */ | ||||
|   .word     FLASH_IRQHandler                  /* FLASH                        */ | ||||
|   .word     RCC_IRQHandler                    /* RCC                          */ | ||||
|   .word     EXTI0_1_IRQHandler                /* EXTI Line 0 and 1            */ | ||||
|   .word     EXTI2_3_IRQHandler                /* EXTI Line 2 and 3            */ | ||||
|   .word     EXTI4_15_IRQHandler               /* EXTI Line 4 to 15            */ | ||||
|   .word     0                                 /* Reserved                     */ | ||||
|   .word     DMA1_Channel1_IRQHandler          /* DMA1 Channel 1               */ | ||||
|   .word     DMA1_Channel2_3_IRQHandler        /* DMA1 Channel 2 and Channel 3 */ | ||||
|   .word     DMA1_Channel4_5_6_7_IRQHandler    /* DMA1 Channel 4, Channel 5, Channel 6 and Channel 7*/ | ||||
|   .word     ADC1_COMP_IRQHandler              /* ADC1, COMP1 and COMP2        */ | ||||
|   .word     LPTIM1_IRQHandler                 /* LPTIM1                       */ | ||||
|   .word     0                                 /* Reserved                     */ | ||||
|   .word     TIM2_IRQHandler                   /* TIM2                         */ | ||||
|   .word     0                                 /* Reserved                     */ | ||||
|   .word     0                                 /* Reserved                     */ | ||||
|   .word     0                                 /* Reserved                     */ | ||||
|   .word     0                                 /* Reserved                     */ | ||||
|   .word     TIM21_IRQHandler                  /* TIM21                        */ | ||||
|   .word     0                                 /* Reserved                     */ | ||||
|   .word     TIM22_IRQHandler                  /* TIM22                        */ | ||||
|   .word     I2C1_IRQHandler                   /* I2C1                         */ | ||||
|   .word     0                                 /* Reserved                     */ | ||||
|   .word     SPI1_IRQHandler                   /* SPI1                         */ | ||||
|   .word     0                                 /* Reserved                     */ | ||||
|   .word     0                                 /* Reserved                     */ | ||||
|   .word     USART2_IRQHandler                 /* USART2                       */ | ||||
|   .word     LPUART1_IRQHandler                /* LPUART1                      */ | ||||
|   .word     0                                 /* Reserved                     */ | ||||
|   .word     0                                 /* Reserved                     */ | ||||
|  | ||||
| /******************************************************************************* | ||||
| * | ||||
| * Provide weak aliases for each Exception handler to the Default_Handler. | ||||
| * As they are weak aliases, any function with the same name will override | ||||
| * this definition. | ||||
| * | ||||
| *******************************************************************************/ | ||||
|  | ||||
|    .weak      NMI_Handler | ||||
|    .thumb_set NMI_Handler,Default_Handler | ||||
|  | ||||
|    .weak      HardFault_Handler | ||||
|    .thumb_set HardFault_Handler,Default_Handler | ||||
|  | ||||
|    .weak      SVC_Handler | ||||
|    .thumb_set SVC_Handler,Default_Handler | ||||
|  | ||||
|    .weak      PendSV_Handler | ||||
|    .thumb_set PendSV_Handler,Default_Handler | ||||
|  | ||||
|    .weak      SysTick_Handler | ||||
|    .thumb_set SysTick_Handler,Default_Handler | ||||
|  | ||||
|    .weak      WWDG_IRQHandler | ||||
|    .thumb_set WWDG_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      PVD_IRQHandler | ||||
|    .thumb_set PVD_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      RTC_IRQHandler | ||||
|    .thumb_set RTC_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      FLASH_IRQHandler | ||||
|    .thumb_set FLASH_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      RCC_IRQHandler | ||||
|    .thumb_set RCC_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      EXTI0_1_IRQHandler | ||||
|    .thumb_set EXTI0_1_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      EXTI2_3_IRQHandler | ||||
|    .thumb_set EXTI2_3_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      EXTI4_15_IRQHandler | ||||
|    .thumb_set EXTI4_15_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      DMA1_Channel1_IRQHandler | ||||
|    .thumb_set DMA1_Channel1_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      DMA1_Channel2_3_IRQHandler | ||||
|    .thumb_set DMA1_Channel2_3_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      DMA1_Channel4_5_6_7_IRQHandler | ||||
|    .thumb_set DMA1_Channel4_5_6_7_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      ADC1_COMP_IRQHandler | ||||
|    .thumb_set ADC1_COMP_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      LPTIM1_IRQHandler | ||||
|    .thumb_set LPTIM1_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      TIM2_IRQHandler | ||||
|    .thumb_set TIM2_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      TIM21_IRQHandler | ||||
|    .thumb_set TIM21_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      TIM22_IRQHandler | ||||
|    .thumb_set TIM22_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      I2C1_IRQHandler | ||||
|    .thumb_set I2C1_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      SPI1_IRQHandler | ||||
|    .thumb_set SPI1_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      USART2_IRQHandler | ||||
|    .thumb_set USART2_IRQHandler,Default_Handler | ||||
|  | ||||
|    .weak      LPUART1_IRQHandler | ||||
|    .thumb_set LPUART1_IRQHandler,Default_Handler | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| /************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	 David Žaitlík
					David Žaitlík