PWM Usage Guide#
Overview#
The K230 platform contains two PWM controllers, each providing 3 channels.
Controller |
Base Address |
Channel Range |
Number of Channels |
sysfs Device |
|---|---|---|---|---|
pwm0_2 |
0x9140a000 |
PWM0-PWM2 |
3 |
pwmchip0 |
pwm3_5 |
0x9140a040 |
PWM3-PWM5 |
3 |
pwmchip3 |
Note: The 3 channels under the same controller share the same PWM period; only the duty cycle can be configured independently.
The register mapping for each controller is as follows:
Register |
Offset |
Description |
|---|---|---|
PWMCFG |
0x00 |
Configuration register |
PWMCOUNT |
0x08 |
Counter register |
PWMS |
0x10 |
Status register |
PWMCMP(0) |
0x20 |
Period comparison value (Period) |
PWMCMP(1) |
0x24 |
Channel 0 duty cycle (Duty) |
PWMCMP(2) |
0x28 |
Channel 1 duty cycle (Duty) |
PWMCMP(3) |
0x2C |
Channel 2 duty cycle (Duty) |
Device Tree Configuration#
The K230 PWM controller needs to be configured in the device tree, including the controller itself and the IOMUX pin multiplexing configuration.
// arch/riscv/boot/dts/canaan/k230.dtsi
&pwm0_2: pwm0_2@9140a000 {
compatible = "canaan,k230-pwm";
reg = <0x0 0x9140a000 0x0 0x40>;
clocks = <&pwm_pclk_gate>;
};
&pwm3_5: pwm3_5@9140a040 {
compatible = "canaan,k230-pwm";
reg = <0x0 0x9140a040 0x0 0x40>;
clocks = <&pwm_pclk_gate>;
};
// arch/riscv/boot/dts/canaan/k230-canmv-01studio-lcd.dts
&iomux {
pwm2_pins: pwm2_pins {
pins = K230_IO46;
function = K230_IO46_PWM2;
};
pwm3_pins: pwm3_pins {
pins = K230_IO47;
function = K230_IO47_PWM3;
};
};
&pwm0_2 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pwm2_pins>;
};
&pwm3_5 {
status = "okay";
pinctrl-names = "default";
pinctrl-0 = <&pwm3_pins>;
};
Note:
The above example only configures the PWM2 and PWM3 pins. If you need to use other PWM channels, please refer to this configuration to modify the corresponding IOMUX settings.
PWM driver code is located at:
drivers/pwm/pwm-k230.c
User-space PWM Operation#
Using the pwm-test Tool#
Example usage of the pwm-test tool:
# 1kHz PWM, 45.5% duty cycle, pwmchip0 channel 2 (PWM2)
pwm-test.sh 0 2 1000 45.5
# 10kHz PWM, 25.5% duty cycle, pwmchip3 channel 0 (PWM3)
pwm-test.sh 3 0 10000 25.5
# View help
pwm-test.sh --help
Configuring via sysfs#
An example of configuring PWM using the sysfs interface is as follows:
# Export channel 2 (PWM2) of PWM chip0
echo 2 > /sys/class/pwm/pwmchip0/export
# Enable PWM
echo 1 > /sys/class/pwm/pwmchip0/pwm2/enable
# Set period (unit: nanoseconds) - 1MHz (1us)
echo 1000 > /sys/class/pwm/pwmchip0/pwm2/period
# Set duty cycle (unit: nanoseconds) - 50% duty cycle
echo 500 > /sys/class/pwm/pwmchip0/pwm2/duty_cycle
# --- Common configuration examples ---
# 1kHz, 50% duty cycle (period 1ms)
echo 1000000 > /sys/class/pwm/pwmchip0/pwm2/period
echo 500000 > /sys/class/pwm/pwmchip0/pwm2/duty_cycle
# 100Hz, 50% duty cycle (period 10ms)
echo 10000000 > /sys/class/pwm/pwmchip0/pwm2/period
echo 5000000 > /sys/class/pwm/pwmchip0/pwm2/duty_cycle
# 10kHz, 25% duty cycle (period 100us)
echo 100000 > /sys/class/pwm/pwmchip0/pwm2/period
echo 25000 > /sys/class/pwm/pwmchip0/pwm2/duty_cycle
# Unexport the PWM channel
echo 2 > /sys/class/pwm/pwmchip0/unexport
PWM Device Structure
The PWM device structure in sysfs is as follows:
/root # ls -la /sys/class/pwm/
total 0
drwxr-xr-x 2 root root 0 May 20 06:29 .
drwxr-xr-x 56 root root 0 Jan 1 1970 ..
lrwxrwxrwx 1 root root 0 May 20 06:29 pwmchip0 -> ../../devices/platform/soc/9140a000.pwm0_2/pwm/pwmchip0
lrwxrwxrwx 1 root root 0 May 20 06:29 pwmchip3 -> ../../devices/platform/soc/9140a040.pwm3_5/pwm/pwmchip3
# After exporting channel 2, the corresponding pwm2 directory will be generated
/root # ls -la /sys/class/pwm/pwmchip0/
total 0
drwxr-xr-x 2 root root 0 May 20 06:29 .
drwxr-xr-x 56 root root 0 Jan 1 1970 ..
-rwxrwxrwx 1 root root 4096 May 20 06:29 alias
-rwxrwxrwx 1 root root 4096 May 20 06:29 device
-rwxrwxrwx 1 root root 4096 May 20 06:29 export
lrwxrwxrwx 1 root root 0 May 20 06:29 pwm2 -> ./pwm2
-rwxrwxrwx 1 root root 4096 May 20 06:29 uevent
-rwxrwxrwx 1 root root 4096 May 20 06:29 unexport
View Current Configuration
# View period
cat /sys/class/pwm/pwmchip0/pwm2/period
# View duty cycle
cat /sys/class/pwm/pwmchip0/pwm2/duty_cycle
# View polarity
cat /sys/class/pwm/pwmchip0/pwm2/polarity
# View enable status
cat /sys/class/pwm/pwmchip0/pwm2/enable
Note: Check IOMUX Configuration
Please confirm that the IOMUX pin multiplexing configuration is correct before use:
# Check the function configuration of IO46 (PWM2) k230_iomux.py --pin io46 --funcs # Check the function configuration of IO47 (PWM3) k230_iomux.py --pin io47 --funcs
C Language Example#
This example is a command-line PWM test tool, with usage examples as follows:
# Default configuration: pwmchip0, channel 2, 1kHz, 50% duty cycle
./pwm_test
# Custom configuration
./pwm_test 0 2 1000 50 # pwmchip0, channel 2, 1kHz, 50%
./pwm_test 3 0 10000 25 # pwmchip3, channel 0, 10kHz, 25%
# View help
./pwm_test --help
The detailed code is as follows:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>
#define SYSFS_PWM_DIR "/sys/class/pwm"
#define MAX_LEN 256
static int current_chip = 0;
static int current_channel = 0;
// Function declarations
int write_sysfs_attr(const char *path, const char *value);
int pwm_export(int chip, int channel);
int pwm_unexport(int chip, int channel);
int pwm_set_polarity(int chip, int channel, const char *polarity);
int pwm_set_period(int chip, int channel, unsigned long period_ns);
int pwm_set_duty_cycle(int chip, int channel, unsigned long duty_ns);
int pwm_enable(int chip, int channel, int enable);
unsigned long pwm_read_value(int chip, int channel, const char *attr);
// Signal handler to cleanup PWM on Ctrl+C
void cleanup_handler(int sig)
{
(void)sig;
printf("\nStopping PWM... clean \n");
exit(0);
}
// Write sysfs attribute
int write_sysfs_attr(const char *path, const char *value)
{
int fd = open(path, O_WRONLY);
if (fd < 0) {
printf("Failed to open sysfs attribute path=%s \n",path);
return -1;
}
ssize_t len = strlen(value);
if (write(fd, value, len) != len) {
printf("Failed to write sysfs attribute p=%s v=%s\n", path,value);
close(fd);
return -1;
}
close(fd);
return 0;
}
// Export PWM channel
int pwm_export(int chip, int channel)
{
char path[MAX_LEN];
snprintf(path, sizeof(path), "%s/pwmchip%d/export", SYSFS_PWM_DIR, chip);
char value[16];
snprintf(value, sizeof(value), "%d", channel);
return write_sysfs_attr(path, value);
}
// Unexport PWM channel
int pwm_unexport(int chip, int channel)
{
char path[MAX_LEN];
char check_path[MAX_LEN];
snprintf(check_path, sizeof(check_path), "%s/pwmchip%d/pwm%d", SYSFS_PWM_DIR, chip, channel);
if (access(check_path, F_OK) != 0) {
return 0;
}
snprintf(path, sizeof(path), "%s/pwmchip%d/unexport", SYSFS_PWM_DIR, chip);
char value[16];
snprintf(value, sizeof(value), "%d", channel);
return write_sysfs_attr(path, value);
}
// Set PWM polarity
int pwm_set_polarity(int chip, int channel, const char *polarity)
{
char path[MAX_LEN];
snprintf(path, sizeof(path), "%s/pwmchip%d/pwm%d/polarity",
SYSFS_PWM_DIR, chip, channel);
return write_sysfs_attr(path, polarity);
}
// Set PWM period
int pwm_set_period(int chip, int channel, unsigned long period_ns)
{
char path[MAX_LEN];
snprintf(path, sizeof(path), "%s/pwmchip%d/pwm%d/period",
SYSFS_PWM_DIR, chip, channel);
char value[32];
snprintf(value, sizeof(value), "%lu", period_ns);
return write_sysfs_attr(path, value);
}
// Set PWM duty cycle
int pwm_set_duty_cycle(int chip, int channel, unsigned long duty_ns)
{
char path[MAX_LEN];
snprintf(path, sizeof(path), "%s/pwmchip%d/pwm%d/duty_cycle",
SYSFS_PWM_DIR, chip, channel);
char value[32];
snprintf(value, sizeof(value), "%lu", duty_ns);
return write_sysfs_attr(path, value);
}
// Enable/Disable PWM
int pwm_enable(int chip, int channel, int enable)
{
char path[MAX_LEN];
snprintf(path, sizeof(path), "%s/pwmchip%d/pwm%d/enable",
SYSFS_PWM_DIR, chip, channel);
return write_sysfs_attr(path, enable ? "1" : "0");
}
// Read PWM value
unsigned long pwm_read_value(int chip, int channel, const char *attr)
{
char path[MAX_LEN];
snprintf(path, sizeof(path), "%s/pwmchip%d/pwm%d/%s",
SYSFS_PWM_DIR, chip, channel, attr);
int fd = open(path, O_RDONLY);
if (fd < 0) {
perror("Failed to open sysfs attribute");
return 0;
}
char buffer[32];
ssize_t len = read(fd, buffer, sizeof(buffer) - 1);
close(fd);
if (len > 0) {
buffer[len] = '\0';
return strtoul(buffer, NULL, 10);
}
return 0;
}
void print_usage(const char *program)
{
printf("K230 PWM Basic Test\n");
printf("====================\n\n");
printf("Usage: %s [chip] [channel] [frequency] [duty]\n\n", program);
printf("Arguments:\n");
printf(" chip - PWM chip number (0 or 3), default: 0\n");
printf(" channel - PWM channel number (0, 1, 2), default: 0\n");
printf(" frequency- Frequency in Hz, default: 1000\n");
printf(" duty - Duty cycle (0-100), default: 50\n\n");
printf("Examples:\n");
printf(" %s # Default: pwmchip0 channel 0, 1kHz, 50%%\n", program);
printf(" %s 0 2 1000 50 # pwmchip0 channel 2, 1kHz, 50%%\n", program);
printf(" %s 3 0 10000 25 # pwmchip3 channel 0, 10kHz, 25%%\n", program);
}
int main(int argc, char *argv[])
{
int chip = 0;
int channel = 2;
int frequency = 1000;
int duty = 50;
if (argc > 1) {
if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0) {
print_usage(argv[0]);
return 0;
}
chip = atoi(argv[1]);
}
if (argc > 2) {
channel = atoi(argv[2]);
}
if (argc > 3) {
frequency = atoi(argv[3]);
}
if (argc > 4) {
duty = atoi(argv[4]);
}
current_chip = chip;
current_channel = channel;
unsigned long period_ns = 1000000000UL / frequency;
unsigned long duty_ns = period_ns * duty / 100;
printf("Configuring PWM %d:%d\n", chip, channel);
printf(" Frequency: %d Hz\n", frequency);
printf(" Period: %lu ns\n", period_ns);
printf(" Duty: %d%% (%lu ns)\n\n", duty, duty_ns);
pwm_unexport(chip, channel);
usleep(10);
printf("Exporting PWM...\n");
if (pwm_export(chip, channel) < 0) {
printf("PWM already exported or error\n");
}
pwm_enable(chip, channel, 1);
pwm_set_polarity(chip, channel, "inversed");
pwm_set_period(chip, channel, period_ns);
pwm_set_duty_cycle(chip, channel, duty_ns);
printf("\nCurrent Configuration:\n");
printf(" Period: %lu ns\n", pwm_read_value(chip, channel, "period"));
printf(" Duty: %lu ns\n", pwm_read_value(chip, channel, "duty_cycle"));
printf(" Enable: %lu\n", pwm_read_value(chip, channel, "enable"));
printf("\nPWM running... Press Ctrl+C to stop.\n");
signal(SIGINT, cleanup_handler);
while(1) sleep(30);
return 0;
}
Compilation and execution:
# Compile
riscv64-unknown-linux-gnu-gcc -o pwm_test pwm_test.c
# Run
./pwm_test 0 2 1000 50 # pwmchip0, channel 2, 1kHz, 50%
./pwm_test 3 0 10000 25 # pwmchip3, channel 0, 10kHz, 25%
Python Example#
Using the pwmio Library (CircuitPython)#
import pwmio
import board
import time
# Create PWM output, frequency 50Hz
pwm = pwmio.PWMOut(board.pin.PWM2, frequency=50)
pwm.duty_cycle = 2 ** 15 # 50% duty cycle (half of 2**16)
while True:
time.sleep(1)
Pure Python Example - Basic Control#
#!/usr/bin/env python3
"""
K230 PWM Python Example - Basic Control
Usage:
python pwm_basic.py # Default: chip=0, channel=2, 1kHz, 50%
python pwm_basic.py -c 0 -C 2 # chip=0, channel=2
python pwm_basic.py -f 10000 # 10kHz
python pwm_basic.py -d 25 # 25% duty cycle
python pwm_basic.py --help # Show help
"""
import os
import sys
import time
import argparse
SYSFS_PWM_DIR = "/sys/class/pwm"
class K230PWM:
def __init__(self, chip=0, channel=0):
"""
Initialize PWM controller
Args:
chip: PWM chip number (0 or 3)
channel: PWM channel number (0, 1, 2)
"""
self.chip = chip
self.channel = channel
self.pwm_path = f"{SYSFS_PWM_DIR}/pwmchip{chip}/pwm{channel}"
self.exported = False
def export(self):
"""Export PWM channel"""
export_path = f"{SYSFS_PWM_DIR}/pwmchip{self.chip}/export"
if not os.path.exists(self.pwm_path):
with open(export_path, 'w') as f:
f.write(str(self.channel))
self.exported = True
time.sleep(0.1)
def unexport(self):
"""Unexport PWM channel"""
if self.exported:
unexport_path = f"{SYSFS_PWM_DIR}/pwmchip{self.chip}/unexport"
with open(unexport_path, 'w') as f:
f.write(str(self.channel))
self.exported = False
def set_polarity(self, polarity="inversed"):
"""
Set PWM polarity
Args:
polarity: "normal" or "inversed"
"""
with open(f"{self.pwm_path}/polarity", 'w') as f:
f.write(polarity)
def set_period(self, period_ns):
"""
Set PWM period (nanoseconds)
Args:
period_ns: Period length in nanoseconds
"""
with open(f"{self.pwm_path}/period", 'w') as f:
f.write(str(period_ns))
def set_duty_cycle(self, duty_ns):
"""
Set PWM duty cycle (nanoseconds)
Args:
duty_ns: Duty cycle time in nanoseconds
"""
with open(f"{self.pwm_path}/duty_cycle", 'w') as f:
f.write(str(duty_ns))
def set_frequency(self, frequency_hz):
"""
Set PWM frequency (Hz)
Args:
frequency_hz: Frequency in Hz
"""
period_ns = int(1000000000 / frequency_hz)
self.set_period(period_ns)
def set_duty_percent(self, duty_percent):
"""
Set PWM duty cycle percentage
Args:
duty_percent: Duty cycle percentage (0-100)
"""
with open(f"{self.pwm_path}/period", 'r') as f:
period_ns = int(f.read().strip())
duty_ns = int(period_ns * duty_percent / 100)
self.set_duty_cycle(duty_ns)
def enable(self):
"""Enable PWM"""
with open(f"{self.pwm_path}/enable", 'w') as f:
f.write("1")
def disable(self):
"""Disable PWM"""
with open(f"{self.pwm_path}/enable", 'w') as f:
f.write("0")
def get_state(self):
"""Get current state"""
state = {}
try:
with open(f"{self.pwm_path}/period", 'r') as f:
state['period'] = int(f.read().strip())
with open(f"{self.pwm_path}/duty_cycle", 'r') as f:
state['duty_cycle'] = int(f.read().strip())
with open(f"{self.pwm_path}/polarity", 'r') as f:
state['polarity'] = f.read().strip()
with open(f"{self.pwm_path}/enable", 'r') as f:
state['enabled'] = bool(int(f.read().strip()))
except Exception as e:
state['error'] = str(e)
return state
def __enter__(self):
self.export()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
self.disable()
self.unexport()
def parse_args():
"""Parse command line arguments"""
parser = argparse.ArgumentParser(
description="K230 PWM Python Test Tool",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python pwm_basic.py # Default: chip=0, channel=0, 1kHz, 50%
python pwm_basic.py -c 0 -C 2 -f 10000 -d 25 # chip=0, channel=2 10kHz 25% duty
python pwm_basic.py -f 10000 # 10kHz
python pwm_basic.py -d 25 # 25% duty cycle
"""
)
parser.add_argument('-c', '--chip', type=int, default=0,
help='PWM chip number (0 or 3), default: 0')
parser.add_argument('-C', '--channel', type=int, default=2,
help='PWM channel number (0, 1, 2), default: 2')
parser.add_argument('-f', '--frequency', type=int, default=1000,
help='Frequency in Hz, default: 1000')
parser.add_argument('-d', '--duty', type=float, default=50,
help='Duty cycle percentage (0-100), default: 50')
parser.add_argument('-p', '--polarity', type=str, default='inversed',
choices=['normal', 'inversed'],
help='Polarity (normal/inversed), default: inversed')
return parser.parse_args()
def main():
args = parse_args()
print("K230 PWM Python Test")
print("====================\n")
print(f"Configuration:")
print(f" Chip: {args.chip}")
print(f" Channel: {args.channel}")
print(f" Frequency: {args.frequency} Hz")
print(f" Duty: {args.duty}%")
print(f" Polarity: {args.polarity}")
print()
with K230PWM(chip=args.chip, channel=args.channel) as pwm:
pwm.enable()
pwm.set_frequency(args.frequency)
pwm.set_duty_percent(args.duty)
print(f"State: {pwm.get_state()}")
print("Running... Press Ctrl+C to stop")
while True:
time.sleep(1)
if __name__ == "__main__":
main()
Pure Python Example - Motor Control#
#!/usr/bin/env python3
"""
K230 PWM Motor Speed Control Example - Console Version
"""
import argparse
import os
import sys
import time
class MotorController:
def __init__(self, chip=0, channel=0, frequency=2000, min_duty=10, max_duty=100):
self.chip = chip
self.channel = channel
self.frequency = frequency
self.min_duty = min_duty
self.max_duty = max_duty
self.running = False
self.duty = 0
self.period_ns = int(1e9 / frequency)
self._export()
def _unexport(self):
"""Unexport PWM channel"""
unexport_path = f"/sys/class/pwm/pwmchip{self.chip}/unexport"
if os.path.exists(f"/sys/class/pwm/pwmchip{self.chip}/pwm{self.channel}"):
with open(unexport_path, 'w') as f:
f.write(str(self.channel))
def _export(self):
self._unexport()
time.sleep(0.1)
export_path = f"/sys/class/pwm/pwmchip{self.chip}/export"
with open(export_path, 'w') as f:
f.write(str(self.channel))
with open(f"/sys/class/pwm/pwmchip{self.chip}/pwm{self.channel}/period", 'w') as f:
f.write(str(self.period_ns))
with open(f"/sys/class/pwm/pwmchip{self.chip}/pwm{self.channel}/duty_cycle", 'w') as f:
f.write(str(int(self.period_ns * self.min_duty / 100)))
with open(f"/sys/class/pwm/pwmchip{self.chip}/pwm{self.channel}/polarity", 'w') as f:
f.write("inversed")
def set_speed(self, percent):
"""Set motor speed (0-100%)"""
if percent < 0:
percent = 0
if percent > 100:
percent = 100
duty = self.min_duty + (self.max_duty - self.min_duty) * percent / 100
duty_ns = int(self.period_ns * duty / 100)
with open(f"/sys/class/pwm/pwmchip{self.chip}/pwm{self.channel}/duty_cycle", 'w') as f:
f.write(str(duty_ns))
self.duty = duty
return duty
def start(self):
"""Start motor"""
with open(f"/sys/class/pwm/pwmchip{self.chip}/pwm{self.channel}/enable", 'w') as f:
f.write("1")
def stop(self):
"""Stop motor"""
with open(f"/sys/class/pwm/pwmchip{self.chip}/pwm{self.channel}/enable", 'w') as f:
f.write("0")
def ramp_up(self, target_percent, duration=2):
"""Ramp up speed"""
steps = 20
interval = duration / steps
current = 0
for _ in range(steps):
if current >= target_percent:
break
current = min(current + (target_percent - current) / 2, target_percent)
self.set_speed(current)
time.sleep(interval)
self.set_speed(target_percent)
def ramp_down(self, target_percent, duration=2):
"""Ramp down speed"""
steps = 20
interval = duration / steps
current = self.duty if self.duty > 0 else 100
for _ in range(steps):
if current <= target_percent:
break
current = max(current - (current - target_percent) / 2, target_percent)
self.set_speed(current)
time.sleep(interval)
self.set_speed(target_percent)
def cleanup(self):
"""Cleanup"""
self.stop()
unexport_path = f"/sys/class/pwm/pwmchip{self.chip}/unexport"
with open(unexport_path, 'w') as f:
f.write(str(self.channel))
def main():
parser = argparse.ArgumentParser(
description="K230 Motor Speed Control Example",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog="""
Examples:
python pwm_motor.py # Default: chip=0, channel=2, freq=2000Hz
python pwm_motor.py -c 0 -C 2 # Use chip0 channel2
python pwm_motor.py --min-duty 5 --max-duty 100 # Wider speed range
python pwm_motor.py --help # Show help
Configuration:
Frequency: 2000 Hz (2kHz) default, max ~2200Hz
Duty range: 10-100% (adjustable via --min-duty and --max-duty)
"""
)
parser.add_argument('-c', '--chip', type=int, default=0,
help='PWM chip number (0 or 3), default: 0')
parser.add_argument('-C', '--channel', type=int, default=2,
help='PWM channel number (0, 1, 2), default: 2')
parser.add_argument('-f', '--frequency', type=int, default=2000,
help='PWM frequency in Hz, default: 2000')
parser.add_argument('--min-duty', type=float, default=10,
help='Minimum duty cycle %% (start speed), default: 10')
parser.add_argument('--max-duty', type=float, default=100,
help='Maximum duty cycle %% (max speed), default: 100')
args = parser.parse_args()
print("K230 Motor Control Example")
print("==========================\n")
print(f"Configuration: chip={args.chip}, channel={args.channel}, frequency={args.frequency}Hz\n")
print(f"Duty range: {args.min_duty}% - {args.max_duty}%\n")
motor = MotorController(chip=args.chip, channel=args.channel, frequency=args.frequency,
min_duty=args.min_duty, max_duty=args.max_duty)
try:
print("Starting motor...")
motor.start()
time.sleep(1)
print("Accelerating to 100%...")
motor.ramp_up(100, duration=2)
time.sleep(1)
print("Decelerating to 0%...")
motor.ramp_down(0, duration=2)
time.sleep(1)
print("Stopping...")
motor.stop()
finally:
motor.cleanup()
print("Cleanup done.")
if __name__ == "__main__":
main()
