mirror of
				https://github.com/rbtsco/cubemx2kicad.git
				synced 2025-11-04 10:35:12 +02:00 
			
		
		
		
	Initial commit
This commit is contained in:
		
							
								
								
									
										157
									
								
								cubemx2kicad.js
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										157
									
								
								cubemx2kicad.js
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,157 @@
 | 
			
		||||
#!/usr/bin/node
 | 
			
		||||
 | 
			
		||||
const fs = require('fs');
 | 
			
		||||
const util = require('util');
 | 
			
		||||
 | 
			
		||||
const print_log = true; // TODO: Make configurable
 | 
			
		||||
const save_backup = true; // TODO: Make configurable
 | 
			
		||||
 | 
			
		||||
const ioc_fn = process.argv[2];
 | 
			
		||||
const schematic_fn = process.argv[3];
 | 
			
		||||
 | 
			
		||||
if (process.argv.length < 4 || !fs.existsSync(ioc_fn) || !fs.existsSync(schematic_fn)) {
 | 
			
		||||
    console.error(`
 | 
			
		||||
Usage:
 | 
			
		||||
 | 
			
		||||
${process.argv[1]} STM32CubeMX_file.ioc KiCad_schematic_file.kicad_sch
 | 
			
		||||
`);
 | 
			
		||||
 | 
			
		||||
    process.exit(1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
const schematic_file = fs.readFileSync(schematic_fn, 'utf-8')
 | 
			
		||||
const cube_file = fs.readFileSync(ioc_fn, 'utf-8')
 | 
			
		||||
 | 
			
		||||
// Stores CubeMX pin configurations
 | 
			
		||||
const pin_map = {};
 | 
			
		||||
 | 
			
		||||
const pin_re = /(^P[A-Z][0-9]+)(-[^.]+)?\.([^=]+)=(.*)$/
 | 
			
		||||
const name_re = /Mcu.UserName=(.*)/
 | 
			
		||||
const sym_re = /\(symbol "(?:([^:]*):)?([^"]+)"/
 | 
			
		||||
const ki_pin_name_re = /(\s*\(name\s+")([^"]*)(".*)/
 | 
			
		||||
 | 
			
		||||
// Used for matching up to the right KiCad symbol
 | 
			
		||||
let mcu_name = ""
 | 
			
		||||
 | 
			
		||||
cube_file.split(/[\r\n]+/).forEach(function(line) {
 | 
			
		||||
    // First find anything that looks like a pin property
 | 
			
		||||
    const match = line.match(pin_re);
 | 
			
		||||
    if (match) {
 | 
			
		||||
        // console.log(`${line} => ${util.inspect(match, {colors: true})}`);
 | 
			
		||||
 | 
			
		||||
        const name = match[1];
 | 
			
		||||
 | 
			
		||||
        if (!pin_map[name]) pin_map[name] = {
 | 
			
		||||
            base_name: name,
 | 
			
		||||
            name_suffix: match[2]
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        const pd = pin_map[name];
 | 
			
		||||
 | 
			
		||||
        pd[match[3]] = match[4]
 | 
			
		||||
    } else {
 | 
			
		||||
        // If we don't have a pin, check for an MCU name
 | 
			
		||||
        const mu = line.match(name_re);
 | 
			
		||||
        if (mu) {
 | 
			
		||||
            // console.log(`${line} => ${util.inspect(mu, {colors: true})}`);
 | 
			
		||||
            mcu_name = mu[1];
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
// STM32 MCU names use 'x' as wildcard
 | 
			
		||||
const mcu_re = new RegExp(`^${mcu_name.replace(/x/, '.*')}.*`);
 | 
			
		||||
 | 
			
		||||
// Create pin names. Spaces are converted to underscore by KiCad. Leaving these spaces for when they finally address that issue.
 | 
			
		||||
for (const pn of Object.keys(pin_map)) {
 | 
			
		||||
    const pin = pin_map[pn];
 | 
			
		||||
    let name;
 | 
			
		||||
    if (pin.GPIO_Label) {
 | 
			
		||||
        if (pin.Signal) {
 | 
			
		||||
            name = `${pin.GPIO_Label} (${pin.Signal} / ${pin.base_name}${pin.name_suffix || ''})`
 | 
			
		||||
        } else {
 | 
			
		||||
            name = `${pin.GPIO_Label} (${pin.base_name}${pin.name_suffix || ''})`
 | 
			
		||||
        }
 | 
			
		||||
    } else {
 | 
			
		||||
        if (pin.Signal) {
 | 
			
		||||
            name = `${pin.Signal} (${pin.base_name}${pin.name_suffix || ''})`;
 | 
			
		||||
        } else {
 | 
			
		||||
            name = `${pin.base_name}${pin.name_suffix || ''}`;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    pin.user_name = name;
 | 
			
		||||
    // console.log(`${pin.base_name} => ${name}`)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// The current 'active' symbol
 | 
			
		||||
let sym;
 | 
			
		||||
// The current active pin
 | 
			
		||||
let ap;
 | 
			
		||||
 | 
			
		||||
const output = [];
 | 
			
		||||
const log = [];
 | 
			
		||||
 | 
			
		||||
schematic_file.split(/[\r\n]+/).forEach(function(line) {
 | 
			
		||||
    const sm = line.match(sym_re);
 | 
			
		||||
    if (sm) {
 | 
			
		||||
        // console.log(util.inspect(sm ))
 | 
			
		||||
        sym = sm[2];
 | 
			
		||||
    } else if (sym && mcu_re.test(sym)) {
 | 
			
		||||
        const m = line.match(ki_pin_name_re);
 | 
			
		||||
        if (m) {
 | 
			
		||||
            // console.log(util.inspect(m, {colors:true}));
 | 
			
		||||
 | 
			
		||||
            const m2 = m[2].match(/^(P[A-Z][0-9]+)(-.+)?$/) || m[2].match(/.*[ _(](P[A-Z][0-9]+)(-.+)?\)$/);
 | 
			
		||||
 | 
			
		||||
            if (m2) {
 | 
			
		||||
                // console.log(m2);
 | 
			
		||||
    
 | 
			
		||||
                const n = m2[1];
 | 
			
		||||
 | 
			
		||||
                let replacement;
 | 
			
		||||
    
 | 
			
		||||
                if (pin_map[n]) {
 | 
			
		||||
                    replacement = pin_map[n].user_name;
 | 
			
		||||
                } else {
 | 
			
		||||
                    replacement = `${m2[1]}${m2[2]||''}`;
 | 
			
		||||
                }
 | 
			
		||||
                if (print_log) {
 | 
			
		||||
                    log.push(`${n} => ${replacement}`)
 | 
			
		||||
                }
 | 
			
		||||
                output.push(`${m[1]}${replacement}${m[3]}`);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    output.push(line);
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function write_and_backup(fn, data, mode = 'utf-8') {
 | 
			
		||||
    if (save_backup) {
 | 
			
		||||
        if (fs.existsSync(fn)) {
 | 
			
		||||
            let bfn;
 | 
			
		||||
            let i = 0;
 | 
			
		||||
            do {
 | 
			
		||||
                bfn = `${fn}.${++i}`
 | 
			
		||||
            } while (fs.existsSync(bfn));
 | 
			
		||||
            fs.renameSync(fn, bfn);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    fs.writeFileSync(fn, data, mode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
write_and_backup(schematic_fn, output.join('\n'));
 | 
			
		||||
 | 
			
		||||
if (print_log) {
 | 
			
		||||
    // Prints the list of pin modifications done, nicely sorted
 | 
			
		||||
    console.log(log.sort(function(a, b) {
 | 
			
		||||
        return a.localeCompare(b, undefined, {
 | 
			
		||||
          numeric: true,
 | 
			
		||||
          sensitivity: 'base'
 | 
			
		||||
        });
 | 
			
		||||
      } ).join('\n'))
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								package.json
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
{
 | 
			
		||||
  "name": "cubemx2kicad",
 | 
			
		||||
  "version": "1.0.0",
 | 
			
		||||
  "description": "Export STM32 CubeMX pin assignments to matching symbols in a KiCad schematic",
 | 
			
		||||
  "main": "cubemx2kicad.js",
 | 
			
		||||
  "scripts": {
 | 
			
		||||
    "test": "echo \"Error: no test specified\" && exit 1"
 | 
			
		||||
  },
 | 
			
		||||
  "author": "Stefan Hamminga",
 | 
			
		||||
  "license": "apache-2.0"
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										32
									
								
								readme.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								readme.md
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,32 @@
 | 
			
		||||
# Export STM32 CubeMX pin assignments to matching symbols in a KiCad schematic
 | 
			
		||||
 | 
			
		||||
This tool reads a CubeMX `.ioc` file, constructs pin labels and applies those to matching MCU symbols in the given KiCad schematic file.
 | 
			
		||||
 | 
			
		||||
## Usage
 | 
			
		||||
 | 
			
		||||
```shell
 | 
			
		||||
./cubemx2kicad.js STM32CubeMX_file.ioc KiCad_schematic_file.kicad_sch
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
Just clone the repo, beyond Node.JS no dependencies.
 | 
			
		||||
 | 
			
		||||
## Some points of attention
 | 
			
		||||
 | 
			
		||||
- CubeMX can stay open during this, but the KiCad schematic editor should be closed and re-opened after
 | 
			
		||||
- Use 'user label' in CubeMX for best experience
 | 
			
		||||
- The backup and log functions need to be disbled in the source
 | 
			
		||||
- This messes up / does not work with alternate pin functions in KiCad, beware.
 | 
			
		||||
- The KiCad ERC will complain that the MCU symbol will no longer match the library.
 | 
			
		||||
- The KiCad 'parser' is extremely simplistic and doesn't respect the s-expression scoping. It works for what KiCad saves, but is probably fragile when using other exporters.
 | 
			
		||||
 | 
			
		||||
## Repository & License
 | 
			
		||||
 | 
			
		||||
Written by Stefan Hamminga <stefan@rbts.co>.
 | 
			
		||||
 | 
			
		||||
This tool can be downloaded from
 | 
			
		||||
 | 
			
		||||
https://github.com/rbtsco/cubemx2kicad
 | 
			
		||||
 | 
			
		||||
and freely distributed under the terms of the Apache 2.0 license.
 | 
			
		||||
		Reference in New Issue
	
	Block a user