forked from Archive/PX4-Autopilot
239 lines
9.4 KiB
Python
Executable File
239 lines
9.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
""" Script to parse board-specific timer_config.cpp and print the output groups
|
|
and timer config params to stdout
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
import re
|
|
from itertools import groupby
|
|
from copy import deepcopy
|
|
|
|
|
|
def find_matching_brackets(brackets, s, verbose):
|
|
idx = 0
|
|
opening = 0
|
|
first_open = -1
|
|
while idx < len(s):
|
|
if s[idx] == brackets[0]:
|
|
opening += 1
|
|
if first_open == -1:
|
|
first_open = idx
|
|
if s[idx] == brackets[1]:
|
|
opening -= 1
|
|
if opening == 0:
|
|
if verbose: print(first_open, idx, s[first_open:idx+1])
|
|
return first_open+1, idx
|
|
idx += 1
|
|
raise Exception('Failed to find opening/closing brackets in {:}'.format(s))
|
|
|
|
def extract_timer(line):
|
|
# Try format: initIOTimer(Timer::Timer5, DMA{DMA::Index1, DMA::Stream0, DMA::Channel6}),
|
|
search = re.search('Timer::([0-9a-zA-Z_]+)[,\)]', line, re.IGNORECASE)
|
|
if search:
|
|
return search.group(1), 'generic'
|
|
|
|
# nxp rt1062 format: initIOPWM(PWM::FlexPWM2),
|
|
search = re.search('PWM::Flex([0-9a-zA-Z_]+)[,\)]', line, re.IGNORECASE)
|
|
if search:
|
|
return search.group(1), 'imxrt'
|
|
|
|
return None, 'unknown'
|
|
|
|
def extract_timer_from_channel(line, num_channels_already_found):
|
|
# Try format: initIOTimerChannel(io_timers, {Timer::Timer5, Timer::Channel1}, {GPIO::PortA, GPIO::Pin0}),
|
|
search = re.search('Timer::([0-9a-zA-Z_]+), ', line, re.IGNORECASE)
|
|
if search:
|
|
return search.group(1)
|
|
|
|
# nxp rt1062 format: initIOTimerChannel(io_timers, {PWM::PWM2_PWM_A, PWM::Submodule0}, IOMUX::Pad::GPIO_B0_06),
|
|
search = re.search('PWM::(PWM[0-9]+)[_,\)]', line, re.IGNORECASE)
|
|
if search:
|
|
# imxrt uses a 1:1 timer group to channel association
|
|
return str(num_channels_already_found)
|
|
|
|
return None
|
|
|
|
def get_timer_groups(timer_config_file, verbose=False):
|
|
with open(timer_config_file, 'r') as f:
|
|
timer_config = f.read()
|
|
|
|
# timers
|
|
dshot_support = {} # key: timer
|
|
timers_start_marker = 'io_timers_t io_timers'
|
|
timers_start = timer_config.find(timers_start_marker)
|
|
if timers_start == -1:
|
|
raise Exception('"{:}" not found in {:}'.format(timers_start_marker, timer_config_file))
|
|
timer_config = timer_config[timers_start:]
|
|
open_idx, close_idx = find_matching_brackets(('{', '}'), timer_config, verbose)
|
|
timers_str = timer_config[open_idx:close_idx]
|
|
timers = []
|
|
for line in timers_str.splitlines():
|
|
line = line.strip()
|
|
if len(line) == 0 or line.startswith('//'):
|
|
continue
|
|
timer, timer_type = extract_timer(line)
|
|
|
|
if timer_type == 'imxrt':
|
|
if verbose: print('imxrt timer found')
|
|
max_num_channels = 16 # Just add a fixed number of timers
|
|
timers = [str(i) for i in range(max_num_channels)]
|
|
dshot_support = {str(i): False for i in range(max_num_channels)}
|
|
for i in range(8): # First 8 channels support dshot
|
|
dshot_support[str(i)] = True
|
|
break
|
|
|
|
if timer:
|
|
if verbose: print('found timer def: {:}'.format(timer))
|
|
dshot_support[timer] = 'DMA' in line
|
|
timers.append(timer)
|
|
else:
|
|
# Make sure we don't miss anything (e.g. for different syntax) or misparse (e.g. multi-line comments)
|
|
raise Exception('Unparsed timer in line: {:}'.format(line))
|
|
|
|
|
|
# channels
|
|
channels_start_marker = 'timer_io_channels_t timer_io_channels'
|
|
channels_start = timer_config.find(channels_start_marker)
|
|
if channels_start == -1:
|
|
raise Exception('"{:}" not found in {:}'.format(channels_start_marker, timer_config_file))
|
|
|
|
timer_config = timer_config[channels_start:]
|
|
open_idx, close_idx = find_matching_brackets(('{', '}'), timer_config, verbose)
|
|
channels = timer_config[open_idx:close_idx]
|
|
channel_timers = []
|
|
channel_types = []
|
|
|
|
for line in channels.splitlines():
|
|
line = line.strip()
|
|
if len(line) == 0 or line.startswith('//'):
|
|
continue
|
|
|
|
if verbose: print('--'+line+'--')
|
|
timer = extract_timer_from_channel(line, len(channel_timers))
|
|
|
|
if timer:
|
|
if verbose: print('Found timer: {:} in channel line {:}'.format(timer, line))
|
|
channel_types.append('cap' if 'capture' in line.lower() else 'pwm')
|
|
channel_timers.append(timer)
|
|
else:
|
|
# Make sure we don't miss anything (e.g. for different syntax) or misparse (e.g. multi-line comments)
|
|
raise Exception('Unparsed channel in line: {:}'.format(line))
|
|
|
|
if len(channel_timers) == 0:
|
|
raise Exception('No channels found in "{:}"'.format(channels))
|
|
|
|
groups = [(timers.index(k), len(list(g)), dshot_support[k]) for k, g in groupby(channel_timers)]
|
|
outputs = {
|
|
'types': channel_types,
|
|
'groups': groups
|
|
}
|
|
|
|
return outputs
|
|
|
|
def get_output_groups(timer_groups, param_prefix="PWM_MAIN",
|
|
channel_labels=["PWM Main", "PWM Capture"],
|
|
standard_params=[],
|
|
extra_function_groups=[], pwm_timer_param=None,
|
|
verbose=False):
|
|
""" convert the timer groups into an output_groups section of module.yaml
|
|
and extra timer params
|
|
"""
|
|
|
|
instance_start = 1
|
|
output_groups = []
|
|
timer_params = {}
|
|
instance_start_label = [ 1, 1 ]
|
|
for timer_index, group_count, dshot_support in timer_groups['groups']:
|
|
|
|
# check for capture vs normal pins for the label
|
|
types = timer_groups['types'][instance_start-1:instance_start+group_count-1]
|
|
if not all(types[0] == t for t in types):
|
|
# Should this ever be needed, we can extend this script to handle that
|
|
raise Exception('Implementation requires all channel types for a timer to be equal (types: {:})'.format(types))
|
|
if types[0] == 'pwm':
|
|
channel_type_idx = 0
|
|
elif types[0] == 'cap':
|
|
channel_type_idx = 1
|
|
else:
|
|
raise Exception('unsupported channel type: {:}'.format(types[0]))
|
|
|
|
channel_label = channel_labels[channel_type_idx]
|
|
channel_type_instance = instance_start_label[channel_type_idx]
|
|
group_label = channel_label + ' ' + str(channel_type_instance)
|
|
if group_count > 1:
|
|
group_label += '-' + str(channel_type_instance+group_count-1)
|
|
group = {
|
|
'param_prefix': param_prefix,
|
|
'channel_label': channel_label,
|
|
'instance_start': instance_start,
|
|
'instance_start_label': channel_type_instance,
|
|
'extra_function_groups': deepcopy(extra_function_groups),
|
|
'num_channels': group_count,
|
|
'standard_params': deepcopy(standard_params),
|
|
'group_label': group_label,
|
|
'channel_label_module_name_prefix': False,
|
|
}
|
|
|
|
if pwm_timer_param is not None:
|
|
pwm_timer_param_cp = deepcopy(pwm_timer_param)
|
|
timer_param_name = param_prefix+'_TIM'+str(timer_index)
|
|
|
|
group['config_parameters'] = [
|
|
{
|
|
'param': timer_param_name,
|
|
'function': 'primary',
|
|
}
|
|
]
|
|
|
|
if dshot_support:
|
|
# don't show pwm limit params when dshot enabled
|
|
|
|
for standard_param in group['standard_params']:
|
|
group['standard_params'][standard_param]['show_if'] = timer_param_name + '>=-1'
|
|
|
|
# indicate support for changing motor spin direction
|
|
group['supported_actions'] = {
|
|
'set_spin_direction1': {
|
|
'supported_if': timer_param_name + '<-1',
|
|
'actuator_types': ['motor']
|
|
},
|
|
'set_spin_direction2': {
|
|
'supported_if': timer_param_name + '<-1',
|
|
'actuator_types': ['motor']
|
|
},
|
|
}
|
|
else:
|
|
# remove dshot entries if no dshot support
|
|
values = pwm_timer_param_cp['values']
|
|
for key in list(values.keys()):
|
|
if 'dshot' in values[key].lower():
|
|
del values[key]
|
|
|
|
for descr_type in ['short', 'long']:
|
|
descr = pwm_timer_param_cp['description'][descr_type]
|
|
pwm_timer_param_cp['description'][descr_type] = \
|
|
descr.replace('${label}', group_label)
|
|
timer_params[timer_param_name] = pwm_timer_param_cp
|
|
output_groups.append(group)
|
|
instance_start += group_count
|
|
instance_start_label[channel_type_idx] += group_count
|
|
return (output_groups, timer_params)
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser(description='Extract output groups from timer_config.cpp')
|
|
|
|
parser.add_argument('--timer-config', type=str, action='store',
|
|
help='timer_config.cpp file', required=True)
|
|
parser.add_argument('-v', '--verbose', dest='verbose', action='store_true',
|
|
help='Verbose Output')
|
|
|
|
args = parser.parse_args()
|
|
verbose = args.verbose
|
|
timer_groups = get_timer_groups(args.timer_config, verbose)
|
|
print('timer groups: {:}'.format(timer_groups))
|
|
output_groups, timer_params = get_output_groups(timer_groups, verbose=verbose)
|
|
print('output groups: {:}'.format(output_groups))
|
|
print('timer params: {:}'.format(timer_params))
|