PT1 plant model#
IP-Core to model a PT1 plant
With the gain \(K\) and time constant T.
Intended for HIL/SIL/xIL on the UltraZohm
PT1 is implemented in discrete time by using zero order hold transformation
Sample time of the integrator is \(T_s=\frac{1}{1\,MHz}\)
IP-Core clock frequency must be \(f_{clk}=100\,MHz\)!
IP-Core has single precision AXI ports
All calculations in the IP-Core are done in double precision!
Rationale: the machine epsilon for single precision is approximately \(\epsilon=6e-08\). The time discrete integrator multiplies the input with \(\frac{1}{T_s}\), resulting in small numbers. This results in noticable rounding errors for the integration compared to double precision. This effect als consistent between the IP-Core and Simulink, thus double precision is used in the IP-Core. AXI is 32-bit by default, thus the input and output values are converted to single precision.
IP-Core Hardware#
The module takes all inputs and converts them from single precision to double precision.
The output is converted from double precision to single precision (using rounding to nearest value in both cases).
All input values are adjustable at run-time
The sample time is fixed!
The IP-Core uses Native Floating Point of the HDL-Coder
The time constant is written as its reciprocal to the AXI register to make the calculations on hardware simple (handled by the driver!)
The IP-Core uses an oversampling factor of 100
Example usage#
Vivado#
Add IP-Core to Vivado and connect to AXI (smartconnect)
Source IPCORE_CLK with a \(100\,MHz\) clock!
Connect other ports accordingly
Assign address to IP-Core
Build bitstream, export .xsa, update vitis platform
Vitis#
In a c-file that has xparameters.h included
Initialize the instance and configure it
struct uz_plantPT1_config_t config={
.base_address=XPAR_UZ_PLANTMODEL_PT1_0_BASEADDR,
.ip_core_frequency_Hz=100000000,
.gain=1.0f,
.time_constant=1.0f
};
uz_plantPT1_t* pt1=uz_plantPT1_init(config);
uz_plantPT1_set_input(pt1,0.0f);
Write the input and read the output of the block in the isr.c
Add a PI controller to control the PT1 (for example)
static float error_sum=0;
float output=uz_plantPT1_read_output(pt1);
float error=input-output;
error_sum+=error;
float K_p=0.3f;
float K_i=1.3f;
float pi_output=K_p*error+K_i*(1.0f/20000.0f)*error_sum; // 20000.0f is the sample rate of the ISR in this example
uz_plantPT1_set_input(pt1,pi_output);
Driver reference#
-
typedef struct uz_plantPT1_t uz_plantPT1_t#
Object definition of the plant model PT1 IP-Core.
-
struct uz_plantPT1_config_t#
Configuration struct for plant PT1 IP-Core.
-
uz_plantPT1_t *uz_plantPT1_init(struct uz_plantPT1_config_t config)#
Initialize an instance of the driver.
- Parameters:
config – Configuration struct
- Returns:
uz_plantPT1_t* Pointer to initialized instance of driver
-
void uz_plantPT1_reset_integrator(uz_plantPT1_t *self)#
Resets the integrator of the PT1 once.
- Parameters:
self – Pointer to driver instance that is reset
-
void uz_plantPT1_set_input(uz_plantPT1_t *self, float input_value)#
Set the input value u(k) of the PT1.
- Parameters:
self – Pointer to driver instance
input_value –
-
void uz_plantPT1_set_gain(uz_plantPT1_t *self, float gain)#
Set the gain of the PT1.
- Parameters:
self – Pointer to driver instance
gain –
-
void uz_plantPT1_set_time_constant(uz_plantPT1_t *self, float time_constant)#
Set the time constant of the PT1.
- Parameters:
self – Pointer to driver instance
time_constant –
-
float uz_plantPT1_read_output(uz_plantPT1_t *self)#
Reads the output of the PT1.
- Parameters:
self – Pointer to driver instance
- Returns:
float