Git Product home page Git Product logo

Comments (13)

hauserkristof avatar hauserkristof commented on June 27, 2024 1

Also @RobMeades as far I know you are in partnership with Mikroe, they have a click shield for the Nucleo 144 it maybe makes easier testing your own modules :)

from ubxlib.

hauserkristof avatar hauserkristof commented on June 27, 2024

Also, if you could supply a sample project for STM32F4 native, with the correct directory / CMake / include tree that would be neat also.

Thank you very much!

from ubxlib.

RobMeades avatar RobMeades commented on June 27, 2024

Hi there, and sorry your having trouble with this. We've never used an STM32F7 ourselves, only STM32F4, though they appear to be pretty similar and Zephyr should support either of course.

If I've understood correctly, and looking at your main.c, you get a hard fault even without ubxlib in the build? If that is the case, and maybe even if it is not, Zephyr-platform-related matters are best addressed to the STM Zephyr people: they hang out in the STM32 section of the Zephyr Discord; Discord is undoubtedly the best way to get instant help on matters Zephyr, it is a very active place.

EDIT: re-reading what you posted, maybe you mean that your main.c runs fine but when you add CONFIG_UBXLIB=y, even though you include no ubxlib code, your main.c runs into a hard fault. If that is the case, it can only mean that one of the items added into prj.conf for ubxlib is upsetting something. If you are unable to run under a debugger to determine what the execution path to the hard fault is then you might try removing the added things, one by one, from prj.conf to see if you can find out which one is upsetting the target. That said, none of them are particularly controversial settings.

Concerning STM32F4-on-Zephyr, the directory layout etc. should be no different to that for any other Zephyr project, just follow their usual pattern and bring ubxlib in as described here as a Zephyr module. We don't actually use Zephyr much ourselves, aside from running our automated tests on it, so we aren't really experts on what the layout should be for development, we are "just a module" to Zephyr, so to speak.

The only issue I'm aware of with using ST devices in Zephyr with ubxlib is that, in the device tree, rather than having all UARTs named uart0, uart1, etc., some STM32F4 UARTs end up being named usartx (i.e. with an s in the name); if you happen to want to use a UART that is named usartx then you will need to include an alias in your .overlay file so that ubxlib can find it (see here for how to do that). But this has nothing to do with your hard fault.

We do have it on our TODO list to run Zephyr on non-Nordic chips so I will see if I can get hold of an STM32F7 board but I can't promise how quickly we will get around to doing this I'm afraid: at a minimum it will be weeks rather than days.

from ubxlib.

cturvey avatar cturvey commented on June 27, 2024

The Nucleo 144-pin boards might be the most flexible choice. Cover the F4, F7, H7, U5, L5, etc
GPS/GNSS/MODEM shields via Arduino connection. ST-LINK VCP USART independent or the Arduino D0/D1 one.
Would suggest NUCLEO-F767ZI

from ubxlib.

RobMeades avatar RobMeades commented on June 27, 2024

@cturvey: excellent, thanks for the tip.

from ubxlib.

RobMeades avatar RobMeades commented on June 27, 2024

Yes, Mikroe and Sparkfun mainly.

from ubxlib.

RobMeades avatar RobMeades commented on June 27, 2024

Not sure you're going to find this very helpful but...

  • I now have a Nucleo-F767ZI board.
  • I have built the default ubxlib Zephyr runner build for it (the build that just runs all our tests), using the Zephyr 3.4.99 version that comes with nrfConnect 2.5.0, with the addition of:
    • cloning and adding hal_stm32 as a ZEPHYR_EXTRA_MODULE,
    • adding a nucleo_f767zi.overlay file to provide:
      / {
              chosen {
                      zephyr,bt_uart = &usart6;
          };
      };
      
      ...since the build wouldn't complete without a choice for zephyr_bt_uart (this is not a ubxlib thing, it must be something in the Zephyr/ST code when BLE is enabled),
    • attached find the build output, for information.
  • I downloaded the .elf file to the board with the STLink utility.
  • I hung a terminal off the serial port that the board maps itself as, @ 115200, but couldn't see anything; this is to be expected since our Zephyr porting layer will be sending debug output to a Nordic RTT port which doesn't exist on ST.
  • So I hooked up OpenOCD by:
    • editing the stm32cube_ide_openocd_swo.cfg we use for STM32F4 to remove the STM32F4/tpiu specifics and point it at STM32F7 instead (this .cfg file copied to the OpenOCD scripts sub-directory),
    • running c:\openocd-v0.11.0-3\scripts>..\bin\openocd.exe --file stm32cube_ide_openocd_swo.cfg at a command prompt, which got me an OpenOCD GDB server:
    xPack OpenOCD x86_64 Open On-Chip Debugger 0.11.0+dev (2021-12-07-17:33)
    Licensed under GNU GPL v2
    For bug reports, read
          http://openocd.org/doc/doxygen/bugs.html
    Info : auto-selecting first available session transport "hla_swd". To override use 'transport select <transport>'.
    Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
    Info : DEPRECATED target event trace-config; use TPIU events {pre,post}-{enable,disable}
    Info : clock speed 2000 kHz
    Info : STLINK V2J39M27 (API v2) VID:PID 0483:374B
    Info : Target voltage: 3.229921
    Info : stm32f7x.cpu: Cortex-M7 r1p0 processor detected
    Info : stm32f7x.cpu: target has 8 breakpoints, 4 watchpoints
    Info : starting gdb server for stm32f7x.cpu on 3333
    Info : Listening on port 3333 for gdb connections
    Info : Unable to match requested speed 2000 kHz, using 1800 kHz
    Info : Unable to match requested speed 2000 kHz, using 1800 kHz
    target halted due to debug-request, current mode: Thread
    xPSR: 0x01000000 pc: 0x08042d18 msp: 0x20048000
    Info : Unable to match requested speed 8000 kHz, using 4000 kHz
    Info : Unable to match requested speed 8000 kHz, using 4000 kHz
    Info : Listening on port 6666 for tcl connections
    Info : Listening on port 4444 for telnet connections
    Info : accepting 'gdb' connection on tcp/3333
    target halted due to debug-request, current mode: Thread
    xPSR: 0x41000000 pc: 0x0804b130 psp: 0x20024dd8
    Info : device id = 0x10016451
    Info : flash size = 2048 kbytes
    Info : Single Bank 2048 kiB STM32F76x/77x found
    Info : flash size = 1024 bytes
    Info : Unable to match requested speed 2000 kHz, using 1800 kHz
    target halted due to debug-request, current mode: Thread
    xPSR: 0x01000000 pc: 0x08042d18 msp: 0x20048000
    Info : Unable to match requested speed 8000 kHz, using 4000 kHz
    Info : Padding image section 0 at 0x080001ac with 4 bytes
    Info : Unable to match requested speed 2000 kHz, using 1800 kHz
    target halted due to debug-request, current mode: Thread
    xPSR: 0x01000000 pc: 0x080762c0 msp: 0x20020000
    semihosting is enabled
    
    • leaving this running, I started-up the STM32Cube IDE and configured it to run GDB HW debugging as described here and pointed it at the zephyr.elf that was just built, for source-level debugging,
    • started stepping through the code in the STM32Cube IDE.

With all of this I can see the target running through the porting tests happily enough:

image

No crashes yet, and we're certainly into "real" code, calling Zephyr RTOS functions, etc. I will see if I can hack the debug output to send to the UART instead.

from ubxlib.

RobMeades avatar RobMeades commented on June 27, 2024

Ah, yes, I edited the prj.conf file for our Zephyr runner build to comment out these lines:

#CONFIG_USE_SEGGER_RTT=y
#CONFIG_RTT_CONSOLE=y

...and set CONFIG_UART_CONSOLE=y (edited version of the prj.conf file attached), built/downloaded to the board again and the debug logging immediately came streaming out of the serial port that the board maps itself as (@ 115200). The full log output is attached: you'll see plenty of failures since I have no cellular/GNSS/whatever HW attached to the STM32F7 board but the main thing is that all of the port tests pass, which means that all of the Zephyr/RTOS stuff is working as expected (these pasted in below).

I suppose the good news is that there's nothing fundamental wrong but this doesn't help you find out what is upsetting things for your case. In case it is useful, find attached a .zip file containing the built zephyr.hex and zephyr.elf files.

U_APP: Running portInitialisation...
U_PORT_TEST: main task stack had a minimum of 7524 byte(s) free (minimum is 5120).
U_PORT_TEST: resources are good (1 outstanding OS resource(s), as expected).
C:/projects/ubxlib_priv/port/test/u_port_test.c:1283:portInitialisation:PASS

U_APP: Running portRentrancy...
U_PORT_TEST_OS_REENT_TASK_1: 1 "4294967295 1 c".
U_PORT_TEST_OS_REENT_TASK_1: please wait while pUPortMalloc() is thrashed...
U_PORT_TEST_OS_REENT_TASK_2: 2 "4294967295 2 c".
U_PORT_TEST_OS_REENT_TASK_2: please wait while pUPortMalloc() is thrashed...
U_PORT_TEST_OS_REENT_TASK_3: 3 "4294967295 3 c".
U_PORT_TEST_OS_REENT_TASK_3: please wait while pUPortMalloc() is thrashed...
U_PORT_TEST_OS_REENT_TASK: instance 1 done, returning 0.
U_PORT_TEST_OS_REENT_TASK: instance 2 done, returning 0.
U_PORT_TEST_OS_REENT_TASK: instance 3 done, returning 0.
U_PORT_TEST: test task 1 had 1696 byte(s) free out of 2048.
U_PORT_TEST: test task 2 had 1696 byte(s) free out of 2048.
U_PORT_TEST: test task 3 had 1696 byte(s) free out of 2048.
U_PORT_TEST: reentrancy task(s) returned 0.
U_PORT_TEST: main task stack had a minimum of 7524 byte(s) free (minimum is 5120).
U_PORT_TEST: resources are good (1 outstanding OS resource(s), as expected).
U_PORT_TEST: we have leaked 0 resources(s).
C:/projects/ubxlib_priv/port/test/u_port_test.c:1311:portRentrancy:PASS

U_APP: Running portOs...
U_PORT_TEST: tick time now is 3612.
U_PORT_TEST: creating a mutex...
             returned error code 0, handle 0x20026930.
U_PORT_TEST: creating a data queue...
             returned error code 0, handle 0x20026950.
U_PORT_TEST: 20 entries free on data queue.
U_PORT_TEST: creating a control queue...
             returned error code 0, handle 0x200269e8.
U_PORT_TEST: 20 entries free on control queue.
U_PORT_TEST: locking mutex, preventing task from executing.
U_PORT_TEST: creating a test task with stack 2048 byte(s) and priority 5, passing it the pointer 0x20022c30 containing the string ""...
             returned erU_PORT_TEST_OS_TASK: task with handle 0x20026a80 started, received parameter pointer 0x20022c30 containing string "Boo!".
U_PORT_TEST_OS_TASK: uPortTaskGetHandle() returned 0x20026a80
U_PORT_TEST_OS_TASK: task trying to lock the mutex.
ror code 0, handle 0x20026a80.
U_PORT_TEST: time now 3690 ms.
U_PORT_TEST: unlocking mutex, allowing task to execute.
U_PORT_TEST_OS_TASK: task trying to lock the mutex again, should fail!.
U_PORT_TEST_OS_TASK: unlocking it again.
U_PORT_TEST_OS_TASK: locking it again (non-try version).
U_PORT_TEST_OS_TASK: task waiting on queue for data.
U_PORT_TEST: trying to lock the mutex, should fail...
U_PORT_TEST: sending stuff to task...
U_PORT_TEST_OS_TASK: task received 0.
                     item 1, expecting 0.
U_PORT_TEST_OS_TASK: queueItem 0.
U_PORT_TEST_OS_TASK: task received 100.
                     item 2, expecting 100.
U_PORT_TEST_OS_TASK: queueItem 100.
U_PORT_TEST_OS_TASK: task received 25.
                     item 3, expecting 25.
U_PORT_TEST_OS_TASK: queueItem 25.
U_PORT_TEST: test task had 1752 byte(s) free out of 2048.
U_PORT_TEST: sending -1 to terminate test task control queue and waiting for it to stop...
U_PORT_TEST_OS_TASK: task received 3.
                     item 4, expecting 3.
U_PORT_TEST_OS_TASK: task received -1 on control queue.
U_PORT_TEST_OS_TASK: queueItem -1.
U_PORT_TEST_OS_TASK: task exiting, unlocking mutex.
U_PORT_TEST_OS_TASK: task deleting itself.
U_PORT_TEST: task stopped.
U_PORT_TEST: deleting mutex...
U_PORT_TEST: deleting queues...
U_PORT_TEST: peeking queue returned 0.
U_PORT_TEST: found 255 on queue.
U_PORT_TEST: according to uPortGetTickTimeMs() the test took 3584 ms.
U_PORT_TEST: main task stack had a minimum of 7524 byte(s) free (minimum is 5120).
U_PORT_TEST: resources are good (1 outstanding OS resource(s), as expected).
U_PORT_TEST: we have leaked 0 resources(s).
C:/projects/ubxlib_priv/port/test/u_port_test.c:1438:portOs:PASS

U_APP: Running portOsSemaphore...
U_PORT_TEST: tick time now is 7243.
U_PORT_TEST: initialize a semaphore with invalid max limit.
U_PORT_TEST: initialize a semaphore with invalid count.
U_PORT_TEST: verify that the semaphore waits and timeouts with TryTake.
U_PORT_TEST: diffMs 500.
U_PORT_TEST: verify that the semaphore waits with Take and is taken.
             by this thread after given by second thread.
U_PORT_TEST: verify that the semaphore waits with TryTake and is taken.
             by this thread after given by second thread.
U_PORT_TEST: verify that +2 as initialCount works for TryTake.
U_PORT_TEST: verify that +2 as limit works for TryTake.
U_PORT_TEST: verify that the semaphore waits with Take and is taken.
             by this thread after given from ISR.
U_PORT_TEST: according to uPortGetTickTimeMs() the test took 2565 ms.
U_PORT_TEST: main task stack had a minimum of 7524 byte(s) free (minimum is 5120).
U_PORT_TEST: resources are good (1 outstanding OS resource(s), as expected).
U_PORT_TEST: we have leaked 0 resources(s).
C:/projects/ubxlib_priv/port/test/u_port_test.c:1638:portOsSemaphore:PASS

U_APP: Running portEventQueue...
U_PORT_TEST: opening two event queues...
U_PORT_TEST: 20 entries free on "event queue max".
U_PORT_TEST: 20 entries free on "event queue min".
U_PORT_TEST: sending to the two event queues 101 time(s)...
U_PORT_TEST: event queue min received 101 message(s).
U_PORT_TEST: event queue max received 101 message(s).
U_PORT_TEST: event queue min task had 816 byte(s) free out of 1156.
U_PORT_TEST: event queue max task had 816 byte(s) free out of 1156.
U_PORT_TEST: closing the event queues...
U_PORT_TEST: main task stack had a minimum of 7524 byte(s) free (minimum is 5120).
U_PORT_TEST: resources are good (1 outstanding OS resource(s), as expected).
U_PORT_TEST: we have leaked 0 resources(s).
C:/projects/ubxlib_priv/port/test/u_port_test.c:1944:portEventQueue:PASS

U_APP: Running portHeap...
U_PORT_TEST: testing heap allocation.
U_PORT_TEST: removing assert hook.
U_PORT_TEST: main task stack had a minimum of 7524 byte(s) free (minimum is 5120).
U_PORT_TEST: resources are good (1 outstanding OS resource(s), as expected).
C:/projects/ubxlib_priv/port/test/u_port_test.c:2139:portHeap:PASS

U_APP: Running portStrtok_r...
U_PORT_TEST: testing strtok_r()...
U_PORT_TEST: main task stack had a minimum of 7524 byte(s) free (minimum is 5120).
U_PORT_TEST: resources are good (1 outstanding OS resource(s), as expected).
U_PORT_TEST: we have leaked 0 resources(s).
C:/projects/ubxlib_priv/port/test/u_port_test.c:2217:portStrtok_r:PASS

U_APP: Running portMktime64...
U_PORT_TEST: testing mktime64()...
U_PORT_TEST: main task stack had a minimum of 7524 byte(s) free (minimum is 5120).
U_PORT_TEST: resources are good (1 outstanding OS resource(s), as expected).
U_PORT_TEST: we have leaked 0 resources(s).
C:/projects/ubxlib_priv/port/test/u_port_test.c:2279:portMktime64:PASS

U_APP: Running portGmtime_r...
U_PORT_TEST: testing gmtime_r()...
U_PORT_TEST: main task stack had a minimum of 7524 byte(s) free (minimum is 5120).
U_PORT_TEST: resources are good (1 outstanding OS resource(s), as expected).
U_PORT_TEST: we have leaked 0 resources(s).
C:/projects/ubxlib_priv/port/test/u_port_test.c:2309:portGmtime_r:PASS

U_APP: Running portGetTimezoneOffsetSeconds...
U_PORT_TEST: testing uPortGetTimezoneOffsetSeconds()...
U_PORT_TEST: UTC time 0 and, given that, mktime() returns 0, therefore timezone offset 0 second(s), uPortGetTimezoneOffsetSeconds() 0 second(s).
U_PORT_TEST: main task stack had a minimum of 7524 byte(s) free (minimum is 5120).
U_PORT_TEST: resources are good (1 outstanding OS resource(s), as expected).
U_PORT_TEST: we have leaked 0 resources(s).
C:/projects/ubxlib_priv/port/test/u_port_test.c:2351:portGetTimezoneOffsetSeconds:PASS

U_APP: Running portTimers...
U_PORT_TEST: testing timers...
U_PORT_TEST: at the end of the timer test:
U_PORT_TEST: timer 1 expired 0 time(s).
U_PORT_TEST: timer 2 expired 0 time(s).
U_PORT_TEST: timer 3 expired 2 time(s).
U_PORT_TEST: timer 4 expired 4 time(s).
U_PORT_TEST: main task stack had a minimum of 7524 byte(s) free (minimum is 5120).
U_PORT_TEST: resources are good (1 outstanding OS resource(s), as expected).
U_PORT_TEST: we have leaked 0 resources(s).
C:/projects/ubxlib_priv/port/test/u_port_test.c:3036:portTimers:PASS

U_APP: Running portCriticalSection...
U_PORT_TEST: testing critical sections, may take up to 10 second(s)...
U_PORT_TEST: error flag is 0x00000000.
U_PORT_TEST: main task stack had a minimum of 7524 byte(s) free (minimum is 5120).
U_PORT_TEST: resources are good (1 outstanding OS resource(s), as expected).
U_PORT_TEST: we have leaked 0 resources(s).
C:/projects/ubxlib_priv/port/test/u_port_test.c:3166:portCriticalSection:PASS

.

from ubxlib.

hauserkristof avatar hauserkristof commented on June 27, 2024

Hey @RobMeades !

Thanks for the really fast responses!

Can you please share the full project with me maybe? I would like to see the full project's structure.

from ubxlib.

RobMeades avatar RobMeades commented on June 27, 2024

There isn't a "proper" project really, I just modified our runner build (see here https://github.com/u-blox/ubxlib/tree/master/port/platform/zephyr/runner), by adding a clone of hal_stm32 somewhere on my hard disk, appended that location to the CMake variable ZEPHYR_EXTRA_MODULE in my CMakeLists.txt file, and added the nucleo_f767zi.overlay file in the boards directory 'cos we switch on BLE (you might not) and that caused the ST HAL code to want a choice for zephyr_bt_uart, it wouldn't build otherwise.

That built to completion and ran our tests as intended, it just needed the few modifications in the prj.conf file to send the log output to the right place.

But this isn't really an example of how a "normal" Zephyr project would be organised, if there is one: as indicated, we (I, at least) don't use Zephyr in our usual development environment so we don't know what a "normal" Zephyr project would look like. Putting it another way: don't take our advice! We are merely a submodule, in Zephyr terms, an add-on to a "normal" project. Surely the Zephyr documentation indicates what a good structure might be?

from ubxlib.

hauserkristof avatar hauserkristof commented on June 27, 2024

Hey @RobMeades !

Thanks for the fast response times!

I've tried the zephyr recommended way, without success. I'm know thinking the native STM32 way, but with the default .ioc project way (because I have an existing project in that structure).

I was looking at #3 but that does not have details how can I add it to an existing project through eclipse.

Do you have any experience with this?

Also, is Unity mandatory for just including the lib?

Thanks

from ubxlib.

RobMeades avatar RobMeades commented on June 27, 2024

Sorry you didn't get anywhere with Zephyr. Issue #3 is very old now, from the days when we used Eclipse-style projects with STM32F4; we moved away from trying to maintain the .project and .cproject files involved: there is a large amount of duplication inside the files themselves and they had a tendency to lose all your changes when opening Eclipse if you got one character wrong.

What we do instead is provide a makefile project that can be imported into Eclipse. Unity is not required for the operation of ubxlib but our runner builds build and run all of our tests, and therefore they do require Unity, since we use Unity for testing.

The README.md in the runner directory describes how to pass conditional compilation flags into the runner build (if you need them) and where to obtain Unity, while the README.md in the STM32F4 platform directory describes how to set up the STM32Cube IDE. Once you have a runner build working (and to do so would require Unity) you could replace the contents of our u_main.c with your own, which will remove any linkage with Unity. Of course you will need to adjust the main task stack and heap sizes etc. to your applications' needs as you do this.

EDIT: on a more general note, if I remember correctly, the main issue with all of these IDEs, Eclipse included, is that they have difficulty with the concept of "out of tree" builds, by which I mean that the project files and metadata etc. are in one place and the source files etc. are NOT BENEATH the project/files/metadata in the same directory tree. In other words, they like this:

--> project 
    config 
    metadata
    etc.
    src ------> .c files directory tree starts here
    include --> .h files here

...but they do not like this:

--> project 
    config 
    metadata
    etc.
--> src ------> .c files directory tree starts here
--> include --> .h files here

...or this:

-->-->--> project 
          config 
          metadata
          etc.
--> src ------> .c files directory tree starts here
--> include --> .h files here

Personally I find this an utter pain: the location of the source files should have absolutely nothing to do with the location of the metadata, and this is particularly true where you have third-party code (which ubxlib will be) that you want to keep isolated from your own code. It actively prevents re-use: I have this great thing over here, I want to re-use it in a new project over there, growing my world, but I can't without physically copying the files, so now I have to maintain two copies.

This is why people use CMake/make natively rather than Eclipse: Eclipse puts unnecessary constraints on how you organise your code, while with a CMakeLists.txt file or a Makefile you can do exactly as you'd like. Eclipse can be made to do the "out of tree" stuff, it is just not normal and tends to require you to use literal paths (i.e. /home/rob/thingy or C:\Users\rob\thingy), which is a pain.

Anyway, those are just my complaints, my approach would be to:

  • organise things (either "in tree" or "out of tree") so that you have a distinct/unique location for your third party code, which will have its own license terms,
  • make sure that your own public .h files (either APIs (if you have any) or files containing values intended to be configurable) are clearly separated from (a) your own .c files and (b) your own internal .h files, and,
  • keep tests within the same directory structure (so that it is difficult for them to get out of step with the code they are testing) but, at the same time, make sure that they are not mixed up with your core code,

All my opinions of course.

from ubxlib.

RobMeades avatar RobMeades commented on June 27, 2024

In commit b627d1ff8686631f5d6c64a81fa06ec4f0c9735 we have added to the ubxlib test farm testing of a Nucleo F767ZI board with native Zephyr (i.e. not through the nRFConnect SDK but direct from https://github.com/zephyrproject-rtos/zephyr).

With that I'm going to close this issue: feel free to open re-open it, or open a new issue, if there is more to discuss.

from ubxlib.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.