Giter VIP home page Giter VIP logo

stm32f107-hardware-term's Introduction

2018 Pusan National University Hardware Term Project

Korean(한국어)

기반 장치

사용되는 모듈

  • HC-SR04P Ultrasonic Module(oscillator 없는 장치)
  • FB755AC Bluetooth Module
  • Relay Module
  • Servo Motor
  • Laser Module
  • PIR Sensor
  • Buzzer

프로젝트 구성 절차

  • 요구되는 프로그램: Eclipse DS-5
  • 작업 환경 구축 절차
    1. C 프로젝트를 생성(생성 시 실행 설정을 Bare-metal Executable → ARM Compiler 5)
    2. PSU_DB, Libraries 폴더와 flashclear.axf, stm32-test.sct파일을 한 곳에 모아주도록 합니다.
    3. 프로젝트가 생성되면 Project → Properties에서 아래를 수행해주시길 바랍니다.
      • ARM C Compiler 5 → Code Generation → cortex-m3 기입
      • ARM Assembler 5 → Code Generation → cortex-m3 기입
      • ARM Linker 5 → General → cortex-m3 기입
    4. C/C++ Build Setting에서 ARM Compiler 5의 Includes에 아래의 폴더 리스트를 기입하도록 합니다.
      • CoreSupport
      • DeviceSupport
      • DeviceSupport/Startup
      • STM32F10xStdPeriph_Drice_v3.5/inc
      • STM32F10xStdPeriph_Drice_v3.5/src
    5. ARM Linker 5 → Image Layout에서 stm32-test.sct 파일을 기입해주도록 합니다.
    6. 프로젝트 설정이 끝나면 Windows → Preferences의 DS-5탭 아래 Configuration Database를 클릭해서 PSU_DB를 넣어주도록 합니다.
    7. 위 과정까지 끝나면 Debug → Debug Configuration을 설정하도록 해줍니다.
    8. 연결 목표 설정을 PSU_DB의 Debug Cortex-M3로 지정해주도록 합니다.
    9. USB 포트를 잡아주도록 합니다.
    10. File 탭에 내용이 아무것도 없는 지 확인을 해주도록 합니다.
    11. Debug 탭에 connection only로 하도록 합니다.
    12. 위 과정을 다 거치면 남은 소스 코드를 넣고 빌드를 수행해주도록 합니다.
    13. 빌드가 완료되면 장치를 연결하고, flash load (파일 위치)/*.axf를 명령어 창에 쳐주도록 합니다.
    14. 장치를 껐다가 켜주도록 합니다.

stm32f107-hardware-term's People

Contributors

blackinkgj avatar

Watchers

 avatar  avatar

stm32f107-hardware-term's Issues

초음파 센서 관련 내용

//초음파 센서의 핀번호를 설정한다.
int echoPin = 12;
int trigPin = 13;

void setup() {
	Serial.begin(9600);
	// trig를 출력모드로 설정, echo를 입력모드로 설정
	pinMode(trigPin, OUTPUT);
	pinMode(echoPin, INPUT);
}

void loop() {

	
	// 초음파를 보낸다. 다 보내면 echo가 HIGH 상태로 대기하게 된다.
    digitalWrite(trig, LOW);
    digitalWrite(echo, LOW);
    delayMicroseconds(2);
    digitalWrite(trig, HIGH);
    delayMicroseconds(10);
    digitalWrite(trig, LOW);

	
	// echoPin 이 HIGH를 유지한 시간을 저장 한다.
    unsigned long duration = pulseIn(echoPin, HIGH); 
	// HIGH 였을 때 시간(초음파가 보냈다가 다시 들어온 시간)을 가지고 거리를 계산 한다.
	float distance = ((float)(340 * duration) / 10000) / 2;  
	
	Serial.print(distance);
	Serial.println("cm");
	// 수정한 값을 출력
	delay(500);
}

상기 아두이노 코드를 확인하면 trigger와 echo 사이에 delay가 존재함을 알 수 있습니다. 아마 우리가 0이 나온 것은 그러한 delay가 없어서 echo가 불가능 상황을 만들어서 그런 것이 아닌가도 싶습니다. 또한 duration의 크기는 약 1000 단위인 것 같아 보입니다. 위의 distance 계산은 cm 단위 계산입니다.

서보 모터 동작 관련 개선 사항

지금 현재 사용하는 TIMER를 General Purpose Timer 중 가장 사용을 하지 않을 것이라 사료되는 것으로 수정 부탁드립니다. 그리고 각종 initailization에 연관된 것은 static으로 선언을 해주시고, 각 기능은 .c, .h 파일로 묶어 주셔서 최대한 추상화가 될 수 있도록 제작해주시길 바랍니다.

관련 참고 자료: ultasonic 브랜치의 ultrasonic.c, ultrasonic.h

week11 코드

#include "stm32f10x.h"

#include "core_cm3.h"
#include "misc.h"

#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_usart.h"
#include "stm32f10x_adc.h"
#include "stm32f10x_exti.h"
#include "stm32f10x_tim.h"

#include "lcd.h"
#include "touch.h"

#define true 1
#define false 0

#define START 0
#define STOP 1
#define RESET 2

typedef int bool;

typedef struct _POINT{
	uint16_t x;
	uint16_t y;
}Point;

typedef struct _BUTTON{
	Point pos;
	bool led[4];
}Button;

bool led[4] = {false, false, false, false};

const unsigned int on[] = {
	GPIO_BSRR_BS2,
	GPIO_BSRR_BS3,
	GPIO_BSRR_BS4,
	GPIO_BSRR_BS7
};
const unsigned int off[] = {
	GPIO_BSRR_BR2,
	GPIO_BSRR_BR3,
	GPIO_BSRR_BR4,
	GPIO_BSRR_BR7
};

Point touch, pos;

Button button[4];

int flag = STOP;
int counter = 0;


int color[12] = {
		// come from lcd.h
		WHITE,
		CYAN,
		BLUE,
		RED,
		MAGENTA,
		LGRAY,
		GREEN,
		YELLOW,
		BROWN,
		BRRED,
		GRAY
};

void _RCC_Init(void);
void _GPIO_Init(void);
void _TIM_Init(void);
void _NVIC_Init(void);
void _ADC_Init(void); // DON'T USE THIS TIME
void _ADC_Start(void); // DON'T USE THIS TIME
void SetSysClock(void);
void SysInit(void);


void delay(int num){
	while(num--);
}

void TIM2_IRQHandler(){
	switch(flag){
	case STOP:
		break;
	case RESET:
		counter = 0;
		flag = 1;
		break;
	case START:
	default:
		counter++;
	}
	TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}

int main(){
	SysInit();
	SetSysClock();
	_RCC_Init();
	_GPIO_Init();
	//_ADC_Init();
	//_ADC_Start();
	_NVIC_Init();
	_TIM_Init();

	LCD_Init();
	Touch_Configuration();
	Touch_Adjust();
	LCD_Clear(WHITE);


	{ // EXECUTION
		int buttonSize = 25;
		char inputString[10];
		int i = 0;
		int min = 0, sec = 0, msec = 0;
		Point prevCirclePos = {-1, -1};
		for(i = 0; i < 3; i++){
			button[i].pos.x = 60*i + 50;
			button[i].pos.y = 240;
			LCD_DrawRectangle(60*i+50 - buttonSize, 240 - buttonSize,
							  60*i+50 + buttonSize, 240 + buttonSize);
			switch(i){
			case 0:
				LCD_ShowString(60*i + 50 - buttonSize, 240, "START", BLACK, WHITE);
				break;
			case 1:
				LCD_ShowString(60*i + 50 - buttonSize, 240, "STOP", BLACK, WHITE);
				break;
			case 2:
				flag = RESET;
				LCD_ShowString(60*i + 50 - buttonSize, 240, "RESET", BLACK, WHITE);
				break;
			}

		}
		while(1){
			uint16_t input;

			LCD_ShowString(1, 1, "TEAM09", BLACK, WHITE);

			Touch_GetXY(&touch.x, &touch.y, 0);


			// @TODO: draw rectangle change

			if(T_INT == 0){
				Convert_Pos(touch.x, touch.y, &pos.x, &pos.y);
				sprintf(inputString, "%d\t%d", pos.x, pos.y);
				LCD_ShowString(50, 50, inputString, BLACK, WHITE);

				for(i = 0; i < 3; i++){
					const bool isInX = button[i].pos.x - buttonSize <= pos.x
									&& button[i].pos.x + buttonSize >= pos.x;
					const bool isInY = button[i].pos.y - buttonSize <= pos.y
									&& button[i].pos.y + buttonSize >= pos.y;
					if(isInX && isInY){
						switch(i){
						case 0:
							flag = START;
							break;
						case 1:
							flag = STOP;
							break;
						case 2:
							flag = RESET;
							break;
						}

					} // end of if
				} // end of for
			} // end of if

			min =  ((counter/100)/60)%60;
			sec = (counter/100)%60;
			msec = (counter/10)%10; // ★★★★ this makes 10ms to 100ms ★★★★  
			if(msec%2)
				GPIO_SetBits(GPIOD,GPIO_Pin_2);
			else
				GPIO_ResetBits(GPIOD,GPIO_Pin_2);
			sprintf(inputString,"");
			sprintf(inputString, "%4d%4d%4d", min , sec, msec);
			LCD_ShowString(50, 100, inputString, BLACK, WHITE);
		} // end of while 
	} // end of execution
}

void _RCC_Init(void){
	RCC_APB1PeriphClockCmd((RCC_APB1Periph_TIM2), ENABLE);
	RCC_APB2PeriphClockCmd((RCC_APB2Periph_AFIO
						  | RCC_APB2Periph_GPIOC
						  | RCC_APB2Periph_GPIOD
						  | RCC_APB2Periph_ADC1),ENABLE);
}

void _GPIO_Init(void){
	GPIO_InitTypeDef gpio_init_struct;

	gpio_init_struct.GPIO_Pin = GPIO_Pin_1;
	gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
	gpio_init_struct.GPIO_Mode = GPIO_Mode_AIN;
	GPIO_Init(GPIOC, &gpio_init_struct);

	gpio_init_struct.GPIO_Pin = GPIO_Pin_2 | GPIO_Pin_3 | GPIO_Pin_4 | GPIO_Pin_7;
	gpio_init_struct.GPIO_Speed = GPIO_Speed_50MHz;
	gpio_init_struct.GPIO_Mode = GPIO_Mode_Out_PP;
	GPIO_Init(GPIOD, &gpio_init_struct);

	GPIO_SetBits(GPIOD, GPIO_Pin_2);

}

void _TIM_Init(void){
	TIM_TimeBaseInitTypeDef time_init_struct;
	/// fclk = 72 MHz > STM32F10x's general clock
	time_init_struct.TIM_Prescaler     = 6640 - 1; // input clk speed control
	time_init_struct.TIM_CounterMode   = TIM_CounterMode_Up; // rising edge
	time_init_struct.TIM_Period        = 60 - 1; // period counting
	time_init_struct.TIM_ClockDivision = TIM_CKD_DIV1;
	// 1/f_clk * prescaler * period = 1/72 * 12000 * 60 = 0.01s = 1ms

	TIM_TimeBaseInit(TIM2, &time_init_struct);

	TIM_Cmd(TIM2, ENABLE);
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
}

void _NVIC_Init(void){
	NVIC_InitTypeDef nvic_init_struct;

	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

	nvic_init_struct.NVIC_IRQChannel 				   = TIM2_IRQn;
	// Priority check
	nvic_init_struct.NVIC_IRQChannelPreemptionPriority = 0x01;
	nvic_init_struct.NVIC_IRQChannelSubPriority 	   = 0x01;
	nvic_init_struct.NVIC_IRQChannelCmd				   = ENABLE;
	NVIC_Init(&nvic_init_struct);
}

void _ADC_Init(void){
	/*
	ADC_InitTypeDef adc_init_struct;
	ADC_DeInit(ADC1);

	adc_init_struct.ADC_Mode = ADC_Mode_Independent;
	adc_init_struct.ADC_ScanConvMode = DISABLE;
	adc_init_struct.ADC_ContinuousConvMode = ENABLE;
	adc_init_struct.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
	adc_init_struct.ADC_DataAlign = ADC_DataAlign_Right;
	adc_init_struct.ADC_NbrOfChannel = 1;
	ADC_RegularChannelConfig(ADC1, ADC_Channel_11, 1, ADC_SampleTime_239Cycles5);
	ADC_Init(ADC1, &adc_init_struct);
	ADC_Cmd(ADC1, ENABLE);
	*/
}

void _ADC_Start(void){
	/*
	ADC_ResetCalibration(ADC1);
	while(ADC_GetResetCalibrationStatus(ADC1));
	ADC_StartCalibration(ADC1);
	while(ADC_GetCalibrationStatus(ADC1));
	ADC_SoftwareStartConvCmd(ADC1, ENABLE);
	*/
}

void SysInit(void) {
	/* Set HSION bit */
	/* Internal Clock Enable */
	RCC->CR |= (uint32_t)0x00000001; //HSION

									 /* Reset SW, HPRE, PPRE1, PPRE2, ADCPRE and MCO bits */
	RCC->CFGR &= (uint32_t)0xF0FF0000;

	/* Reset HSEON, CSSON and PLLON bits */
	RCC->CR &= (uint32_t)0xFEF6FFFF;

	/* Reset HSEBYP bit */
	RCC->CR &= (uint32_t)0xFFFBFFFF;

	/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE/OTGFSPRE bits */
	RCC->CFGR &= (uint32_t)0xFF80FFFF;

	/* Reset PLL2ON and PLL3ON bits */
	RCC->CR &= (uint32_t)0xEBFFFFFF;

	/* Disable all interrupts and clear pending bits  */
	RCC->CIR = 0x00FF0000;

	/* Reset CFGR2 register */
	RCC->CFGR2 = 0x00000000;
}


void SetSysClock(void)
{
	volatile uint32_t StartUpCounter = 0, HSEStatus = 0;

	/* SYSCLK, HCLK, PCLK2 and PCLK1 configuration ---------------------------*/
	/* Enable HSE */
	RCC->CR |= ((uint32_t)RCC_CR_HSEON);

	/* Wait till HSE is ready and if Time out is reached exit */
	do
	{
		HSEStatus = RCC->CR & RCC_CR_HSERDY;
		StartUpCounter++;
	} while ((HSEStatus == 0) && (StartUpCounter != HSE_STARTUP_TIMEOUT));

	if ((RCC->CR & RCC_CR_HSERDY) != RESET)
	{
		HSEStatus = (uint32_t)0x01;
	}
	else
	{
		HSEStatus = (uint32_t)0x00;
	}

	if (HSEStatus == (uint32_t)0x01)
	{
		/* Enable Prefetch Buffer */
		FLASH->ACR |= FLASH_ACR_PRFTBE;

		/* Flash 0 wait state */
		FLASH->ACR &= (uint32_t)((uint32_t)~FLASH_ACR_LATENCY);
		FLASH->ACR |= (uint32_t)FLASH_ACR_LATENCY_0;

		/* HCLK = SYSCLK */
		RCC->CFGR |= (uint32_t)RCC_CFGR_HPRE_DIV1;

		/* PCLK2 = HCLK */
		/*@TODO*/
		RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE2_DIV1;

		/* PCLK1 = HCLK */
		RCC->CFGR |= (uint32_t)RCC_CFGR_PPRE1_DIV1;

		/* Configure PLLs ------------------------------------------------------*/
		/* PLL configuration: PLLCLK = ???? */
		/*@TODO*/
		RCC->CFGR &= (uint32_t)~(RCC_CFGR_PLLXTPRE | RCC_CFGR_PLLSRC | RCC_CFGR_PLLMULL);
		RCC->CFGR |= (uint32_t)(RCC_CFGR_PLLXTPRE_PREDIV1 | RCC_CFGR_PLLSRC_PREDIV1 | RCC_CFGR_PLLMULL4); // CFGR_PLLMULL1 SET

		/* PLL2 configuration: PLL2CLK = ???? */
		/* PREDIV1 configuration: PREDIV1CLK = ???? */
		/*@TODO*/
		RCC->CFGR2 &= (uint32_t)~(RCC_CFGR2_PREDIV2 | RCC_CFGR2_PLL2MUL | RCC_CFGR2_PREDIV1 | RCC_CFGR2_PREDIV1SRC);
		RCC->CFGR2 |= (uint32_t)(RCC_CFGR2_PREDIV2_DIV5 | RCC_CFGR2_PLL2MUL10 | RCC_CFGR2_PREDIV1SRC_PLL2 | RCC_CFGR2_PREDIV1_DIV5); //CFGR2 PREDIV1_2,2MUL

		/* Enable PLL2 */
		RCC->CR |= RCC_CR_PLL2ON;
		/* Wait till PLL2 is ready */
		while ((RCC->CR & RCC_CR_PLL2RDY) == 0)
		{
		}

		/* Enable PLL */
		RCC->CR |= RCC_CR_PLLON;

		/* Wait till PLL is ready */
		while ((RCC->CR & RCC_CR_PLLRDY) == 0)
		{
		}

		/* Select PLL as system clock source */
		RCC->CFGR &= (uint32_t)((uint32_t)~(RCC_CFGR_SW));
		RCC->CFGR |= (uint32_t)RCC_CFGR_SW_PLL;

		/* Wait till PLL is used as system clock source */
		while ((RCC->CFGR & (uint32_t)RCC_CFGR_SWS) != (uint32_t)0x08)
		{
		}

		/* Select System Clock as output of MCO */
		//@TODO
		RCC->CFGR |= RCC_CFGR_MCO_SYSCLK;

	}
	else
	{ /* If HSE fails to start-up, the application will have wrong clock
	  configuration. User can add here some code to deal with this error */
	}
}

USART 데이터 출력 규격 관련 내용

USART에서 데이터 출력을 에서 int형에 해당하는 내용은 아래와 같이 작성하면 될 것 같습니다.

#include<stdio.h>
#include<string.h>

void usart_send_int(int value){
    char data[100];
    int idx = 0;
    sprintf(data, "%d", value);
    for(idx = 0; idx < strlen(data); idx++){
        USART_Send(USART1, data[idx]); // This is not exact
        delay(...); // I think this number should be bigger than 1000
    }
   USART_Send(USART1, '\r');
   delay(...); // I think this number should be bigger than 1000
   USART_Send(USART1, '\n');
   delay(...); // I think this number should be bigger than 1000
}

될지 안될지는 잘 모르겠습니다. 혹시 모르니 sprintf를 사용하지 않은 변환기도 준비 부탁드립니다!

초음파 센서 초기화 문제

현재의 테스트 코드에서 ultra_sensor_init()이 사용된 곳을 전혀 찾을 수 없습니다. 이는 정상적인 동작을 보장하지 못합니다.

초음파 코드 확인해주세요

#include "stm32f10x.h"
#include "stm32f1xx_nucleo.h"
 
uint32_t TimingDelay;
 
void init_TIM2(void);
 
int main(void){
    uint16_t distance;
    RCC->APB2ENR = (1<<2);
    GPIOA->CRH = (GPIOA->CRH&0xFFFFFF00)|(3<<0*4)|(4<<1*4); // PA8 Trig, PA9 Echo
 
    init_TIM2();
 
    while(1){
        GPIOA->BSRR = 0x01000100;
        TIM2->CNT=0; while(TIM2->CNT<12);  // 12us delay
        GPIOA->BSRR = 0x01000000;
        while(!(GPIOA->IDR&0x0200));
        TIM2->CNT=0; while(GPIOA->IDR&0x0200);
        distance=(TIM2->CNT+1)/58;  // cm
    }
}
 
void init_TIM2(void){  // Output compare mode, PWM
    RCC->APB1ENR |= (1<<0); // Bit 0 TIM2EN
 
    TIM2->PSC = 72-1;   // 1us
    TIM2->EGR = (1<<0); // Bit 0 UG
    TIM2->CR1 = (1<<0); // Bit 0 CEN
}

//---------------------------------------------------------------------------

int sigcount=0;
int t=0;
int sensor=0;
int state = 0;
 
void InsertSignal() {
	if(t ==0) {
	GPIOD->BSRRL=GPIO_Pin_0;
	t = 1;
	}

	if(t ==1) {
		GPIOD->BSRRH=GPIO_Pin_0;
		int readGPIO = GPIO_ReadInputDataBit(GPIOD,GPIO_Pin_1);

		if(readGPIO != 0){
			sigcount++;
			state=1;
			}

		else if(readGPIO == 0 && state == 1) {
			sensor = sigcount;
			state=0;
			sigcount=0;
			}
	}
}

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
		InsertSignal();
	}
}
 
void TIM3_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM3, TIM_IT_Update) == SET) {
		TIM_ClearITPendingBit(TIM3, TIM_IT_Update);
		if(state == 0 && sigcount==0) t = 0;
		if(state2 == 0 && sigcount2==0) t2 = 0;
		if(state3 == 0 && sigcount3==0) t3 = 0;
	}
}

//--------------------------------------------------------------

GPIO_EXTILineConfig(GPIO_PortSourceGPIO0,GPIO_PinSource0);
exti.EXTI_Line = EXTI_Line0;
exti.EXTI_LineCmd = ENABLE;
exti.EXTI_Mode = EXTI_Mode_Interrupt;
exti.EXTI_Trigger = EXTI_Trigger_Rising_Falling;
EXTI_Init(&exti);

nvic_struct2.NVIC_IRQChannel = TIM4_IRQn;
nvic_struct2.NVIC_IRQChannelPreemptionPriority = 0;
nvic_struct2.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&nvic_struct2);

void TIM4_IRQHandler(void){
    if(TIM_GetITStatus(TIM4,TIM_IT_Update)!=RESET){
        sonar_start();
        FLAG_ECHO = 1;
        TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
    }
}

void EXTI0_IRQHandler(void){
    if(EXTI_GetITStatus(EXTI_Line0)!=RESET){
        if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)!=0){
            TIM_SetCounter(TIM3,0);
        }
        if(GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_0)==0){
            SonarValue[0] = TIM_GetCounter(TIM3);
        }
        EXTI_ClearITPendingBit(EXTI_Line0);
    }
}

void sonar_start(){
    int i;

    GPIO_SetBits(GPIOB,GPIO_Pin_15);
    GPIO_SetBits(GPIOB,GPIO_Pin_14);

    for(i = 0; i<0x7200; i++)
    ;
    GPIO_ResetBits(GPIOB,GPIO_Pin_15);
    GPIO_ResetBits(GPIOB,GPIO_Pin_14);
    
}

unsigned int sonar_get(int index){
    unsigned long Sonar;
    Sonar = (354/2) * (unsigned long) SonarValue[index] / (72000/72);
    if(Sonar > 4000)
        Sonar = 4000;
    if(Sonar <20)
        Sonar=20;

    return (unsigned int) Sonar;
}

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.