WIP: add hardware support #42

Draft
s452634 wants to merge 16 commits from hardware into main
51 changed files with 5818 additions and 25 deletions

View File

@ -7,6 +7,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased] ## [Unreleased]
### Added
- Serial communication with dedicated device including
- knob and button updates
- support for CV as MIDI output inside Musique
## [0.4.0] ## [0.4.0]
### Added ### Added

View File

@ -18,11 +18,6 @@ include scripts/test.mk
bin/$(Target): bin/$(os)/$(Target) bin/$(Target): bin/$(os)/$(Target)
ln -f $< $@ ln -f $< $@
# http://www.music.mcgill.ca/~gary/rtmidi/#compiling
bin/$(os)/rtmidi.o: lib/rtmidi/RtMidi.cpp lib/rtmidi/RtMidi.h
@echo "CXX $@"
@$(CXX) $< -c -O2 -o $@ $(CPPFLAGS) -std=c++20
doc: Doxyfile musique/*.cc musique/*.hh doc: Doxyfile musique/*.cc musique/*.hh
doxygen doxygen
@ -57,3 +52,4 @@ musique.zip:
$(shell mkdir -p $(subst musique/,bin/$(os)/,$(shell find musique/* -type d))) $(shell mkdir -p $(subst musique/,bin/$(os)/,$(shell find musique/* -type d)))
$(shell mkdir -p $(subst musique/,bin/$(os)/debug/,$(shell find musique/* -type d))) $(shell mkdir -p $(subst musique/,bin/$(os)/debug/,$(shell find musique/* -type d)))
$(shell mkdir -p bin/$(os)/serial)

View File

@ -31,6 +31,7 @@ Copy [editor/vscode-musique](editor/vscode-musique) directory to `<user home>/.v
- Creator of [tl::expected](https://github.com/TartanLlama/expected) - [Sy Brand](https://sybrand.ink/) - Creator of [tl::expected](https://github.com/TartanLlama/expected) - [Sy Brand](https://sybrand.ink/)
- Creator of [bestline](https://github.com/jart/bestline) - [Justine Tunney](https://justinetunney.com/) - Creator of [bestline](https://github.com/jart/bestline) - [Justine Tunney](https://justinetunney.com/)
- Creator of [rtmidi](https://github.com/thestk/rtmidi/) - [Gary P. Scavone](http://www.music.mcgill.ca/~gary/) - Creator of [rtmidi](https://github.com/thestk/rtmidi/) - [Gary P. Scavone](http://www.music.mcgill.ca/~gary/)
- Creators of [serial](https://github.com/wjwwood/serial) - [William Woodall](https://github.com/wjwwood/) and [John Harrison](https://github.com/ashgti)
- Creators of [link](https://github.com/Ableton/link) - Creators of [link](https://github.com/Ableton/link)
and all contributors that created libraries above. and all contributors that created libraries above.

View File

@ -13,7 +13,7 @@ VERSION := $(MAJOR).$(MINOR).$(PATCH)-dev+$(COMMIT)
CXXFLAGS:=$(CXXFLAGS) -std=c++20 -Wall -Wextra -Werror=switch -Werror=return-type -Werror=unused-result CXXFLAGS:=$(CXXFLAGS) -std=c++20 -Wall -Wextra -Werror=switch -Werror=return-type -Werror=unused-result
CPPFLAGS:=$(CPPFLAGS) -DMusique_Version='"$(VERSION)"' \ CPPFLAGS:=$(CPPFLAGS) -DMusique_Version='"$(VERSION)"' \
-Ilib/expected/ -I. -Ilib/bestline/ -Ilib/rtmidi/ -Ilib/link/include -Ilib/asio/include/ -Ilib/expected/ -I. -Ilib/bestline/ -Ilib/rtmidi/ -Ilib/link/include -Ilib/asio/include/ -Ilib/serial/include/
LDFLAGS=-flto LDFLAGS=-flto
LDLIBS= -lpthread LDLIBS= -lpthread

0
hardware/.gitkeep Normal file
View File

BIN
hardware/MusiqueBox.fzz Normal file

Binary file not shown.

BIN
hardware/MusiqueBox_bb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

155
hardware/README.md Normal file
View File

@ -0,0 +1,155 @@
# Instrukcja konstrukcji urządzenia MusiqueBox
MusiqueBox jest urządzeniem towarzyszącym dla języka Musique. Może zapewnić on kontrolki do użycia w języku - przykładowo pokrętła, których wartość można odczytać w języku, a ich stany użyć w kodzie. Urządzenie to może również pozwolić na sterowanie instrumentami obsługującymi Control Voltage przez język.
MusiqueBox jest otwarty jak reszta projektu Musique - posiadając umiejętności programistyczne można go modyfikować dla własnych potrzeb. W tym poradniku opisana zostanie konstrukcja urządzenia posiadającego dwa pokrętła oraz trzy przyciski przekazujące swoje stany do interpretera Musique, oraz posiadającego 2 wyjścia analogowe i jedno 2-stanowe cyfrowe do obsługi urządzenia sterowanego przez CV - jednak zależnie od potrzeb użytkownika, część z tych rzeczy można pominąć a urządzenie wciąż będzie działać.
Do zbudowania przynajmniej podstawowej wersji urządzenia potrzeba podstawowej umiejętności łączenia komponentów elektronicznych na płytce stykowej, oraz rozpoznawania wyprowadzeń w podstawowych komponentach elektronicznych i układach scalonych. Do budowy układu do obsługi urządzeń sterowanych napięciem zalecane jest jednak posiadanie większego doświadczenia z elektroniką.
## Wymagane komponenty
Urządzenie MusiqueBox bazuje na chwilę obecną na płytce rozwojowej STM32 NUCLEO L476-RG. Płytka ta jest programowalna z poziomu komputera osobistego przez złącze USB, oraz posiada wygodnie wyprowadzone piny do podłączenia komponentów. Bazowe elementy potrzebne do zbudowania urządzenia to:
- płytka rozwojowa STM32 NUCLEO L476-RG
- przewód USB/Mini USB pozwalający podłączyć urządzenie do komputera z intepreterem
- płytka stykowa oraz przewody połączeniowe do podłączenia komponentów składających się na urządzenie
Kontrolki do sterowania wartościami w Musique mogą być dobrane przez użytkownika. Na rzecz tego poradnika przedstawimy sposób konstrukcji urządzenia z trzema przyciskami i dwoma pokrętłami, ale możliwe jest podłączenie innych rodzajów podstawowych kontrolek. Przykładowo można zastąpić pokrętła suwakami - obie z tych rzeczy zwykle działają w oparciu o potencjometr, więc je można podłączyć do urządzenia analogicznie do siebie. Do zbudowania kontrolek dla tego projektu należy zaopatrzyć się w:
- 3x mikroprzełącznik z pinami do montażu na płytce stykowej
- 2x potencjometr o maksymalnym oporze 100kΩ
- 2x rezystor o oporze 220kΩ
Aby urządzenie rozszerzyć o układ sterujący instrumentem sterowanym CV, potrzebne będą również:
- wzmacniacz operacyjny TL072CP (lub podobny) w obudowie DIP-8
- 2x potencjometr montażowy o maksymalnym oporze 10kΩ
- 2x rezystor o oporze 10kΩ
- złącza i przewody odpowiednie do wyprowadzenia napięć sterujących do instrumentu, oraz podłączenia zasilania instrumentu do MusiqueBoxa
- dla syntezatorów modularnych typu Eurorack (oraz wielu innych instrumentów sterowanych CV) będą to złącza Minijack TS, aczkolwiek w tym przykładzie złącza będą przedstawione jako rzędy pinów - 3 dla zasilania, oraz 3 dla napięć sterujących
- potrzebna jest możliwość wyprowadzenia napięć dodatniego i ujemnego, ale również uziemienia z intrumentu muzycznego
## Konstrukcja urządzenia
MusiqueBox bazuje na chwilę obecną na płytce rozwojowej STM32 NUCLEO L476-RG. Stanowi ona centralny element urządzenia; mikrokontroler na płytce jest łatwo programowalny przez zapewniane przez producenta programy komputerowe. W celu konstrukcji MusiqueBoxa należy tę płytkę zaprogramować, a następnie podłączyć odpowiednie układy do portów na płytce.
### Programowanie płytki NUCLEO
W pierwszej kolejności do działania MusiqueBoxa należy zaprogramować płytkę NUCLEO na której urządzenie bazuje...
[TODO]
### Podłączenie układów do płytki
![](./MusiqueBox_bb.png)
Powyższy rysunek ilustruje sposób konstrukcji odpowiednich układów na płytce stykowej i podłączenia ich do płytki rozwojowej NUCLEO. W repozytorium zamieszczony jest również schemat elektroniczny urządzenia.
W ramach tego poradnika konstrukcja przedstawiona będzie głównie w postaci jak rysunek powyżej.
Duża część z wyprowadzeń na płytce NUCLEO ma więcej niż jedno oznaczenie - jeden rodzaj oznaczeń odpowiada oznaczeniom portów na mikrokontrolerze STM32L476RG, a drugi odpowiada oznaczeniom obecnym na płytkach rozwojowych Arduino UNO. Na zawartych w tym poradniku infografikach zastosowane są oznaczenia odpowiadające Arduino UNO i opisy słowne również odnoszą się do tej wersji oznaczeń.
### Układ wejścia
W chwili obecnej program MusiqueBoxa zapewnia wsparcie dla maksymalnie trzech przycisków, oraz maksymalnie dwóch pokręteł analogowych. Możliwe jest podłączenie mniejszej ilości kontrolek.
Każdy z przycisków należy podłączyć przez połączenie jednego z wyprowadzeń z masą (wyprowadzenie GND na płytce), a drugiego - do odpowiedniego wyprowadzenia cyfrowego. Do podłączenia przycisków na płytce dostępne są wyprowadzenia D7, D8 oraz D11.
<figure>
<img
src="./examples/MusiqueBox-examples_bb-1.png"
alt="Krok 1">
<figcaption>Krok 1. <br>Podłączenie przycisków do masy</figcaption>
</figure>
<figure>
<img
src="./examples/MusiqueBox-examples_bb-2.png"
alt="Krok 2">
<figcaption>Krok 2. <br>Podłączenie przycisków do wyprowadzeń cyfrowych</figcaption>
</figure>
Aby podłączyć do urządzenia potencjometr, jedno z jego skrajnych wyprowadzeń należy podłączyć do masy, a drugie - szeregowo poprzez rezystor 220kΩ - do styku 3V3 na płytce NUCLEO. Środkowe wyprowadzenie potencjometru należy podłączyć do jednego z dwóch dostępnych wejść analogowych - są to piny oznaczone jako A4 i A5.
<figure>
<img
src="./examples/MusiqueBox-examples_bb-3.png"
alt="Krok 3">
<figcaption>Krok 3. <br>Podłączenie potencjometrów do masy</figcaption>
</figure>
<figure>
<img
src="./examples/MusiqueBox-examples_bb-4.png"
alt="Krok 4">
<figcaption>Krok 4. <br>Podłączenie potencjometrów do napięcia 3V</figcaption>
</figure>
<figure>
<img
src="./examples/MusiqueBox-examples_bb-5.png"
alt="Krok 5">
<figcaption>Krok 5. <br>Podłączenie potencjometrów do wyprowadzeń analogowych</figcaption>
</figure>
### Układ sterujący CV
Pospolite instrumenty sterowane napięciem zasilane oraz sterowane są napięciami z zakresu 0 do 12V. W celu zapewnienia takich napięć na wyjściu MusiqueBoxa konieczne jest zastosowanie wzmacniacza operacyjnego aby odizolować napięcia na płytce rozwojowej od napięć instrumentu, oraz aby rozszerzyć zakres napięć wyjściowych.
Konstrukcja tego układu wygląda następująco:
<figure>
<img
src="./examples/MusiqueBox-amp_schem.png"
alt="Schemat wzmacniacza">
<figcaption>Schemat wzmacniacza</figcaption>
</figure>
Sugerowane jest zastosowanie układu TL072CP, składającego się z dwóch zintegrowanych wzmacniaczy operacyjnych. W celu konstrukcji układu sterowania CV należy wyprowadzić piny 4 oraz 8 - odpowiednio napięcie negatywne i pozytywne - na zewnętrzne porty. Są to piny zasilające wzmacniacz, i muszą zostać one podłączone później do zasilania **instrumentu** w celu prawidłowego działania. Wyprowadzić należy również na podobny port masę MusiqueBoxa - musi ono zostać sparowane z masą instrumentu.
<figure>
<img
src="./examples/MusiqueBox_bb-amp-example-1.png"
alt="Krok 1">
<figcaption>Krok 1. <br>Wyprowadzenie pinów zasilania</figcaption>
</figure>
<figure>
<img
src="./examples/MusiqueBox_bb-amp-example-2.png"
alt="Krok 2">
<figcaption>Krok 2. <br>Wyprowadzenie masy</figcaption>
</figure>
Piny 2 i 6 układu TL072CP należy podłączyć do masy rezystorami 10kΩ, oraz każdy z nich podłączyć również przez potencjometr montażowy 10kΩ - odpowiednio do pinów 1 oraz 7 tego samego układu. Piny 1 oraz 7 będą również wyjściami napięcia sterującego, należy je wyprowadzić na porty zewnętrzne, podobnie do portów zasilania omówionych nieco wyżej. Napięcie sterujące będzie kontrolowane przez piny A2 oraz D13 na płytce NUCLEO. Te dwa wyprowadzenia należy zewrzeć odpowiednio z pinami 3 oraz 5 wzmacniacza. Napięcie wyjściowe będzie odpowiadało napięciom na tych pinach, ale będzie wzmocnione i odizolowane od instrumentu przez wzmacniacz.
<figure>
<img
src="./examples/MusiqueBox_bb-amp-example-3.png"
alt="Krok 3">
<figcaption>Krok 3. <br>Rezystory 10kΩ</figcaption>
</figure>
<figure>
<img
src="./examples/MusiqueBox_bb-amp-example-4.png"
alt="Krok 4">
<figcaption>Krok 4. <br>Potencjometry montażowe 10kΩ</figcaption>
</figure>
<figure>
<img
src="./examples/MusiqueBox_bb-amp-example-5.png"
alt="Krok 5">
<figcaption>Krok 5. <br>Wyprowadzenie pinów z napięciem sterującym</figcaption>
</figure>
<figure>
<img
src="./examples/MusiqueBox_bb-amp-example-6.png"
alt="Krok 6">
<figcaption>Krok 6. <br>Podłączenie pinów sterujących z płytki</figcaption>
</figure>
Wzmocnienie napięcia przez wzmacniacz w powyższym układzie kontrolowane jest przez potencjometry montażowe - zależnie od oporu przez nie ustawionego, zakres napięć można zwiększyć maksymalnie do 10V, ale nie powyżej napięcia zapewnionego przez instrument. Po montażu urządzenia i wgraniu oprogramowania, należy je podłączyć do instrumentu i w razie potrzeby doregulować opór potencjometrów, tak, aby odpowiednio nastroić urządzenie.
Dodatkowo, aby grać instrumentem sterowanym napięciem potrzebny jest cyfrowy sygnał GATE. W naszej implementacji sygnał GATE obsługiwany jest bezpośrednio przez pin D12 płytki i również należy go wyprowadzić na zewnętrzny port.
<figure>
<img
src="./examples/MusiqueBox_bb-amp-example-7.png"
alt="Krok 7">
<figcaption>Krok 7. <br>Wyprowadzenie pinu GATE</figcaption>
</figure>

View File

@ -0,0 +1,601 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file : main.c
* @brief : Main program body
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2022 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
/* USER CODE END PTD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
#define min(a,b) (((a) < (b)) ? (a) : (b))
#define max(a,b) (((a) > (b)) ? (a) : (b))
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
ADC_HandleTypeDef hadc1;
DAC_HandleTypeDef hdac1;
UART_HandleTypeDef huart2;
/* USER CODE BEGIN PV */
uint32_t adc_vals_old[2] = {};
uint32_t adc_vals[2];
uint8_t rx = 0;
uint8_t rx_buf[128] = {0};
int tail = 0, head = 0;
uint8_t notes[128] = {0};
uint8_t note_priority = 1;
uint32_t tones[] = {1508, 1587, 1666, 1745, 1824, 1903, 1986, 2065, 2146, 2225, 2304, 2383,
1508, 1587, 1666, 1745, 1824, 1903, 1986, 2065, 2146, 2225, 2304, 2383,
1508, 1587, 1666, 1745, 1824, 1903, 1986, 2065, 2146, 2225, 2304, 2383,
1508, 1587, 1666, 1745, 1824, 1903, 1986, 2065, 2146, 2225, 2304, 2383,
1508, 1587, 1666, 1745, 1824, 1903, 1986, 2065, 2146, 2225, 2304, 2383,
/*c4*/ 1508, 1587, 1666, 1745, 1824, 1903, 1986, 2065, 2146, 2225, 2304, 2383,
1508, 1587, 1666, 1745, 1824, 1903, 1986, 2065, 2146, 2225, 2304, 2383,
1508, 1587, 1666, 1745, 1824, 1903, 1986, 2065, 2146, 2225, 2304, 2383,
1508, 1587, 1666, 1745, 1824, 1903, 1986, 2065, 2146, 2225, 2304, 2383,
1508, 1587, 1666, 1745, 1824, 1903, 1986, 2065, 2146, 2225, 2304, 2383,
1508, 1587, 1666, 1745, 1824, 1903, 1986, 2065, 2146, 2225, 2304, 2383,
1508, 1587, 1666, 1745, 1824, 1903, 1986, 2065};
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_USART2_UART_Init(void);
static void MX_ADC1_Init(void);
static void MX_DAC1_Init(void);
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
uint32_t endian_swap(uint32_t value)
{
_Static_assert(sizeof(uint32_t) == 4, "I can't imagine world where this is false");
char buf[sizeof(uint32_t)];
memcpy(buf, &value, sizeof(value));
// swap(buf[0], buf[3])
uint32_t t = buf[0]; buf[0] = buf[3]; buf[3] = t;
// swap(buf[1], buf[2])
t = buf[1]; buf[1] = buf[2]; buf[2] = t;
memcpy(&value, buf, sizeof(value));
return value;
}
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == IT1_Pin) {
uint8_t val = 0b10000000;
//HAL_UART_Transmit(&huart2, (uint8_t *) &val, sizeof(val), HAL_MAX_DELAY);
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_9)){
// falling
printf("D%04lu\n", 0);
} else{
// rising
printf("D%04lu\n", 4095);
}
}
if (GPIO_Pin == IT2_Pin) {
uint8_t val = 0b10000001;
//HAL_UART_Transmit(&huart2, (uint8_t *) &val, sizeof(val), HAL_MAX_DELAY);
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_8)){
// falling
printf("C%04lu\n", 0);
} else{
// rising
printf("C%04lu\n", 4095);
}
}
if (GPIO_Pin == IT3_Pin) {
uint8_t val = 0b10000010;
//HAL_UART_Transmit(&huart2, (uint8_t *) &val, sizeof(val), HAL_MAX_DELAY);
if(HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_7)){
// falling
printf("E%04lu\n", 0);
} else{
// rising
printf("E%04lu\n", 4095);
}
}
}
int __io_putchar(int ch)
{
if (ch == '\n') {
__io_putchar('\r');
}
HAL_UART_Transmit(&huart2, (uint8_t*)&ch, 1, HAL_MAX_DELAY);
return 1;
}
/* USER CODE END 0 */
/**
* @brief The application entry point.
* @retval int
*/
int main(void)
{
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/* MCU Configuration--------------------------------------------------------*/
/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();
/* USER CODE BEGIN Init */
/* USER CODE END Init */
/* Configure the system clock */
SystemClock_Config();
/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */
/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_USART2_UART_Init();
MX_ADC1_Init();
MX_DAC1_Init();
/* USER CODE BEGIN 2 */
// start both DAC channels
HAL_DAC_Start(&hdac1, DAC1_CHANNEL_1);
HAL_DAC_Start(&hdac1, DAC1_CHANNEL_2);
// configure built-in ADC
HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);
HAL_UART_Receive_IT(&huart2, &rx, 1);
/* USER CODE END 2 */
/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/*int transmit_flag = 0;
char adc_chars[sizeof(uint32_t)] = {};
for(int i = 0; i < 2; i++){
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
adc_vals[i] = HAL_ADC_GetValue(&hadc1);
if((adc_vals[i] > adc_vals_old[i] && adc_vals[i] - adc_vals_old[i] > 100) ||
(adc_vals[i] < adc_vals_old[i] && adc_vals_old[i] - adc_vals[i] > 100)){
printf("NR %d do zmiany\n", i);
uint32_t temp = adc_vals[i];
adc_vals_old[i] = temp;
adc_vals[i] = endian_swap(temp);
memcpy(adc_chars + (sizeof(uint32_t)*i), &adc_vals[i], sizeof(uint32_t));
transmit_flag = 1;
}
}
if(transmit_flag == 1){
//HAL_UART_Transmit(&huart2, (uint8_t *) &adc_chars[0], 2 * sizeof(uint32_t), HAL_MAX_DELAY);
}*/
int note_update_flag = 0;
if (head != tail){
if((rx_buf[tail] & 0b11110000) == 0b10010000){
// note on
int bytes_available = (head - tail + 128) % 128;
if (bytes_available >= 3){
// if all 3 bytes available
// printf("G%04d\n", rx_buf[tail+1]);
notes[rx_buf[tail+1]] = note_priority++;
note_update_flag = 1;
tail = (tail+3)%128;
}
}else if((rx_buf[tail] & 0b11110000) == 0b10000000){
// note off
int bytes_available = (head - tail + 128) % 128;
if (bytes_available >= 3){
// if all 3 bytes available
// printf("G%04d\n", rx_buf[tail+1]);
notes[rx_buf[tail+1]] = 0;
note_update_flag = 1;
tail = (tail+3)%128;
}
}else{
// anything else
// for now drop the data
tail = (tail+1)%128;
}
if(note_update_flag){
int newest_note = -1;
uint8_t max_priority = 0;
for(int i = 0; i < 128; i++){
if(notes[i] > max_priority){
max_priority = notes[i];
newest_note = i;
}
}
if(newest_note >= 0){
DAC1->DHR12R1 = max(min(tones[newest_note], 4095), 0);
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_SET);
}else{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
}
}
/////////////////////// A //////////////////////
/*uint8_t data[3];
data[0] = 0b10010000;
data[1] = rx_buf[tail];
data[2] = 0b01111111;
HAL_UART_Transmit(&huart3, &data[0], 3, HAL_MAX_DELAY);
HAL_Delay(500);
data[0] = 0b10000000;
HAL_UART_Transmit(&huart3, &data[0], 3, HAL_MAX_DELAY);
tail = (tail+1)%128;*/
}
char adc_chars[sizeof(uint32_t)] = {};
for(int i = 0; i < 2; i++){
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);
adc_vals[i] = 4095 - HAL_ADC_GetValue(&hadc1);
}
printf("A%04lu\n", adc_vals[0]);
printf("B%04lu\n", adc_vals[1]);
/*
for(int i = 0; i < 2; i++){
uint32_t temp = adc_vals[i];
adc_vals[i] = endian_swap(temp) << 3;
memcpy(adc_chars + (sizeof(uint32_t)*i), &adc_vals[i], sizeof(uint32_t));
}
HAL_UART_Transmit(&huart2, (uint8_t *) &adc_chars[0], 2 * sizeof(uint32_t), HAL_MAX_DELAY);
*/
HAL_Delay(100);
/* USER CODE END WHILE */
/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}
/**
* @brief System Clock Configuration
* @retval None
*/
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/** Configure the main internal regulator output voltage
*/
if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
{
Error_Handler();
}
/** Initializes the RCC Oscillators according to the specified parameters
* in the RCC_OscInitTypeDef structure.
*/
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
RCC_OscInitStruct.HSIState = RCC_HSI_ON;
RCC_OscInitStruct.HSICalibrationValue = RCC_HSICALIBRATION_DEFAULT;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
RCC_OscInitStruct.PLL.PLLM = 1;
RCC_OscInitStruct.PLL.PLLN = 10;
RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/** Initializes the CPU, AHB and APB buses clocks
*/
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
{
Error_Handler();
}
}
/**
* @brief ADC1 Initialization Function
* @param None
* @retval None
*/
static void MX_ADC1_Init(void)
{
/* USER CODE BEGIN ADC1_Init 0 */
/* USER CODE END ADC1_Init 0 */
ADC_MultiModeTypeDef multimode = {0};
ADC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN ADC1_Init 1 */
/* USER CODE END ADC1_Init 1 */
/** Common config
*/
hadc1.Instance = ADC1;
hadc1.Init.ClockPrescaler = ADC_CLOCK_ASYNC_DIV1;
hadc1.Init.Resolution = ADC_RESOLUTION_12B;
hadc1.Init.DataAlign = ADC_DATAALIGN_RIGHT;
hadc1.Init.ScanConvMode = ADC_SCAN_ENABLE;
hadc1.Init.EOCSelection = ADC_EOC_SINGLE_CONV;
hadc1.Init.LowPowerAutoWait = DISABLE;
hadc1.Init.ContinuousConvMode = DISABLE;
hadc1.Init.NbrOfConversion = 2;
hadc1.Init.DiscontinuousConvMode = DISABLE;
hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START;
hadc1.Init.ExternalTrigConvEdge = ADC_EXTERNALTRIGCONVEDGE_NONE;
hadc1.Init.DMAContinuousRequests = DISABLE;
hadc1.Init.Overrun = ADC_OVR_DATA_PRESERVED;
hadc1.Init.OversamplingMode = DISABLE;
if (HAL_ADC_Init(&hadc1) != HAL_OK)
{
Error_Handler();
}
/** Configure the ADC multi-mode
*/
multimode.Mode = ADC_MODE_INDEPENDENT;
if (HAL_ADCEx_MultiModeConfigChannel(&hadc1, &multimode) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_1;
sConfig.Rank = ADC_REGULAR_RANK_1;
sConfig.SamplingTime = ADC_SAMPLETIME_640CYCLES_5;
sConfig.SingleDiff = ADC_SINGLE_ENDED;
sConfig.OffsetNumber = ADC_OFFSET_NONE;
sConfig.Offset = 0;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/** Configure Regular Channel
*/
sConfig.Channel = ADC_CHANNEL_2;
sConfig.Rank = ADC_REGULAR_RANK_2;
if (HAL_ADC_ConfigChannel(&hadc1, &sConfig) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN ADC1_Init 2 */
/* USER CODE END ADC1_Init 2 */
}
/**
* @brief DAC1 Initialization Function
* @param None
* @retval None
*/
static void MX_DAC1_Init(void)
{
/* USER CODE BEGIN DAC1_Init 0 */
/* USER CODE END DAC1_Init 0 */
DAC_ChannelConfTypeDef sConfig = {0};
/* USER CODE BEGIN DAC1_Init 1 */
/* USER CODE END DAC1_Init 1 */
/** DAC Initialization
*/
hdac1.Instance = DAC1;
if (HAL_DAC_Init(&hdac1) != HAL_OK)
{
Error_Handler();
}
/** DAC channel OUT1 config
*/
sConfig.DAC_SampleAndHold = DAC_SAMPLEANDHOLD_DISABLE;
sConfig.DAC_Trigger = DAC_TRIGGER_NONE;
sConfig.DAC_OutputBuffer = DAC_OUTPUTBUFFER_ENABLE;
sConfig.DAC_ConnectOnChipPeripheral = DAC_CHIPCONNECT_DISABLE;
sConfig.DAC_UserTrimming = DAC_TRIMMING_FACTORY;
if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
/** DAC channel OUT2 config
*/
if (HAL_DAC_ConfigChannel(&hdac1, &sConfig, DAC_CHANNEL_2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN DAC1_Init 2 */
/* USER CODE END DAC1_Init 2 */
}
/**
* @brief USART2 Initialization Function
* @param None
* @retval None
*/
static void MX_USART2_UART_Init(void)
{
/* USER CODE BEGIN USART2_Init 0 */
/* USER CODE END USART2_Init 0 */
/* USER CODE BEGIN USART2_Init 1 */
/* USER CODE END USART2_Init 1 */
huart2.Instance = USART2;
huart2.Init.BaudRate = 115200;
huart2.Init.WordLength = UART_WORDLENGTH_8B;
huart2.Init.StopBits = UART_STOPBITS_1;
huart2.Init.Parity = UART_PARITY_NONE;
huart2.Init.Mode = UART_MODE_TX_RX;
huart2.Init.HwFlowCtl = UART_HWCONTROL_NONE;
huart2.Init.OverSampling = UART_OVERSAMPLING_16;
huart2.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE;
huart2.AdvancedInit.AdvFeatureInit = UART_ADVFEATURE_NO_INIT;
if (HAL_UART_Init(&huart2) != HAL_OK)
{
Error_Handler();
}
/* USER CODE BEGIN USART2_Init 2 */
/* USER CODE END USART2_Init 2 */
}
/**
* @brief GPIO Initialization Function
* @param None
* @retval None
*/
static void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
__HAL_RCC_GPIOH_CLK_ENABLE();
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_GPIOB_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_6, GPIO_PIN_RESET);
/*Configure GPIO pin : B1_Pin */
GPIO_InitStruct.Pin = B1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_FALLING;
GPIO_InitStruct.Pull = GPIO_NOPULL;
HAL_GPIO_Init(B1_GPIO_Port, &GPIO_InitStruct);
/*Configure GPIO pin : PA6 */
GPIO_InitStruct.Pin = GPIO_PIN_6;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/*Configure GPIO pins : IT3_Pin IT2_Pin IT1_Pin */
GPIO_InitStruct.Pin = IT3_Pin|IT2_Pin|IT1_Pin;
GPIO_InitStruct.Mode = GPIO_MODE_IT_RISING_FALLING;
GPIO_InitStruct.Pull = GPIO_PULLUP;
HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
/* EXTI interrupt init*/
HAL_NVIC_SetPriority(EXTI9_5_IRQn, 8, 0);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
}
/* USER CODE BEGIN 4 */
/* USER CODE END 4 */
/**
* @brief This function is executed in case of error occurrence.
* @retval None
*/
void Error_Handler(void)
{
/* USER CODE BEGIN Error_Handler_Debug */
/* User can add his own implementation to report the HAL error return state */
__disable_irq();
while (1)
{
}
/* USER CODE END Error_Handler_Debug */
}
#ifdef USE_FULL_ASSERT
/**
* @brief Reports the name of the source file and the source line number
* where the assert_param error has occurred.
* @param file: pointer to the source file name
* @param line: assert_param error line source number
* @retval None
*/
void assert_failed(uint8_t *file, uint32_t line)
{
/* USER CODE BEGIN 6 */
/* User can add his own implementation to report the file name and line number,
ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
/* USER CODE END 6 */
}
#endif /* USE_FULL_ASSERT */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@ -0,0 +1,246 @@
/* USER CODE BEGIN Header */
/**
******************************************************************************
* @file stm32l4xx_it.c
* @brief Interrupt Service Routines.
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2022 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* USER CODE END Header */
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32l4xx_it.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN TD */
/* USER CODE END TD */
/* Private define ------------------------------------------------------------*/
/* USER CODE BEGIN PD */
/* USER CODE END PD */
/* Private macro -------------------------------------------------------------*/
/* USER CODE BEGIN PM */
/* USER CODE END PM */
/* Private variables ---------------------------------------------------------*/
/* USER CODE BEGIN PV */
extern uint8_t rx;
extern uint8_t rx_buf[128];
extern uint8_t head;
extern uint8_t tail;
/* USER CODE END PV */
/* Private function prototypes -----------------------------------------------*/
/* USER CODE BEGIN PFP */
/* USER CODE END PFP */
/* Private user code ---------------------------------------------------------*/
/* USER CODE BEGIN 0 */
/* USER CODE END 0 */
/* External variables --------------------------------------------------------*/
extern UART_HandleTypeDef huart2;
/* USER CODE BEGIN EV */
/* USER CODE END EV */
/******************************************************************************/
/* Cortex-M4 Processor Interruption and Exception Handlers */
/******************************************************************************/
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
while (1)
{
}
/* USER CODE END NonMaskableInt_IRQn 1 */
}
/**
* @brief This function handles Hard fault interrupt.
*/
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
/* USER CODE END HardFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
}
/**
* @brief This function handles Memory management fault.
*/
void MemManage_Handler(void)
{
/* USER CODE BEGIN MemoryManagement_IRQn 0 */
/* USER CODE END MemoryManagement_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
/* USER CODE END W1_MemoryManagement_IRQn 0 */
}
}
/**
* @brief This function handles Prefetch fault, memory access fault.
*/
void BusFault_Handler(void)
{
/* USER CODE BEGIN BusFault_IRQn 0 */
/* USER CODE END BusFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_BusFault_IRQn 0 */
/* USER CODE END W1_BusFault_IRQn 0 */
}
}
/**
* @brief This function handles Undefined instruction or illegal state.
*/
void UsageFault_Handler(void)
{
/* USER CODE BEGIN UsageFault_IRQn 0 */
/* USER CODE END UsageFault_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_UsageFault_IRQn 0 */
/* USER CODE END W1_UsageFault_IRQn 0 */
}
}
/**
* @brief This function handles System service call via SWI instruction.
*/
void SVC_Handler(void)
{
/* USER CODE BEGIN SVCall_IRQn 0 */
/* USER CODE END SVCall_IRQn 0 */
/* USER CODE BEGIN SVCall_IRQn 1 */
/* USER CODE END SVCall_IRQn 1 */
}
/**
* @brief This function handles Debug monitor.
*/
void DebugMon_Handler(void)
{
/* USER CODE BEGIN DebugMonitor_IRQn 0 */
/* USER CODE END DebugMonitor_IRQn 0 */
/* USER CODE BEGIN DebugMonitor_IRQn 1 */
/* USER CODE END DebugMonitor_IRQn 1 */
}
/**
* @brief This function handles Pendable request for system service.
*/
void PendSV_Handler(void)
{
/* USER CODE BEGIN PendSV_IRQn 0 */
/* USER CODE END PendSV_IRQn 0 */
/* USER CODE BEGIN PendSV_IRQn 1 */
/* USER CODE END PendSV_IRQn 1 */
}
/**
* @brief This function handles System tick timer.
*/
void SysTick_Handler(void)
{
/* USER CODE BEGIN SysTick_IRQn 0 */
/* USER CODE END SysTick_IRQn 0 */
HAL_IncTick();
/* USER CODE BEGIN SysTick_IRQn 1 */
/* USER CODE END SysTick_IRQn 1 */
}
/******************************************************************************/
/* STM32L4xx Peripheral Interrupt Handlers */
/* Add here the Interrupt Handlers for the used peripherals. */
/* For the available peripheral interrupt handler names, */
/* please refer to the startup file (startup_stm32l4xx.s). */
/******************************************************************************/
/**
* @brief This function handles EXTI line[9:5] interrupts.
*/
void EXTI9_5_IRQHandler(void)
{
/* USER CODE BEGIN EXTI9_5_IRQn 0 */
/* USER CODE END EXTI9_5_IRQn 0 */
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_7);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_8);
HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_9);
/* USER CODE BEGIN EXTI9_5_IRQn 1 */
/* USER CODE END EXTI9_5_IRQn 1 */
}
/**
* @brief This function handles USART2 global interrupt.
*/
void USART2_IRQHandler(void)
{
/* USER CODE BEGIN USART2_IRQn 0 */
/* USER CODE END USART2_IRQn 0 */
HAL_UART_IRQHandler(&huart2);
/* USER CODE BEGIN USART2_IRQn 1 */
if ((head+1)%128 != tail){
rx_buf[head] = rx;
head = (head+1)%128;
}
HAL_UART_Receive_IT(&huart2, &rx, 1);
/* USER CODE END USART2_IRQn 1 */
}
/* USER CODE BEGIN 1 */
/* USER CODE END 1 */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

View File

@ -0,0 +1,244 @@
#MicroXplorer Configuration settings - do not modify
ADC1.Channel-1\#ChannelRegularConversion=ADC_CHANNEL_1
ADC1.Channel-2\#ChannelRegularConversion=ADC_CHANNEL_2
ADC1.IPParameters=Rank-1\#ChannelRegularConversion,Channel-1\#ChannelRegularConversion,SamplingTime-1\#ChannelRegularConversion,OffsetNumber-1\#ChannelRegularConversion,NbrOfConversionFlag,Rank-2\#ChannelRegularConversion,Channel-2\#ChannelRegularConversion,SamplingTime-2\#ChannelRegularConversion,OffsetNumber-2\#ChannelRegularConversion,NbrOfConversion,master
ADC1.NbrOfConversion=2
ADC1.NbrOfConversionFlag=1
ADC1.OffsetNumber-1\#ChannelRegularConversion=ADC_OFFSET_NONE
ADC1.OffsetNumber-2\#ChannelRegularConversion=ADC_OFFSET_NONE
ADC1.Rank-1\#ChannelRegularConversion=1
ADC1.Rank-2\#ChannelRegularConversion=2
ADC1.SamplingTime-1\#ChannelRegularConversion=ADC_SAMPLETIME_640CYCLES_5
ADC1.SamplingTime-2\#ChannelRegularConversion=ADC_SAMPLETIME_640CYCLES_5
ADC1.master=1
DAC1.DAC_Channel-DAC_OUT2=DAC_CHANNEL_2
DAC1.IPParameters=DAC_Channel-DAC_OUT2
File.Version=6
GPIO.groupedBy=Group By Peripherals
KeepUserPlacement=false
Mcu.Family=STM32L4
Mcu.IP0=ADC1
Mcu.IP1=DAC1
Mcu.IP2=NVIC
Mcu.IP3=RCC
Mcu.IP4=SYS
Mcu.IP5=USART2
Mcu.IPNb=6
Mcu.Name=STM32L476R(C-E-G)Tx
Mcu.Package=LQFP64
Mcu.Pin0=PC13
Mcu.Pin1=PC14-OSC32_IN (PC14)
Mcu.Pin10=PA5
Mcu.Pin11=PA6
Mcu.Pin12=PA7
Mcu.Pin13=PA8
Mcu.Pin14=PA9
Mcu.Pin15=PA13 (JTMS-SWDIO)
Mcu.Pin16=PA14 (JTCK-SWCLK)
Mcu.Pin17=PB3 (JTDO-TRACESWO)
Mcu.Pin18=VP_ADC1_Vref_Input
Mcu.Pin19=VP_SYS_VS_Systick
Mcu.Pin2=PC15-OSC32_OUT (PC15)
Mcu.Pin3=PH0-OSC_IN (PH0)
Mcu.Pin4=PH1-OSC_OUT (PH1)
Mcu.Pin5=PC0
Mcu.Pin6=PC1
Mcu.Pin7=PA2
Mcu.Pin8=PA3
Mcu.Pin9=PA4
Mcu.PinsNb=20
Mcu.ThirdPartyNb=0
Mcu.UserConstants=
Mcu.UserName=STM32L476RGTx
MxCube.Version=6.3.0
MxDb.Version=DB.6.0.30
NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:true\:false
NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:true\:false
NVIC.EXTI9_5_IRQn=true\:8\:0\:true\:false\:true\:true\:true
NVIC.ForceEnableDMAVector=true
NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:true\:false
NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:true\:false
NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:true\:false
NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false
NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4
NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false
NVIC.SysTick_IRQn=true\:0\:0\:false\:false\:true\:true\:true
NVIC.USART2_IRQn=true\:0\:0\:false\:false\:true\:true\:true
NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:true\:false
PA13\ (JTMS-SWDIO).GPIOParameters=GPIO_Label
PA13\ (JTMS-SWDIO).GPIO_Label=TMS
PA13\ (JTMS-SWDIO).Locked=true
PA13\ (JTMS-SWDIO).Mode=Serial_Wire
PA13\ (JTMS-SWDIO).Signal=SYS_JTMS-SWDIO
PA14\ (JTCK-SWCLK).GPIOParameters=GPIO_Label
PA14\ (JTCK-SWCLK).GPIO_Label=TCK
PA14\ (JTCK-SWCLK).Locked=true
PA14\ (JTCK-SWCLK).Mode=Serial_Wire
PA14\ (JTCK-SWCLK).Signal=SYS_JTCK-SWCLK
PA2.GPIOParameters=GPIO_Speed,GPIO_PuPd,GPIO_Label,GPIO_Mode
PA2.GPIO_Label=USART_TX
PA2.GPIO_Mode=GPIO_MODE_AF_PP
PA2.GPIO_PuPd=GPIO_NOPULL
PA2.GPIO_Speed=GPIO_SPEED_FREQ_VERY_HIGH
PA2.Locked=true
PA2.Mode=Asynchronous
PA2.Signal=USART2_TX
PA3.GPIOParameters=GPIO_Speed,GPIO_PuPd,GPIO_Label,GPIO_Mode
PA3.GPIO_Label=USART_RX
PA3.GPIO_Mode=GPIO_MODE_AF_PP
PA3.GPIO_PuPd=GPIO_NOPULL
PA3.GPIO_Speed=GPIO_SPEED_FREQ_VERY_HIGH
PA3.Locked=true
PA3.Mode=Asynchronous
PA3.Signal=USART2_RX
PA4.Signal=COMP_DAC11_group
PA5.Signal=COMP_DAC12_group
PA6.Locked=true
PA6.Signal=GPIO_Output
PA7.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultEXTI
PA7.GPIO_Label=IT3
PA7.GPIO_ModeDefaultEXTI=GPIO_MODE_IT_RISING_FALLING
PA7.GPIO_PuPd=GPIO_PULLUP
PA7.Locked=true
PA7.Signal=GPXTI7
PA8.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultEXTI
PA8.GPIO_Label=IT2
PA8.GPIO_ModeDefaultEXTI=GPIO_MODE_IT_RISING_FALLING
PA8.GPIO_PuPd=GPIO_PULLUP
PA8.Locked=true
PA8.Signal=GPXTI8
PA9.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultEXTI
PA9.GPIO_Label=IT1
PA9.GPIO_ModeDefaultEXTI=GPIO_MODE_IT_RISING_FALLING
PA9.GPIO_PuPd=GPIO_PULLUP
PA9.Locked=true
PA9.Signal=GPXTI9
PB3\ (JTDO-TRACESWO).GPIOParameters=GPIO_Label
PB3\ (JTDO-TRACESWO).GPIO_Label=SWO
PB3\ (JTDO-TRACESWO).Locked=true
PB3\ (JTDO-TRACESWO).Signal=SYS_JTDO-SWO
PC0.Signal=ADCx_IN1
PC1.Signal=ADCx_IN2
PC13.GPIOParameters=GPIO_PuPd,GPIO_Label,GPIO_ModeDefaultEXTI
PC13.GPIO_Label=B1 [Blue PushButton]
PC13.GPIO_ModeDefaultEXTI=GPIO_MODE_IT_FALLING
PC13.GPIO_PuPd=GPIO_NOPULL
PC13.Locked=true
PC13.Signal=GPXTI13
PC14-OSC32_IN\ (PC14).Locked=true
PC14-OSC32_IN\ (PC14).Mode=LSE-External-Oscillator
PC14-OSC32_IN\ (PC14).Signal=RCC_OSC32_IN
PC15-OSC32_OUT\ (PC15).Locked=true
PC15-OSC32_OUT\ (PC15).Mode=LSE-External-Oscillator
PC15-OSC32_OUT\ (PC15).Signal=RCC_OSC32_OUT
PH0-OSC_IN\ (PH0).Locked=true
PH0-OSC_IN\ (PH0).Signal=RCC_OSC_IN
PH1-OSC_OUT\ (PH1).Locked=true
PH1-OSC_OUT\ (PH1).Signal=RCC_OSC_OUT
PinOutPanel.RotationAngle=0
ProjectManager.AskForMigrate=true
ProjectManager.BackupPrevious=false
ProjectManager.CompilerOptimize=6
ProjectManager.ComputerToolchain=false
ProjectManager.CoupleFile=false
ProjectManager.CustomerFirmwarePackage=
ProjectManager.DefaultFWLocation=true
ProjectManager.DeletePrevious=true
ProjectManager.DeviceId=STM32L476RGTx
ProjectManager.FirmwarePackage=STM32Cube FW_L4 V1.17.2
ProjectManager.FreePins=false
ProjectManager.HalAssertFull=false
ProjectManager.HeapSize=0x200
ProjectManager.KeepUserCode=true
ProjectManager.LastFirmware=true
ProjectManager.LibraryCopy=1
ProjectManager.MainLocation=Core/Src
ProjectManager.NoMain=false
ProjectManager.PreviousToolchain=STM32CubeIDE
ProjectManager.ProjectBuild=false
ProjectManager.ProjectFileName=musique_box.ioc
ProjectManager.ProjectName=musique_box
ProjectManager.RegisterCallBack=
ProjectManager.StackSize=0x400
ProjectManager.TargetToolchain=STM32CubeIDE
ProjectManager.ToolChainLocation=
ProjectManager.UnderRoot=true
ProjectManager.functionlistsort=1-MX_GPIO_Init-GPIO-false-HAL-true,2-SystemClock_Config-RCC-false-HAL-false,3-MX_USART2_UART_Init-USART2-false-HAL-true,4-MX_ADC1_Init-ADC1-false-HAL-true,5-MX_DAC1_Init-DAC1-false-HAL-true
RCC.ADCFreq_Value=64000000
RCC.AHBFreq_Value=80000000
RCC.APB1Freq_Value=80000000
RCC.APB1TimFreq_Value=80000000
RCC.APB2Freq_Value=80000000
RCC.APB2TimFreq_Value=80000000
RCC.CortexFreq_Value=80000000
RCC.DFSDMFreq_Value=80000000
RCC.FCLKCortexFreq_Value=80000000
RCC.FamilyName=M
RCC.HCLKFreq_Value=80000000
RCC.HSE_VALUE=8000000
RCC.HSI_VALUE=16000000
RCC.I2C1Freq_Value=80000000
RCC.I2C2Freq_Value=80000000
RCC.I2C3Freq_Value=80000000
RCC.IPParameters=ADCFreq_Value,AHBFreq_Value,APB1Freq_Value,APB1TimFreq_Value,APB2Freq_Value,APB2TimFreq_Value,CortexFreq_Value,DFSDMFreq_Value,FCLKCortexFreq_Value,FamilyName,HCLKFreq_Value,HSE_VALUE,HSI_VALUE,I2C1Freq_Value,I2C2Freq_Value,I2C3Freq_Value,LPTIM1Freq_Value,LPTIM2Freq_Value,LPUART1Freq_Value,LSCOPinFreq_Value,LSI_VALUE,MCO1PinFreq_Value,MSI_VALUE,PLLN,PLLPoutputFreq_Value,PLLQoutputFreq_Value,PLLRCLKFreq_Value,PLLSAI1PoutputFreq_Value,PLLSAI1QoutputFreq_Value,PLLSAI1RoutputFreq_Value,PLLSAI2PoutputFreq_Value,PLLSAI2RoutputFreq_Value,PLLSourceVirtual,PREFETCH_ENABLE,PWRFreq_Value,RNGFreq_Value,SAI1Freq_Value,SAI2Freq_Value,SDMMCFreq_Value,SWPMI1Freq_Value,SYSCLKFreq_VALUE,SYSCLKSource,UART4Freq_Value,UART5Freq_Value,USART1Freq_Value,USART2Freq_Value,USART3Freq_Value,USBFreq_Value,VCOInputFreq_Value,VCOOutputFreq_Value,VCOSAI1OutputFreq_Value,VCOSAI2OutputFreq_Value
RCC.LPTIM1Freq_Value=80000000
RCC.LPTIM2Freq_Value=80000000
RCC.LPUART1Freq_Value=80000000
RCC.LSCOPinFreq_Value=32000
RCC.LSI_VALUE=32000
RCC.MCO1PinFreq_Value=80000000
RCC.MSI_VALUE=4000000
RCC.PLLN=10
RCC.PLLPoutputFreq_Value=22857142.85714286
RCC.PLLQoutputFreq_Value=80000000
RCC.PLLRCLKFreq_Value=80000000
RCC.PLLSAI1PoutputFreq_Value=18285714.285714287
RCC.PLLSAI1QoutputFreq_Value=64000000
RCC.PLLSAI1RoutputFreq_Value=64000000
RCC.PLLSAI2PoutputFreq_Value=18285714.285714287
RCC.PLLSAI2RoutputFreq_Value=64000000
RCC.PLLSourceVirtual=RCC_PLLSOURCE_HSI
RCC.PREFETCH_ENABLE=1
RCC.PWRFreq_Value=80000000
RCC.RNGFreq_Value=64000000
RCC.SAI1Freq_Value=18285714.285714287
RCC.SAI2Freq_Value=18285714.285714287
RCC.SDMMCFreq_Value=64000000
RCC.SWPMI1Freq_Value=80000000
RCC.SYSCLKFreq_VALUE=80000000
RCC.SYSCLKSource=RCC_SYSCLKSOURCE_PLLCLK
RCC.UART4Freq_Value=80000000
RCC.UART5Freq_Value=80000000
RCC.USART1Freq_Value=80000000
RCC.USART2Freq_Value=80000000
RCC.USART3Freq_Value=80000000
RCC.USBFreq_Value=64000000
RCC.VCOInputFreq_Value=16000000
RCC.VCOOutputFreq_Value=160000000
RCC.VCOSAI1OutputFreq_Value=128000000
RCC.VCOSAI2OutputFreq_Value=128000000
SH.ADCx_IN1.0=ADC1_IN1,IN1-Single-Ended
SH.ADCx_IN1.ConfNb=1
SH.ADCx_IN2.0=ADC1_IN2,IN2-Single-Ended
SH.ADCx_IN2.ConfNb=1
SH.COMP_DAC11_group.0=DAC1_OUT1,DAC_OUT1
SH.COMP_DAC11_group.ConfNb=1
SH.COMP_DAC12_group.0=DAC1_OUT2,DAC_OUT2
SH.COMP_DAC12_group.ConfNb=1
SH.GPXTI13.0=GPIO_EXTI13
SH.GPXTI13.ConfNb=1
SH.GPXTI7.0=GPIO_EXTI7
SH.GPXTI7.ConfNb=1
SH.GPXTI8.0=GPIO_EXTI8
SH.GPXTI8.ConfNb=1
SH.GPXTI9.0=GPIO_EXTI9
SH.GPXTI9.ConfNb=1
USART2.IPParameters=VirtualMode-Asynchronous
USART2.VirtualMode-Asynchronous=VM_ASYNC
VP_ADC1_Vref_Input.Mode=IN-Vrefint
VP_ADC1_Vref_Input.Signal=ADC1_Vref_Input
VP_SYS_VS_Systick.Mode=SysTick
VP_SYS_VS_Systick.Signal=SYS_VS_Systick
board=NUCLEO-L476RG
boardIOC=true
isbadioc=false

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 351 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 327 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 339 KiB

7
lib/serial/LICENSE Normal file
View File

@ -0,0 +1,7 @@
Copyright (c) 2012 William Woodall, John Harrison
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,221 @@
/*!
* \file serial/impl/unix.h
* \author William Woodall <wjwwood@gmail.com>
* \author John Harrison <ash@greaterthaninfinity.com>
* \version 0.1
*
* \section LICENSE
*
* The MIT License
*
* Copyright (c) 2012 William Woodall, John Harrison
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* \section DESCRIPTION
*
* This provides a unix based pimpl for the Serial class. This implementation is
* based off termios.h and uses select for multiplexing the IO ports.
*
*/
#if !defined(_WIN32)
#ifndef SERIAL_IMPL_UNIX_H
#define SERIAL_IMPL_UNIX_H
#include "serial/serial.h"
#include <pthread.h>
namespace serial {
using std::size_t;
using std::string;
using std::invalid_argument;
using serial::SerialException;
using serial::IOException;
class MillisecondTimer {
public:
MillisecondTimer(const uint32_t millis);
int64_t remaining();
private:
static timespec timespec_now();
timespec expiry;
};
class serial::Serial::SerialImpl {
public:
SerialImpl (const string &port,
unsigned long baudrate,
bytesize_t bytesize,
parity_t parity,
stopbits_t stopbits,
flowcontrol_t flowcontrol);
virtual ~SerialImpl ();
void
open ();
void
close ();
bool
isOpen () const;
size_t
available ();
bool
waitReadable (uint32_t timeout);
void
waitByteTimes (size_t count);
size_t
read (uint8_t *buf, size_t size = 1);
size_t
write (const uint8_t *data, size_t length);
void
flush ();
void
flushInput ();
void
flushOutput ();
void
sendBreak (int duration);
void
setBreak (bool level);
void
setRTS (bool level);
void
setDTR (bool level);
bool
waitForChange ();
bool
getCTS ();
bool
getDSR ();
bool
getRI ();
bool
getCD ();
void
setPort (const string &port);
string
getPort () const;
void
setTimeout (Timeout &timeout);
Timeout
getTimeout () const;
void
setBaudrate (unsigned long baudrate);
unsigned long
getBaudrate () const;
void
setBytesize (bytesize_t bytesize);
bytesize_t
getBytesize () const;
void
setParity (parity_t parity);
parity_t
getParity () const;
void
setStopbits (stopbits_t stopbits);
stopbits_t
getStopbits () const;
void
setFlowcontrol (flowcontrol_t flowcontrol);
flowcontrol_t
getFlowcontrol () const;
void
readLock ();
void
readUnlock ();
void
writeLock ();
void
writeUnlock ();
protected:
void reconfigurePort ();
private:
string port_; // Path to the file descriptor
int fd_; // The current file descriptor
bool is_open_;
bool xonxoff_;
bool rtscts_;
Timeout timeout_; // Timeout for read operations
unsigned long baudrate_; // Baudrate
uint32_t byte_time_ns_; // Nanoseconds to transmit/receive a single byte
parity_t parity_; // Parity
bytesize_t bytesize_; // Size of the bytes
stopbits_t stopbits_; // Stop Bits
flowcontrol_t flowcontrol_; // Flow Control
// Mutex used to lock the read functions
pthread_mutex_t read_mutex;
// Mutex used to lock the write functions
pthread_mutex_t write_mutex;
};
}
#endif // SERIAL_IMPL_UNIX_H
#endif // !defined(_WIN32)

View File

@ -0,0 +1,207 @@
/*!
* \file serial/impl/windows.h
* \author William Woodall <wjwwood@gmail.com>
* \author John Harrison <ash@greaterthaninfinity.com>
* \version 0.1
*
* \section LICENSE
*
* The MIT License
*
* Copyright (c) 2012 William Woodall, John Harrison
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* \section DESCRIPTION
*
* This provides a windows implementation of the Serial class interface.
*
*/
#if defined(_WIN32)
#ifndef SERIAL_IMPL_WINDOWS_H
#define SERIAL_IMPL_WINDOWS_H
#include "serial/serial.h"
#include "windows.h"
namespace serial {
using std::string;
using std::wstring;
using std::invalid_argument;
using serial::SerialException;
using serial::IOException;
class serial::Serial::SerialImpl {
public:
SerialImpl (const string &port,
unsigned long baudrate,
bytesize_t bytesize,
parity_t parity,
stopbits_t stopbits,
flowcontrol_t flowcontrol);
virtual ~SerialImpl ();
void
open ();
void
close ();
bool
isOpen () const;
size_t
available ();
bool
waitReadable (uint32_t timeout);
void
waitByteTimes (size_t count);
size_t
read (uint8_t *buf, size_t size = 1);
size_t
write (const uint8_t *data, size_t length);
void
flush ();
void
flushInput ();
void
flushOutput ();
void
sendBreak (int duration);
void
setBreak (bool level);
void
setRTS (bool level);
void
setDTR (bool level);
bool
waitForChange ();
bool
getCTS ();
bool
getDSR ();
bool
getRI ();
bool
getCD ();
void
setPort (const string &port);
string
getPort () const;
void
setTimeout (Timeout &timeout);
Timeout
getTimeout () const;
void
setBaudrate (unsigned long baudrate);
unsigned long
getBaudrate () const;
void
setBytesize (bytesize_t bytesize);
bytesize_t
getBytesize () const;
void
setParity (parity_t parity);
parity_t
getParity () const;
void
setStopbits (stopbits_t stopbits);
stopbits_t
getStopbits () const;
void
setFlowcontrol (flowcontrol_t flowcontrol);
flowcontrol_t
getFlowcontrol () const;
void
readLock ();
void
readUnlock ();
void
writeLock ();
void
writeUnlock ();
protected:
void reconfigurePort ();
private:
wstring port_; // Path to the file descriptor
HANDLE fd_;
bool is_open_;
Timeout timeout_; // Timeout for read operations
unsigned long baudrate_; // Baudrate
parity_t parity_; // Parity
bytesize_t bytesize_; // Size of the bytes
stopbits_t stopbits_; // Stop Bits
flowcontrol_t flowcontrol_; // Flow Control
// Mutex used to lock the read functions
HANDLE read_mutex;
// Mutex used to lock the write functions
HANDLE write_mutex;
};
}
#endif // SERIAL_IMPL_WINDOWS_H
#endif // if defined(_WIN32)

View File

@ -0,0 +1,775 @@
/*!
* \file serial/serial.h
* \author William Woodall <wjwwood@gmail.com>
* \author John Harrison <ash.gti@gmail.com>
* \version 0.1
*
* \section LICENSE
*
* The MIT License
*
* Copyright (c) 2012 William Woodall
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*
* \section DESCRIPTION
*
* This provides a cross platform interface for interacting with Serial Ports.
*/
#ifndef SERIAL_H
#define SERIAL_H
#include <limits>
#include <vector>
#include <string>
#include <cstring>
#include <sstream>
#include <exception>
#include <stdexcept>
#include <serial/v8stdint.h>
#define THROW(exceptionClass, message) throw exceptionClass(__FILE__, \
__LINE__, (message) )
namespace serial {
/*!
* Enumeration defines the possible bytesizes for the serial port.
*/
typedef enum {
fivebits = 5,
sixbits = 6,
sevenbits = 7,
eightbits = 8
} bytesize_t;
/*!
* Enumeration defines the possible parity types for the serial port.
*/
typedef enum {
parity_none = 0,
parity_odd = 1,
parity_even = 2,
parity_mark = 3,
parity_space = 4
} parity_t;
/*!
* Enumeration defines the possible stopbit types for the serial port.
*/
typedef enum {
stopbits_one = 1,
stopbits_two = 2,
stopbits_one_point_five
} stopbits_t;
/*!
* Enumeration defines the possible flowcontrol types for the serial port.
*/
typedef enum {
flowcontrol_none = 0,
flowcontrol_software,
flowcontrol_hardware
} flowcontrol_t;
/*!
* Structure for setting the timeout of the serial port, times are
* in milliseconds.
*
* In order to disable the interbyte timeout, set it to Timeout::max().
*/
struct Timeout {
#ifdef max
# undef max
#endif
static uint32_t max() {return std::numeric_limits<uint32_t>::max();}
/*!
* Convenience function to generate Timeout structs using a
* single absolute timeout.
*
* \param timeout A long that defines the time in milliseconds until a
* timeout occurs after a call to read or write is made.
*
* \return Timeout struct that represents this simple timeout provided.
*/
static Timeout simpleTimeout(uint32_t timeout) {
return Timeout(max(), timeout, 0, timeout, 0);
}
/*! Number of milliseconds between bytes received to timeout on. */
uint32_t inter_byte_timeout;
/*! A constant number of milliseconds to wait after calling read. */
uint32_t read_timeout_constant;
/*! A multiplier against the number of requested bytes to wait after
* calling read.
*/
uint32_t read_timeout_multiplier;
/*! A constant number of milliseconds to wait after calling write. */
uint32_t write_timeout_constant;
/*! A multiplier against the number of requested bytes to wait after
* calling write.
*/
uint32_t write_timeout_multiplier;
explicit Timeout (uint32_t inter_byte_timeout_=0,
uint32_t read_timeout_constant_=0,
uint32_t read_timeout_multiplier_=0,
uint32_t write_timeout_constant_=0,
uint32_t write_timeout_multiplier_=0)
: inter_byte_timeout(inter_byte_timeout_),
read_timeout_constant(read_timeout_constant_),
read_timeout_multiplier(read_timeout_multiplier_),
write_timeout_constant(write_timeout_constant_),
write_timeout_multiplier(write_timeout_multiplier_)
{}
};
/*!
* Class that provides a portable serial port interface.
*/
class Serial {
public:
/*!
* Creates a Serial object and opens the port if a port is specified,
* otherwise it remains closed until serial::Serial::open is called.
*
* \param port A std::string containing the address of the serial port,
* which would be something like 'COM1' on Windows and '/dev/ttyS0'
* on Linux.
*
* \param baudrate An unsigned 32-bit integer that represents the baudrate
*
* \param timeout A serial::Timeout struct that defines the timeout
* conditions for the serial port. \see serial::Timeout
*
* \param bytesize Size of each byte in the serial transmission of data,
* default is eightbits, possible values are: fivebits, sixbits, sevenbits,
* eightbits
*
* \param parity Method of parity, default is parity_none, possible values
* are: parity_none, parity_odd, parity_even
*
* \param stopbits Number of stop bits used, default is stopbits_one,
* possible values are: stopbits_one, stopbits_one_point_five, stopbits_two
*
* \param flowcontrol Type of flowcontrol used, default is
* flowcontrol_none, possible values are: flowcontrol_none,
* flowcontrol_software, flowcontrol_hardware
*
* \throw serial::PortNotOpenedException
* \throw serial::IOException
* \throw std::invalid_argument
*/
Serial (const std::string &port = "",
uint32_t baudrate = 9600,
Timeout timeout = Timeout(),
bytesize_t bytesize = eightbits,
parity_t parity = parity_none,
stopbits_t stopbits = stopbits_one,
flowcontrol_t flowcontrol = flowcontrol_none);
/*! Destructor */
virtual ~Serial ();
/*!
* Opens the serial port as long as the port is set and the port isn't
* already open.
*
* If the port is provided to the constructor then an explicit call to open
* is not needed.
*
* \see Serial::Serial
*
* \throw std::invalid_argument
* \throw serial::SerialException
* \throw serial::IOException
*/
void
open ();
/*! Gets the open status of the serial port.
*
* \return Returns true if the port is open, false otherwise.
*/
bool
isOpen () const;
/*! Closes the serial port. */
void
close ();
/*! Return the number of characters in the buffer. */
size_t
available ();
/*! Block until there is serial data to read or read_timeout_constant
* number of milliseconds have elapsed. The return value is true when
* the function exits with the port in a readable state, false otherwise
* (due to timeout or select interruption). */
bool
waitReadable ();
/*! Block for a period of time corresponding to the transmission time of
* count characters at present serial settings. This may be used in con-
* junction with waitReadable to read larger blocks of data from the
* port. */
void
waitByteTimes (size_t count);
/*! Read a given amount of bytes from the serial port into a given buffer.
*
* The read function will return in one of three cases:
* * The number of requested bytes was read.
* * In this case the number of bytes requested will match the size_t
* returned by read.
* * A timeout occurred, in this case the number of bytes read will not
* match the amount requested, but no exception will be thrown. One of
* two possible timeouts occurred:
* * The inter byte timeout expired, this means that number of
* milliseconds elapsed between receiving bytes from the serial port
* exceeded the inter byte timeout.
* * The total timeout expired, which is calculated by multiplying the
* read timeout multiplier by the number of requested bytes and then
* added to the read timeout constant. If that total number of
* milliseconds elapses after the initial call to read a timeout will
* occur.
* * An exception occurred, in this case an actual exception will be thrown.
*
* \param buffer An uint8_t array of at least the requested size.
* \param size A size_t defining how many bytes to be read.
*
* \return A size_t representing the number of bytes read as a result of the
* call to read.
*
* \throw serial::PortNotOpenedException
* \throw serial::SerialException
*/
size_t
read (uint8_t *buffer, size_t size);
/*! Read a given amount of bytes from the serial port into a give buffer.
*
* \param buffer A reference to a std::vector of uint8_t.
* \param size A size_t defining how many bytes to be read.
*
* \return A size_t representing the number of bytes read as a result of the
* call to read.
*
* \throw serial::PortNotOpenedException
* \throw serial::SerialException
*/
size_t
read (std::vector<uint8_t> &buffer, size_t size = 1);
/*! Read a given amount of bytes from the serial port into a give buffer.
*
* \param buffer A reference to a std::string.
* \param size A size_t defining how many bytes to be read.
*
* \return A size_t representing the number of bytes read as a result of the
* call to read.
*
* \throw serial::PortNotOpenedException
* \throw serial::SerialException
*/
size_t
read (std::string &buffer, size_t size = 1);
/*! Read a given amount of bytes from the serial port and return a string
* containing the data.
*
* \param size A size_t defining how many bytes to be read.
*
* \return A std::string containing the data read from the port.
*
* \throw serial::PortNotOpenedException
* \throw serial::SerialException
*/
std::string
read (size_t size = 1);
/*! Reads in a line or until a given delimiter has been processed.
*
* Reads from the serial port until a single line has been read.
*
* \param buffer A std::string reference used to store the data.
* \param size A maximum length of a line, defaults to 65536 (2^16)
* \param eol A string to match against for the EOL.
*
* \return A size_t representing the number of bytes read.
*
* \throw serial::PortNotOpenedException
* \throw serial::SerialException
*/
size_t
readline (std::string &buffer, size_t size = 65536, std::string eol = "\n");
/*! Reads in a line or until a given delimiter has been processed.
*
* Reads from the serial port until a single line has been read.
*
* \param size A maximum length of a line, defaults to 65536 (2^16)
* \param eol A string to match against for the EOL.
*
* \return A std::string containing the line.
*
* \throw serial::PortNotOpenedException
* \throw serial::SerialException
*/
std::string
readline (size_t size = 65536, std::string eol = "\n");
/*! Reads in multiple lines until the serial port times out.
*
* This requires a timeout > 0 before it can be run. It will read until a
* timeout occurs and return a list of strings.
*
* \param size A maximum length of combined lines, defaults to 65536 (2^16)
*
* \param eol A string to match against for the EOL.
*
* \return A vector<string> containing the lines.
*
* \throw serial::PortNotOpenedException
* \throw serial::SerialException
*/
std::vector<std::string>
readlines (size_t size = 65536, std::string eol = "\n");
/*! Write a string to the serial port.
*
* \param data A const reference containing the data to be written
* to the serial port.
*
* \param size A size_t that indicates how many bytes should be written from
* the given data buffer.
*
* \return A size_t representing the number of bytes actually written to
* the serial port.
*
* \throw serial::PortNotOpenedException
* \throw serial::SerialException
* \throw serial::IOException
*/
size_t
write (const uint8_t *data, size_t size);
/*! Write a string to the serial port.
*
* \param data A const reference containing the data to be written
* to the serial port.
*
* \return A size_t representing the number of bytes actually written to
* the serial port.
*
* \throw serial::PortNotOpenedException
* \throw serial::SerialException
* \throw serial::IOException
*/
size_t
write (const std::vector<uint8_t> &data);
/*! Write a string to the serial port.
*
* \param data A const reference containing the data to be written
* to the serial port.
*
* \return A size_t representing the number of bytes actually written to
* the serial port.
*
* \throw serial::PortNotOpenedException
* \throw serial::SerialException
* \throw serial::IOException
*/
size_t
write (const std::string &data);
/*! Sets the serial port identifier.
*
* \param port A const std::string reference containing the address of the
* serial port, which would be something like 'COM1' on Windows and
* '/dev/ttyS0' on Linux.
*
* \throw std::invalid_argument
*/
void
setPort (const std::string &port);
/*! Gets the serial port identifier.
*
* \see Serial::setPort
*
* \throw std::invalid_argument
*/
std::string
getPort () const;
/*! Sets the timeout for reads and writes using the Timeout struct.
*
* There are two timeout conditions described here:
* * The inter byte timeout:
* * The inter_byte_timeout component of serial::Timeout defines the
* maximum amount of time, in milliseconds, between receiving bytes on
* the serial port that can pass before a timeout occurs. Setting this
* to zero will prevent inter byte timeouts from occurring.
* * Total time timeout:
* * The constant and multiplier component of this timeout condition,
* for both read and write, are defined in serial::Timeout. This
* timeout occurs if the total time since the read or write call was
* made exceeds the specified time in milliseconds.
* * The limit is defined by multiplying the multiplier component by the
* number of requested bytes and adding that product to the constant
* component. In this way if you want a read call, for example, to
* timeout after exactly one second regardless of the number of bytes
* you asked for then set the read_timeout_constant component of
* serial::Timeout to 1000 and the read_timeout_multiplier to zero.
* This timeout condition can be used in conjunction with the inter
* byte timeout condition with out any problems, timeout will simply
* occur when one of the two timeout conditions is met. This allows
* users to have maximum control over the trade-off between
* responsiveness and efficiency.
*
* Read and write functions will return in one of three cases. When the
* reading or writing is complete, when a timeout occurs, or when an
* exception occurs.
*
* A timeout of 0 enables non-blocking mode.
*
* \param timeout A serial::Timeout struct containing the inter byte
* timeout, and the read and write timeout constants and multipliers.
*
* \see serial::Timeout
*/
void
setTimeout (Timeout &timeout);
/*! Sets the timeout for reads and writes. */
void
setTimeout (uint32_t inter_byte_timeout, uint32_t read_timeout_constant,
uint32_t read_timeout_multiplier, uint32_t write_timeout_constant,
uint32_t write_timeout_multiplier)
{
Timeout timeout(inter_byte_timeout, read_timeout_constant,
read_timeout_multiplier, write_timeout_constant,
write_timeout_multiplier);
return setTimeout(timeout);
}
/*! Gets the timeout for reads in seconds.
*
* \return A Timeout struct containing the inter_byte_timeout, and read
* and write timeout constants and multipliers.
*
* \see Serial::setTimeout
*/
Timeout
getTimeout () const;
/*! Sets the baudrate for the serial port.
*
* Possible baudrates depends on the system but some safe baudrates include:
* 110, 300, 600, 1200, 2400, 4800, 9600, 14400, 19200, 28800, 38400, 56000,
* 57600, 115200
* Some other baudrates that are supported by some comports:
* 128000, 153600, 230400, 256000, 460800, 500000, 921600
*
* \param baudrate An integer that sets the baud rate for the serial port.
*
* \throw std::invalid_argument
*/
void
setBaudrate (uint32_t baudrate);
/*! Gets the baudrate for the serial port.
*
* \return An integer that sets the baud rate for the serial port.
*
* \see Serial::setBaudrate
*
* \throw std::invalid_argument
*/
uint32_t
getBaudrate () const;
/*! Sets the bytesize for the serial port.
*
* \param bytesize Size of each byte in the serial transmission of data,
* default is eightbits, possible values are: fivebits, sixbits, sevenbits,
* eightbits
*
* \throw std::invalid_argument
*/
void
setBytesize (bytesize_t bytesize);
/*! Gets the bytesize for the serial port.
*
* \see Serial::setBytesize
*
* \throw std::invalid_argument
*/
bytesize_t
getBytesize () const;
/*! Sets the parity for the serial port.
*
* \param parity Method of parity, default is parity_none, possible values
* are: parity_none, parity_odd, parity_even
*
* \throw std::invalid_argument
*/
void
setParity (parity_t parity);
/*! Gets the parity for the serial port.
*
* \see Serial::setParity
*
* \throw std::invalid_argument
*/
parity_t
getParity () const;
/*! Sets the stopbits for the serial port.
*
* \param stopbits Number of stop bits used, default is stopbits_one,
* possible values are: stopbits_one, stopbits_one_point_five, stopbits_two
*
* \throw std::invalid_argument
*/
void
setStopbits (stopbits_t stopbits);
/*! Gets the stopbits for the serial port.
*
* \see Serial::setStopbits
*
* \throw std::invalid_argument
*/
stopbits_t
getStopbits () const;
/*! Sets the flow control for the serial port.
*
* \param flowcontrol Type of flowcontrol used, default is flowcontrol_none,
* possible values are: flowcontrol_none, flowcontrol_software,
* flowcontrol_hardware
*
* \throw std::invalid_argument
*/
void
setFlowcontrol (flowcontrol_t flowcontrol);
/*! Gets the flow control for the serial port.
*
* \see Serial::setFlowcontrol
*
* \throw std::invalid_argument
*/
flowcontrol_t
getFlowcontrol () const;
/*! Flush the input and output buffers */
void
flush ();
/*! Flush only the input buffer */
void
flushInput ();
/*! Flush only the output buffer */
void
flushOutput ();
/*! Sends the RS-232 break signal. See tcsendbreak(3). */
void
sendBreak (int duration);
/*! Set the break condition to a given level. Defaults to true. */
void
setBreak (bool level = true);
/*! Set the RTS handshaking line to the given level. Defaults to true. */
void
setRTS (bool level = true);
/*! Set the DTR handshaking line to the given level. Defaults to true. */
void
setDTR (bool level = true);
/*!
* Blocks until CTS, DSR, RI, CD changes or something interrupts it.
*
* Can throw an exception if an error occurs while waiting.
* You can check the status of CTS, DSR, RI, and CD once this returns.
* Uses TIOCMIWAIT via ioctl if available (mostly only on Linux) with a
* resolution of less than +-1ms and as good as +-0.2ms. Otherwise a
* polling method is used which can give +-2ms.
*
* \return Returns true if one of the lines changed, false if something else
* occurred.
*
* \throw SerialException
*/
bool
waitForChange ();
/*! Returns the current status of the CTS line. */
bool
getCTS ();
/*! Returns the current status of the DSR line. */
bool
getDSR ();
/*! Returns the current status of the RI line. */
bool
getRI ();
/*! Returns the current status of the CD line. */
bool
getCD ();
private:
// Disable copy constructors
Serial(const Serial&);
Serial& operator=(const Serial&);
// Pimpl idiom, d_pointer
class SerialImpl;
SerialImpl *pimpl_;
// Scoped Lock Classes
class ScopedReadLock;
class ScopedWriteLock;
// Read common function
size_t
read_ (uint8_t *buffer, size_t size);
// Write common function
size_t
write_ (const uint8_t *data, size_t length);
};
class SerialException : public std::exception
{
// Disable copy constructors
SerialException& operator=(const SerialException&);
std::string e_what_;
public:
SerialException (const char *description) {
std::stringstream ss;
ss << "SerialException " << description << " failed.";
e_what_ = ss.str();
}
SerialException (const SerialException& other) : e_what_(other.e_what_) {}
virtual ~SerialException() throw() {}
virtual const char* what () const throw () {
return e_what_.c_str();
}
};
class IOException : public std::exception
{
// Disable copy constructors
IOException& operator=(const IOException&);
std::string file_;
int line_;
std::string e_what_;
int errno_;
public:
explicit IOException (std::string file, int line, int errnum)
: file_(file), line_(line), errno_(errnum) {
std::stringstream ss;
#if defined(_WIN32) && !defined(__MINGW32__)
char error_str [1024];
strerror_s(error_str, 1024, errnum);
#else
char * error_str = strerror(errnum);
#endif
ss << "IO Exception (" << errno_ << "): " << error_str;
ss << ", file " << file_ << ", line " << line_ << ".";
e_what_ = ss.str();
}
explicit IOException (std::string file, int line, const char * description)
: file_(file), line_(line), errno_(0) {
std::stringstream ss;
ss << "IO Exception: " << description;
ss << ", file " << file_ << ", line " << line_ << ".";
e_what_ = ss.str();
}
virtual ~IOException() throw() {}
IOException (const IOException& other) : line_(other.line_), e_what_(other.e_what_), errno_(other.errno_) {}
int getErrorNumber () const { return errno_; }
virtual const char* what () const throw () {
return e_what_.c_str();
}
};
class PortNotOpenedException : public std::exception
{
// Disable copy constructors
const PortNotOpenedException& operator=(PortNotOpenedException);
std::string e_what_;
public:
PortNotOpenedException (const char * description) {
std::stringstream ss;
ss << "PortNotOpenedException " << description << " failed.";
e_what_ = ss.str();
}
PortNotOpenedException (const PortNotOpenedException& other) : e_what_(other.e_what_) {}
virtual ~PortNotOpenedException() throw() {}
virtual const char* what () const throw () {
return e_what_.c_str();
}
};
/*!
* Structure that describes a serial device.
*/
struct PortInfo {
/*! Address of the serial port (this can be passed to the constructor of Serial). */
std::string port;
/*! Human readable description of serial device if available. */
std::string description;
/*! Hardware ID (e.g. VID:PID of USB serial devices) or "n/a" if not available. */
std::string hardware_id;
};
/* Lists the serial ports available on the system
*
* Returns a vector of available serial ports, each represented
* by a serial::PortInfo data structure:
*
* \return vector of serial::PortInfo.
*/
std::vector<PortInfo>
list_ports();
} // namespace serial
#endif

View File

@ -0,0 +1,57 @@
// This header is from the v8 google project:
// http://code.google.com/p/v8/source/browse/trunk/include/v8stdint.h
// Copyright 2012 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Load definitions of standard types.
#ifndef V8STDINT_H_
#define V8STDINT_H_
#include <stddef.h>
#include <stdio.h>
#if defined(_WIN32) && !defined(__MINGW32__)
typedef signed char int8_t;
typedef unsigned char uint8_t;
typedef short int16_t; // NOLINT
typedef unsigned short uint16_t; // NOLINT
typedef int int32_t;
typedef unsigned int uint32_t;
typedef __int64 int64_t;
typedef unsigned __int64 uint64_t;
// intptr_t and friends are defined in crtdefs.h through stdio.h.
#else
#include <stdint.h>
#endif
#endif // V8STDINT_H_

View File

@ -0,0 +1,336 @@
#if defined(__linux__)
/*
* Copyright (c) 2014 Craig Lilley <cralilley@gmail.com>
* This software is made available under the terms of the MIT licence.
* A copy of the licence can be obtained from:
* http://opensource.org/licenses/MIT
*/
#include <vector>
#include <string>
#include <sstream>
#include <stdexcept>
#include <iostream>
#include <fstream>
#include <cstdio>
#include <cstdarg>
#include <cstdlib>
#include <glob.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include "serial/serial.h"
using serial::PortInfo;
using std::istringstream;
using std::ifstream;
using std::getline;
using std::vector;
using std::string;
using std::cout;
using std::endl;
static vector<string> glob(const vector<string>& patterns);
static string basename(const string& path);
static string dirname(const string& path);
static bool path_exists(const string& path);
static string realpath(const string& path);
static string usb_sysfs_friendly_name(const string& sys_usb_path);
static vector<string> get_sysfs_info(const string& device_path);
static string read_line(const string& file);
static string usb_sysfs_hw_string(const string& sysfs_path);
static string format(const char* format, ...);
vector<string>
glob(const vector<string>& patterns)
{
vector<string> paths_found;
if(patterns.size() == 0)
return paths_found;
glob_t glob_results;
int glob_retval = glob(patterns[0].c_str(), 0, NULL, &glob_results);
vector<string>::const_iterator iter = patterns.begin();
while(++iter != patterns.end())
{
glob_retval = glob(iter->c_str(), GLOB_APPEND, NULL, &glob_results);
}
for(int path_index = 0; path_index < glob_results.gl_pathc; path_index++)
{
paths_found.push_back(glob_results.gl_pathv[path_index]);
}
globfree(&glob_results);
return paths_found;
}
string
basename(const string& path)
{
size_t pos = path.rfind("/");
if(pos == std::string::npos)
return path;
return string(path, pos+1, string::npos);
}
string
dirname(const string& path)
{
size_t pos = path.rfind("/");
if(pos == std::string::npos)
return path;
else if(pos == 0)
return "/";
return string(path, 0, pos);
}
bool
path_exists(const string& path)
{
struct stat sb;
if( stat(path.c_str(), &sb ) == 0 )
return true;
return false;
}
string
realpath(const string& path)
{
char* real_path = realpath(path.c_str(), NULL);
string result;
if(real_path != NULL)
{
result = real_path;
free(real_path);
}
return result;
}
string
usb_sysfs_friendly_name(const string& sys_usb_path)
{
unsigned int device_number = 0;
istringstream( read_line(sys_usb_path + "/devnum") ) >> device_number;
string manufacturer = read_line( sys_usb_path + "/manufacturer" );
string product = read_line( sys_usb_path + "/product" );
string serial = read_line( sys_usb_path + "/serial" );
if( manufacturer.empty() && product.empty() && serial.empty() )
return "";
return format("%s %s %s", manufacturer.c_str(), product.c_str(), serial.c_str() );
}
vector<string>
get_sysfs_info(const string& device_path)
{
string device_name = basename( device_path );
string friendly_name;
string hardware_id;
string sys_device_path = format( "/sys/class/tty/%s/device", device_name.c_str() );
if( device_name.compare(0,6,"ttyUSB") == 0 )
{
sys_device_path = dirname( dirname( realpath( sys_device_path ) ) );
if( path_exists( sys_device_path ) )
{
friendly_name = usb_sysfs_friendly_name( sys_device_path );
hardware_id = usb_sysfs_hw_string( sys_device_path );
}
}
else if( device_name.compare(0,6,"ttyACM") == 0 )
{
sys_device_path = dirname( realpath( sys_device_path ) );
if( path_exists( sys_device_path ) )
{
friendly_name = usb_sysfs_friendly_name( sys_device_path );
hardware_id = usb_sysfs_hw_string( sys_device_path );
}
}
else
{
// Try to read ID string of PCI device
string sys_id_path = sys_device_path + "/id";
if( path_exists( sys_id_path ) )
hardware_id = read_line( sys_id_path );
}
if( friendly_name.empty() )
friendly_name = device_name;
if( hardware_id.empty() )
hardware_id = "n/a";
vector<string> result;
result.push_back(friendly_name);
result.push_back(hardware_id);
return result;
}
string
read_line(const string& file)
{
ifstream ifs(file.c_str(), ifstream::in);
string line;
if(ifs)
{
getline(ifs, line);
}
return line;
}
string
format(const char* format, ...)
{
va_list ap;
size_t buffer_size_bytes = 256;
string result;
char* buffer = (char*)malloc(buffer_size_bytes);
if( buffer == NULL )
return result;
bool done = false;
unsigned int loop_count = 0;
while(!done)
{
va_start(ap, format);
int return_value = vsnprintf(buffer, buffer_size_bytes, format, ap);
if( return_value < 0 )
{
done = true;
}
else if( return_value >= buffer_size_bytes )
{
// Realloc and try again.
buffer_size_bytes = return_value + 1;
char* new_buffer_ptr = (char*)realloc(buffer, buffer_size_bytes);
if( new_buffer_ptr == NULL )
{
done = true;
}
else
{
buffer = new_buffer_ptr;
}
}
else
{
result = buffer;
done = true;
}
va_end(ap);
if( ++loop_count > 5 )
done = true;
}
free(buffer);
return result;
}
string
usb_sysfs_hw_string(const string& sysfs_path)
{
string serial_number = read_line( sysfs_path + "/serial" );
if( serial_number.length() > 0 )
{
serial_number = format( "SNR=%s", serial_number.c_str() );
}
string vid = read_line( sysfs_path + "/idVendor" );
string pid = read_line( sysfs_path + "/idProduct" );
return format("USB VID:PID=%s:%s %s", vid.c_str(), pid.c_str(), serial_number.c_str() );
}
vector<PortInfo>
serial::list_ports()
{
vector<PortInfo> results;
vector<string> search_globs;
search_globs.push_back("/dev/ttyACM*");
search_globs.push_back("/dev/ttyS*");
search_globs.push_back("/dev/ttyUSB*");
search_globs.push_back("/dev/tty.*");
search_globs.push_back("/dev/cu.*");
search_globs.push_back("/dev/rfcomm*");
vector<string> devices_found = glob( search_globs );
vector<string>::iterator iter = devices_found.begin();
while( iter != devices_found.end() )
{
string device = *iter++;
vector<string> sysfs_info = get_sysfs_info( device );
string friendly_name = sysfs_info[0];
string hardware_id = sysfs_info[1];
PortInfo device_entry;
device_entry.port = device;
device_entry.description = friendly_name;
device_entry.hardware_id = hardware_id;
results.push_back( device_entry );
}
return results;
}
#endif // defined(__linux__)

View File

@ -0,0 +1,286 @@
#if defined(__APPLE__)
#include <sys/param.h>
#include <stdint.h>
#include <CoreFoundation/CoreFoundation.h>
#include <IOKit/IOKitLib.h>
#include <IOKit/serial/IOSerialKeys.h>
#include <IOKit/IOBSD.h>
#include <iostream>
#include <string>
#include <vector>
#include "serial/serial.h"
using serial::PortInfo;
using std::string;
using std::vector;
#define HARDWARE_ID_STRING_LENGTH 128
string cfstring_to_string( CFStringRef cfstring );
string get_device_path( io_object_t& serial_port );
string get_class_name( io_object_t& obj );
io_registry_entry_t get_parent_iousb_device( io_object_t& serial_port );
string get_string_property( io_object_t& device, const char* property );
uint16_t get_int_property( io_object_t& device, const char* property );
string rtrim(const string& str);
string
cfstring_to_string( CFStringRef cfstring )
{
char cstring[MAXPATHLEN];
string result;
if( cfstring )
{
Boolean success = CFStringGetCString( cfstring,
cstring,
sizeof(cstring),
kCFStringEncodingASCII );
if( success )
result = cstring;
}
return result;
}
string
get_device_path( io_object_t& serial_port )
{
CFTypeRef callout_path;
string device_path;
callout_path = IORegistryEntryCreateCFProperty( serial_port,
CFSTR(kIOCalloutDeviceKey),
kCFAllocatorDefault,
0 );
if (callout_path)
{
if( CFGetTypeID(callout_path) == CFStringGetTypeID() )
device_path = cfstring_to_string( static_cast<CFStringRef>(callout_path) );
CFRelease(callout_path);
}
return device_path;
}
string
get_class_name( io_object_t& obj )
{
string result;
io_name_t class_name;
kern_return_t kern_result;
kern_result = IOObjectGetClass( obj, class_name );
if( kern_result == KERN_SUCCESS )
result = class_name;
return result;
}
io_registry_entry_t
get_parent_iousb_device( io_object_t& serial_port )
{
io_object_t device = serial_port;
io_registry_entry_t parent = 0;
io_registry_entry_t result = 0;
kern_return_t kern_result = KERN_FAILURE;
string name = get_class_name(device);
// Walk the IO Registry tree looking for this devices parent IOUSBDevice.
while( name != "IOUSBDevice" )
{
kern_result = IORegistryEntryGetParentEntry( device,
kIOServicePlane,
&parent );
if(kern_result != KERN_SUCCESS)
{
result = 0;
break;
}
device = parent;
name = get_class_name(device);
}
if(kern_result == KERN_SUCCESS)
result = device;
return result;
}
string
get_string_property( io_object_t& device, const char* property )
{
string property_name;
if( device )
{
CFStringRef property_as_cfstring = CFStringCreateWithCString (
kCFAllocatorDefault,
property,
kCFStringEncodingASCII );
CFTypeRef name_as_cfstring = IORegistryEntryCreateCFProperty(
device,
property_as_cfstring,
kCFAllocatorDefault,
0 );
if( name_as_cfstring )
{
if( CFGetTypeID(name_as_cfstring) == CFStringGetTypeID() )
property_name = cfstring_to_string( static_cast<CFStringRef>(name_as_cfstring) );
CFRelease(name_as_cfstring);
}
if(property_as_cfstring)
CFRelease(property_as_cfstring);
}
return property_name;
}
uint16_t
get_int_property( io_object_t& device, const char* property )
{
uint16_t result = 0;
if( device )
{
CFStringRef property_as_cfstring = CFStringCreateWithCString (
kCFAllocatorDefault,
property,
kCFStringEncodingASCII );
CFTypeRef number = IORegistryEntryCreateCFProperty( device,
property_as_cfstring,
kCFAllocatorDefault,
0 );
if(property_as_cfstring)
CFRelease(property_as_cfstring);
if( number )
{
if( CFGetTypeID(number) == CFNumberGetTypeID() )
{
bool success = CFNumberGetValue( static_cast<CFNumberRef>(number),
kCFNumberSInt16Type,
&result );
if( !success )
result = 0;
}
CFRelease(number);
}
}
return result;
}
string rtrim(const string& str)
{
string result = str;
string whitespace = " \t\f\v\n\r";
std::size_t found = result.find_last_not_of(whitespace);
if (found != std::string::npos)
result.erase(found+1);
else
result.clear();
return result;
}
vector<PortInfo>
serial::list_ports(void)
{
vector<PortInfo> devices_found;
CFMutableDictionaryRef classes_to_match;
io_iterator_t serial_port_iterator;
io_object_t serial_port;
mach_port_t master_port;
kern_return_t kern_result;
kern_result = IOMasterPort(MACH_PORT_NULL, &master_port);
if(kern_result != KERN_SUCCESS)
return devices_found;
classes_to_match = IOServiceMatching(kIOSerialBSDServiceValue);
if (classes_to_match == NULL)
return devices_found;
CFDictionarySetValue( classes_to_match,
CFSTR(kIOSerialBSDTypeKey),
CFSTR(kIOSerialBSDAllTypes) );
kern_result = IOServiceGetMatchingServices(master_port, classes_to_match, &serial_port_iterator);
if (KERN_SUCCESS != kern_result)
return devices_found;
while ( (serial_port = IOIteratorNext(serial_port_iterator)) )
{
string device_path = get_device_path( serial_port );
io_registry_entry_t parent = get_parent_iousb_device( serial_port );
IOObjectRelease(serial_port);
if( device_path.empty() )
continue;
PortInfo port_info;
port_info.port = device_path;
port_info.description = "n/a";
port_info.hardware_id = "n/a";
string device_name = rtrim( get_string_property( parent, "USB Product Name" ) );
string vendor_name = rtrim( get_string_property( parent, "USB Vendor Name") );
string description = rtrim( vendor_name + " " + device_name );
if( !description.empty() )
port_info.description = description;
string serial_number = rtrim(get_string_property( parent, "USB Serial Number" ) );
uint16_t vendor_id = get_int_property( parent, "idVendor" );
uint16_t product_id = get_int_property( parent, "idProduct" );
if( vendor_id && product_id )
{
char cstring[HARDWARE_ID_STRING_LENGTH];
if(serial_number.empty())
serial_number = "None";
int ret = snprintf( cstring, HARDWARE_ID_STRING_LENGTH, "USB VID:PID=%04x:%04x SNR=%s",
vendor_id,
product_id,
serial_number.c_str() );
if( (ret >= 0) && (ret < HARDWARE_ID_STRING_LENGTH) )
port_info.hardware_id = cstring;
}
devices_found.push_back(port_info);
}
IOObjectRelease(serial_port_iterator);
return devices_found;
}
#endif // defined(__APPLE__)

View File

@ -0,0 +1,152 @@
#if defined(_WIN32)
/*
* Copyright (c) 2014 Craig Lilley <cralilley@gmail.com>
* This software is made available under the terms of the MIT licence.
* A copy of the licence can be obtained from:
* http://opensource.org/licenses/MIT
*/
#include "serial/serial.h"
#include <tchar.h>
#include <windows.h>
#include <setupapi.h>
#include <initguid.h>
#include <devguid.h>
#include <cstring>
using serial::PortInfo;
using std::vector;
using std::string;
static const DWORD port_name_max_length = 256;
static const DWORD friendly_name_max_length = 256;
static const DWORD hardware_id_max_length = 256;
// Convert a wide Unicode string to an UTF8 string
std::string utf8_encode(const std::wstring &wstr)
{
int size_needed = WideCharToMultiByte(CP_UTF8, 0, &wstr[0], (int)wstr.size(), NULL, 0, NULL, NULL);
std::string strTo( size_needed, 0 );
WideCharToMultiByte (CP_UTF8, 0, &wstr[0], (int)wstr.size(), &strTo[0], size_needed, NULL, NULL);
return strTo;
}
vector<PortInfo>
serial::list_ports()
{
vector<PortInfo> devices_found;
HDEVINFO device_info_set = SetupDiGetClassDevs(
(const GUID *) &GUID_DEVCLASS_PORTS,
NULL,
NULL,
DIGCF_PRESENT);
unsigned int device_info_set_index = 0;
SP_DEVINFO_DATA device_info_data;
device_info_data.cbSize = sizeof(SP_DEVINFO_DATA);
while(SetupDiEnumDeviceInfo(device_info_set, device_info_set_index, &device_info_data))
{
device_info_set_index++;
// Get port name
HKEY hkey = SetupDiOpenDevRegKey(
device_info_set,
&device_info_data,
DICS_FLAG_GLOBAL,
0,
DIREG_DEV,
KEY_READ);
TCHAR port_name[port_name_max_length];
DWORD port_name_length = port_name_max_length;
LONG return_code = RegQueryValueEx(
hkey,
_T("PortName"),
NULL,
NULL,
(LPBYTE)port_name,
&port_name_length);
RegCloseKey(hkey);
if(return_code != EXIT_SUCCESS)
continue;
if(port_name_length > 0 && port_name_length <= port_name_max_length)
port_name[port_name_length-1] = '\0';
else
port_name[0] = '\0';
// Ignore parallel ports
if(_tcsstr(port_name, _T("LPT")) != NULL)
continue;
// Get port friendly name
TCHAR friendly_name[friendly_name_max_length];
DWORD friendly_name_actual_length = 0;
BOOL got_friendly_name = SetupDiGetDeviceRegistryProperty(
device_info_set,
&device_info_data,
SPDRP_FRIENDLYNAME,
NULL,
(PBYTE)friendly_name,
friendly_name_max_length,
&friendly_name_actual_length);
if(got_friendly_name == TRUE && friendly_name_actual_length > 0)
friendly_name[friendly_name_actual_length-1] = '\0';
else
friendly_name[0] = '\0';
// Get hardware ID
TCHAR hardware_id[hardware_id_max_length];
DWORD hardware_id_actual_length = 0;
BOOL got_hardware_id = SetupDiGetDeviceRegistryProperty(
device_info_set,
&device_info_data,
SPDRP_HARDWAREID,
NULL,
(PBYTE)hardware_id,
hardware_id_max_length,
&hardware_id_actual_length);
if(got_hardware_id == TRUE && hardware_id_actual_length > 0)
hardware_id[hardware_id_actual_length-1] = '\0';
else
hardware_id[0] = '\0';
#ifdef UNICODE
std::string portName = utf8_encode(port_name);
std::string friendlyName = utf8_encode(friendly_name);
std::string hardwareId = utf8_encode(hardware_id);
#else
std::string portName = port_name;
std::string friendlyName = friendly_name;
std::string hardwareId = hardware_id;
#endif
PortInfo port_entry;
port_entry.port = portName;
port_entry.description = friendlyName;
port_entry.hardware_id = hardwareId;
devices_found.push_back(port_entry);
}
SetupDiDestroyDeviceInfoList(device_info_set);
return devices_found;
}
#endif // #if defined(_WIN32)

1084
lib/serial/src/impl/unix.cc Executable file

File diff suppressed because it is too large Load Diff

646
lib/serial/src/impl/win.cc Normal file
View File

@ -0,0 +1,646 @@
#if defined(_WIN32)
/* Copyright 2012 William Woodall and John Harrison */
#include <sstream>
#include "serial/impl/win.h"
using std::string;
using std::wstring;
using std::stringstream;
using std::invalid_argument;
using serial::Serial;
using serial::Timeout;
using serial::bytesize_t;
using serial::parity_t;
using serial::stopbits_t;
using serial::flowcontrol_t;
using serial::SerialException;
using serial::PortNotOpenedException;
using serial::IOException;
inline wstring
_prefix_port_if_needed(const wstring &input)
{
static wstring windows_com_port_prefix = L"\\\\.\\";
if (input.compare(0, windows_com_port_prefix.size(), windows_com_port_prefix) != 0)
{
return windows_com_port_prefix + input;
}
return input;
}
Serial::SerialImpl::SerialImpl (const string &port, unsigned long baudrate,
bytesize_t bytesize,
parity_t parity, stopbits_t stopbits,
flowcontrol_t flowcontrol)
: port_ (port.begin(), port.end()), fd_ (INVALID_HANDLE_VALUE), is_open_ (false),
baudrate_ (baudrate), parity_ (parity),
bytesize_ (bytesize), stopbits_ (stopbits), flowcontrol_ (flowcontrol)
{
if (port_.empty () == false)
open ();
read_mutex = CreateMutex(NULL, false, NULL);
write_mutex = CreateMutex(NULL, false, NULL);
}
Serial::SerialImpl::~SerialImpl ()
{
this->close();
CloseHandle(read_mutex);
CloseHandle(write_mutex);
}
void
Serial::SerialImpl::open ()
{
if (port_.empty ()) {
throw invalid_argument ("Empty port is invalid.");
}
if (is_open_ == true) {
throw SerialException ("Serial port already open.");
}
// See: https://github.com/wjwwood/serial/issues/84
wstring port_with_prefix = _prefix_port_if_needed(port_);
LPCWSTR lp_port = port_with_prefix.c_str();
fd_ = CreateFileW(lp_port,
GENERIC_READ | GENERIC_WRITE,
0,
0,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
0);
if (fd_ == INVALID_HANDLE_VALUE) {
DWORD create_file_err = GetLastError();
stringstream ss;
switch (create_file_err) {
case ERROR_FILE_NOT_FOUND:
// Use this->getPort to convert to a std::string
ss << "Specified port, " << this->getPort() << ", does not exist.";
THROW (IOException, ss.str().c_str());
default:
ss << "Unknown error opening the serial port: " << create_file_err;
THROW (IOException, ss.str().c_str());
}
}
reconfigurePort();
is_open_ = true;
}
void
Serial::SerialImpl::reconfigurePort ()
{
if (fd_ == INVALID_HANDLE_VALUE) {
// Can only operate on a valid file descriptor
THROW (IOException, "Invalid file descriptor, is the serial port open?");
}
DCB dcbSerialParams = {0};
dcbSerialParams.DCBlength=sizeof(dcbSerialParams);
if (!GetCommState(fd_, &dcbSerialParams)) {
//error getting state
THROW (IOException, "Error getting the serial port state.");
}
// setup baud rate
switch (baudrate_) {
#ifdef CBR_0
case 0: dcbSerialParams.BaudRate = CBR_0; break;
#endif
#ifdef CBR_50
case 50: dcbSerialParams.BaudRate = CBR_50; break;
#endif
#ifdef CBR_75
case 75: dcbSerialParams.BaudRate = CBR_75; break;
#endif
#ifdef CBR_110
case 110: dcbSerialParams.BaudRate = CBR_110; break;
#endif
#ifdef CBR_134
case 134: dcbSerialParams.BaudRate = CBR_134; break;
#endif
#ifdef CBR_150
case 150: dcbSerialParams.BaudRate = CBR_150; break;
#endif
#ifdef CBR_200
case 200: dcbSerialParams.BaudRate = CBR_200; break;
#endif
#ifdef CBR_300
case 300: dcbSerialParams.BaudRate = CBR_300; break;
#endif
#ifdef CBR_600
case 600: dcbSerialParams.BaudRate = CBR_600; break;
#endif
#ifdef CBR_1200
case 1200: dcbSerialParams.BaudRate = CBR_1200; break;
#endif
#ifdef CBR_1800
case 1800: dcbSerialParams.BaudRate = CBR_1800; break;
#endif
#ifdef CBR_2400
case 2400: dcbSerialParams.BaudRate = CBR_2400; break;
#endif
#ifdef CBR_4800
case 4800: dcbSerialParams.BaudRate = CBR_4800; break;
#endif
#ifdef CBR_7200
case 7200: dcbSerialParams.BaudRate = CBR_7200; break;
#endif
#ifdef CBR_9600
case 9600: dcbSerialParams.BaudRate = CBR_9600; break;
#endif
#ifdef CBR_14400
case 14400: dcbSerialParams.BaudRate = CBR_14400; break;
#endif
#ifdef CBR_19200
case 19200: dcbSerialParams.BaudRate = CBR_19200; break;
#endif
#ifdef CBR_28800
case 28800: dcbSerialParams.BaudRate = CBR_28800; break;
#endif
#ifdef CBR_57600
case 57600: dcbSerialParams.BaudRate = CBR_57600; break;
#endif
#ifdef CBR_76800
case 76800: dcbSerialParams.BaudRate = CBR_76800; break;
#endif
#ifdef CBR_38400
case 38400: dcbSerialParams.BaudRate = CBR_38400; break;
#endif
#ifdef CBR_115200
case 115200: dcbSerialParams.BaudRate = CBR_115200; break;
#endif
#ifdef CBR_128000
case 128000: dcbSerialParams.BaudRate = CBR_128000; break;
#endif
#ifdef CBR_153600
case 153600: dcbSerialParams.BaudRate = CBR_153600; break;
#endif
#ifdef CBR_230400
case 230400: dcbSerialParams.BaudRate = CBR_230400; break;
#endif
#ifdef CBR_256000
case 256000: dcbSerialParams.BaudRate = CBR_256000; break;
#endif
#ifdef CBR_460800
case 460800: dcbSerialParams.BaudRate = CBR_460800; break;
#endif
#ifdef CBR_921600
case 921600: dcbSerialParams.BaudRate = CBR_921600; break;
#endif
default:
// Try to blindly assign it
dcbSerialParams.BaudRate = baudrate_;
}
// setup char len
if (bytesize_ == eightbits)
dcbSerialParams.ByteSize = 8;
else if (bytesize_ == sevenbits)
dcbSerialParams.ByteSize = 7;
else if (bytesize_ == sixbits)
dcbSerialParams.ByteSize = 6;
else if (bytesize_ == fivebits)
dcbSerialParams.ByteSize = 5;
else
throw invalid_argument ("invalid char len");
// setup stopbits
if (stopbits_ == stopbits_one)
dcbSerialParams.StopBits = ONESTOPBIT;
else if (stopbits_ == stopbits_one_point_five)
dcbSerialParams.StopBits = ONE5STOPBITS;
else if (stopbits_ == stopbits_two)
dcbSerialParams.StopBits = TWOSTOPBITS;
else
throw invalid_argument ("invalid stop bit");
// setup parity
if (parity_ == parity_none) {
dcbSerialParams.Parity = NOPARITY;
} else if (parity_ == parity_even) {
dcbSerialParams.Parity = EVENPARITY;
} else if (parity_ == parity_odd) {
dcbSerialParams.Parity = ODDPARITY;
} else if (parity_ == parity_mark) {
dcbSerialParams.Parity = MARKPARITY;
} else if (parity_ == parity_space) {
dcbSerialParams.Parity = SPACEPARITY;
} else {
throw invalid_argument ("invalid parity");
}
// setup flowcontrol
if (flowcontrol_ == flowcontrol_none) {
dcbSerialParams.fOutxCtsFlow = false;
dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
dcbSerialParams.fOutX = false;
dcbSerialParams.fInX = false;
}
if (flowcontrol_ == flowcontrol_software) {
dcbSerialParams.fOutxCtsFlow = false;
dcbSerialParams.fRtsControl = RTS_CONTROL_DISABLE;
dcbSerialParams.fOutX = true;
dcbSerialParams.fInX = true;
}
if (flowcontrol_ == flowcontrol_hardware) {
dcbSerialParams.fOutxCtsFlow = true;
dcbSerialParams.fRtsControl = RTS_CONTROL_HANDSHAKE;
dcbSerialParams.fOutX = false;
dcbSerialParams.fInX = false;
}
// activate settings
if (!SetCommState(fd_, &dcbSerialParams)){
CloseHandle(fd_);
THROW (IOException, "Error setting serial port settings.");
}
// Setup timeouts
COMMTIMEOUTS timeouts = {0};
timeouts.ReadIntervalTimeout = timeout_.inter_byte_timeout;
timeouts.ReadTotalTimeoutConstant = timeout_.read_timeout_constant;
timeouts.ReadTotalTimeoutMultiplier = timeout_.read_timeout_multiplier;
timeouts.WriteTotalTimeoutConstant = timeout_.write_timeout_constant;
timeouts.WriteTotalTimeoutMultiplier = timeout_.write_timeout_multiplier;
if (!SetCommTimeouts(fd_, &timeouts)) {
THROW (IOException, "Error setting timeouts.");
}
}
void
Serial::SerialImpl::close ()
{
if (is_open_ == true) {
if (fd_ != INVALID_HANDLE_VALUE) {
int ret;
ret = CloseHandle(fd_);
if (ret == 0) {
stringstream ss;
ss << "Error while closing serial port: " << GetLastError();
THROW (IOException, ss.str().c_str());
} else {
fd_ = INVALID_HANDLE_VALUE;
}
}
is_open_ = false;
}
}
bool
Serial::SerialImpl::isOpen () const
{
return is_open_;
}
size_t
Serial::SerialImpl::available ()
{
if (!is_open_) {
return 0;
}
COMSTAT cs;
if (!ClearCommError(fd_, NULL, &cs)) {
stringstream ss;
ss << "Error while checking status of the serial port: " << GetLastError();
THROW (IOException, ss.str().c_str());
}
return static_cast<size_t>(cs.cbInQue);
}
bool
Serial::SerialImpl::waitReadable (uint32_t /*timeout*/)
{
THROW (IOException, "waitReadable is not implemented on Windows.");
return false;
}
void
Serial::SerialImpl::waitByteTimes (size_t /*count*/)
{
THROW (IOException, "waitByteTimes is not implemented on Windows.");
}
size_t
Serial::SerialImpl::read (uint8_t *buf, size_t size)
{
if (!is_open_) {
throw PortNotOpenedException ("Serial::read");
}
DWORD bytes_read;
if (!ReadFile(fd_, buf, static_cast<DWORD>(size), &bytes_read, NULL)) {
stringstream ss;
ss << "Error while reading from the serial port: " << GetLastError();
THROW (IOException, ss.str().c_str());
}
return (size_t) (bytes_read);
}
size_t
Serial::SerialImpl::write (const uint8_t *data, size_t length)
{
if (is_open_ == false) {
throw PortNotOpenedException ("Serial::write");
}
DWORD bytes_written;
if (!WriteFile(fd_, data, static_cast<DWORD>(length), &bytes_written, NULL)) {
stringstream ss;
ss << "Error while writing to the serial port: " << GetLastError();
THROW (IOException, ss.str().c_str());
}
return (size_t) (bytes_written);
}
void
Serial::SerialImpl::setPort (const string &port)
{
port_ = wstring(port.begin(), port.end());
}
string
Serial::SerialImpl::getPort () const
{
return string(port_.begin(), port_.end());
}
void
Serial::SerialImpl::setTimeout (serial::Timeout &timeout)
{
timeout_ = timeout;
if (is_open_) {
reconfigurePort ();
}
}
serial::Timeout
Serial::SerialImpl::getTimeout () const
{
return timeout_;
}
void
Serial::SerialImpl::setBaudrate (unsigned long baudrate)
{
baudrate_ = baudrate;
if (is_open_) {
reconfigurePort ();
}
}
unsigned long
Serial::SerialImpl::getBaudrate () const
{
return baudrate_;
}
void
Serial::SerialImpl::setBytesize (serial::bytesize_t bytesize)
{
bytesize_ = bytesize;
if (is_open_) {
reconfigurePort ();
}
}
serial::bytesize_t
Serial::SerialImpl::getBytesize () const
{
return bytesize_;
}
void
Serial::SerialImpl::setParity (serial::parity_t parity)
{
parity_ = parity;
if (is_open_) {
reconfigurePort ();
}
}
serial::parity_t
Serial::SerialImpl::getParity () const
{
return parity_;
}
void
Serial::SerialImpl::setStopbits (serial::stopbits_t stopbits)
{
stopbits_ = stopbits;
if (is_open_) {
reconfigurePort ();
}
}
serial::stopbits_t
Serial::SerialImpl::getStopbits () const
{
return stopbits_;
}
void
Serial::SerialImpl::setFlowcontrol (serial::flowcontrol_t flowcontrol)
{
flowcontrol_ = flowcontrol;
if (is_open_) {
reconfigurePort ();
}
}
serial::flowcontrol_t
Serial::SerialImpl::getFlowcontrol () const
{
return flowcontrol_;
}
void
Serial::SerialImpl::flush ()
{
if (is_open_ == false) {
throw PortNotOpenedException ("Serial::flush");
}
FlushFileBuffers (fd_);
}
void
Serial::SerialImpl::flushInput ()
{
if (is_open_ == false) {
throw PortNotOpenedException("Serial::flushInput");
}
PurgeComm(fd_, PURGE_RXCLEAR);
}
void
Serial::SerialImpl::flushOutput ()
{
if (is_open_ == false) {
throw PortNotOpenedException("Serial::flushOutput");
}
PurgeComm(fd_, PURGE_TXCLEAR);
}
void
Serial::SerialImpl::sendBreak (int /*duration*/)
{
THROW (IOException, "sendBreak is not supported on Windows.");
}
void
Serial::SerialImpl::setBreak (bool level)
{
if (is_open_ == false) {
throw PortNotOpenedException ("Serial::setBreak");
}
if (level) {
EscapeCommFunction (fd_, SETBREAK);
} else {
EscapeCommFunction (fd_, CLRBREAK);
}
}
void
Serial::SerialImpl::setRTS (bool level)
{
if (is_open_ == false) {
throw PortNotOpenedException ("Serial::setRTS");
}
if (level) {
EscapeCommFunction (fd_, SETRTS);
} else {
EscapeCommFunction (fd_, CLRRTS);
}
}
void
Serial::SerialImpl::setDTR (bool level)
{
if (is_open_ == false) {
throw PortNotOpenedException ("Serial::setDTR");
}
if (level) {
EscapeCommFunction (fd_, SETDTR);
} else {
EscapeCommFunction (fd_, CLRDTR);
}
}
bool
Serial::SerialImpl::waitForChange ()
{
if (is_open_ == false) {
throw PortNotOpenedException ("Serial::waitForChange");
}
DWORD dwCommEvent;
if (!SetCommMask(fd_, EV_CTS | EV_DSR | EV_RING | EV_RLSD)) {
// Error setting communications mask
return false;
}
if (!WaitCommEvent(fd_, &dwCommEvent, NULL)) {
// An error occurred waiting for the event.
return false;
} else {
// Event has occurred.
return true;
}
}
bool
Serial::SerialImpl::getCTS ()
{
if (is_open_ == false) {
throw PortNotOpenedException ("Serial::getCTS");
}
DWORD dwModemStatus;
if (!GetCommModemStatus(fd_, &dwModemStatus)) {
THROW (IOException, "Error getting the status of the CTS line.");
}
return (MS_CTS_ON & dwModemStatus) != 0;
}
bool
Serial::SerialImpl::getDSR ()
{
if (is_open_ == false) {
throw PortNotOpenedException ("Serial::getDSR");
}
DWORD dwModemStatus;
if (!GetCommModemStatus(fd_, &dwModemStatus)) {
THROW (IOException, "Error getting the status of the DSR line.");
}
return (MS_DSR_ON & dwModemStatus) != 0;
}
bool
Serial::SerialImpl::getRI()
{
if (is_open_ == false) {
throw PortNotOpenedException ("Serial::getRI");
}
DWORD dwModemStatus;
if (!GetCommModemStatus(fd_, &dwModemStatus)) {
THROW (IOException, "Error getting the status of the RI line.");
}
return (MS_RING_ON & dwModemStatus) != 0;
}
bool
Serial::SerialImpl::getCD()
{
if (is_open_ == false) {
throw PortNotOpenedException ("Serial::getCD");
}
DWORD dwModemStatus;
if (!GetCommModemStatus(fd_, &dwModemStatus)) {
// Error in GetCommModemStatus;
THROW (IOException, "Error getting the status of the CD line.");
}
return (MS_RLSD_ON & dwModemStatus) != 0;
}
void
Serial::SerialImpl::readLock()
{
if (WaitForSingleObject(read_mutex, INFINITE) != WAIT_OBJECT_0) {
THROW (IOException, "Error claiming read mutex.");
}
}
void
Serial::SerialImpl::readUnlock()
{
if (!ReleaseMutex(read_mutex)) {
THROW (IOException, "Error releasing read mutex.");
}
}
void
Serial::SerialImpl::writeLock()
{
if (WaitForSingleObject(write_mutex, INFINITE) != WAIT_OBJECT_0) {
THROW (IOException, "Error claiming write mutex.");
}
}
void
Serial::SerialImpl::writeUnlock()
{
if (!ReleaseMutex(write_mutex)) {
THROW (IOException, "Error releasing write mutex.");
}
}
#endif // #if defined(_WIN32)

432
lib/serial/src/serial.cc Executable file
View File

@ -0,0 +1,432 @@
/* Copyright 2012 William Woodall and John Harrison */
#include <algorithm>
#if !defined(_WIN32) && !defined(__OpenBSD__) && !defined(__FreeBSD__)
# include <alloca.h>
#endif
#if defined (__MINGW32__)
# define alloca __builtin_alloca
#endif
#include "serial/serial.h"
#ifdef _WIN32
#include "serial/impl/win.h"
#else
#include "serial/impl/unix.h"
#endif
using std::invalid_argument;
using std::min;
using std::numeric_limits;
using std::vector;
using std::size_t;
using std::string;
using serial::Serial;
using serial::SerialException;
using serial::IOException;
using serial::bytesize_t;
using serial::parity_t;
using serial::stopbits_t;
using serial::flowcontrol_t;
class Serial::ScopedReadLock {
public:
ScopedReadLock(SerialImpl *pimpl) : pimpl_(pimpl) {
this->pimpl_->readLock();
}
~ScopedReadLock() {
this->pimpl_->readUnlock();
}
private:
// Disable copy constructors
ScopedReadLock(const ScopedReadLock&);
const ScopedReadLock& operator=(ScopedReadLock);
SerialImpl *pimpl_;
};
class Serial::ScopedWriteLock {
public:
ScopedWriteLock(SerialImpl *pimpl) : pimpl_(pimpl) {
this->pimpl_->writeLock();
}
~ScopedWriteLock() {
this->pimpl_->writeUnlock();
}
private:
// Disable copy constructors
ScopedWriteLock(const ScopedWriteLock&);
const ScopedWriteLock& operator=(ScopedWriteLock);
SerialImpl *pimpl_;
};
Serial::Serial (const string &port, uint32_t baudrate, serial::Timeout timeout,
bytesize_t bytesize, parity_t parity, stopbits_t stopbits,
flowcontrol_t flowcontrol)
: pimpl_(new SerialImpl (port, baudrate, bytesize, parity,
stopbits, flowcontrol))
{
pimpl_->setTimeout(timeout);
}
Serial::~Serial ()
{
delete pimpl_;
}
void
Serial::open ()
{
pimpl_->open ();
}
void
Serial::close ()
{
pimpl_->close ();
}
bool
Serial::isOpen () const
{
return pimpl_->isOpen ();
}
size_t
Serial::available ()
{
return pimpl_->available ();
}
bool
Serial::waitReadable ()
{
serial::Timeout timeout(pimpl_->getTimeout ());
return pimpl_->waitReadable(timeout.read_timeout_constant);
}
void
Serial::waitByteTimes (size_t count)
{
pimpl_->waitByteTimes(count);
}
size_t
Serial::read_ (uint8_t *buffer, size_t size)
{
return this->pimpl_->read (buffer, size);
}
size_t
Serial::read (uint8_t *buffer, size_t size)
{
ScopedReadLock lock(this->pimpl_);
return this->pimpl_->read (buffer, size);
}
size_t
Serial::read (std::vector<uint8_t> &buffer, size_t size)
{
ScopedReadLock lock(this->pimpl_);
uint8_t *buffer_ = new uint8_t[size];
size_t bytes_read = 0;
try {
bytes_read = this->pimpl_->read (buffer_, size);
}
catch (const std::exception &e) {
delete[] buffer_;
throw;
}
buffer.insert (buffer.end (), buffer_, buffer_+bytes_read);
delete[] buffer_;
return bytes_read;
}
size_t
Serial::read (std::string &buffer, size_t size)
{
ScopedReadLock lock(this->pimpl_);
uint8_t *buffer_ = new uint8_t[size];
size_t bytes_read = 0;
try {
bytes_read = this->pimpl_->read (buffer_, size);
}
catch (const std::exception &e) {
delete[] buffer_;
throw;
}
buffer.append (reinterpret_cast<const char*>(buffer_), bytes_read);
delete[] buffer_;
return bytes_read;
}
string
Serial::read (size_t size)
{
std::string buffer;
this->read (buffer, size);
return buffer;
}
size_t
Serial::readline (string &buffer, size_t size, string eol)
{
ScopedReadLock lock(this->pimpl_);
size_t eol_len = eol.length ();
uint8_t *buffer_ = static_cast<uint8_t*>
(alloca (size * sizeof (uint8_t)));
size_t read_so_far = 0;
while (true)
{
size_t bytes_read = this->read_ (buffer_ + read_so_far, 1);
read_so_far += bytes_read;
if (bytes_read == 0) {
break; // Timeout occured on reading 1 byte
}
if(read_so_far < eol_len) continue;
if (string (reinterpret_cast<const char*>
(buffer_ + read_so_far - eol_len), eol_len) == eol) {
break; // EOL found
}
if (read_so_far == size) {
break; // Reached the maximum read length
}
}
buffer.append(reinterpret_cast<const char*> (buffer_), read_so_far);
return read_so_far;
}
string
Serial::readline (size_t size, string eol)
{
std::string buffer;
this->readline (buffer, size, eol);
return buffer;
}
vector<string>
Serial::readlines (size_t size, string eol)
{
ScopedReadLock lock(this->pimpl_);
std::vector<std::string> lines;
size_t eol_len = eol.length ();
uint8_t *buffer_ = static_cast<uint8_t*>
(alloca (size * sizeof (uint8_t)));
size_t read_so_far = 0;
size_t start_of_line = 0;
while (read_so_far < size) {
size_t bytes_read = this->read_ (buffer_+read_so_far, 1);
read_so_far += bytes_read;
if (bytes_read == 0) {
if (start_of_line != read_so_far) {
lines.push_back (
string (reinterpret_cast<const char*> (buffer_ + start_of_line),
read_so_far - start_of_line));
}
break; // Timeout occured on reading 1 byte
}
if(read_so_far < eol_len) continue;
if (string (reinterpret_cast<const char*>
(buffer_ + read_so_far - eol_len), eol_len) == eol) {
// EOL found
lines.push_back(
string(reinterpret_cast<const char*> (buffer_ + start_of_line),
read_so_far - start_of_line));
start_of_line = read_so_far;
}
if (read_so_far == size) {
if (start_of_line != read_so_far) {
lines.push_back(
string(reinterpret_cast<const char*> (buffer_ + start_of_line),
read_so_far - start_of_line));
}
break; // Reached the maximum read length
}
}
return lines;
}
size_t
Serial::write (const string &data)
{
ScopedWriteLock lock(this->pimpl_);
return this->write_ (reinterpret_cast<const uint8_t*>(data.c_str()),
data.length());
}
size_t
Serial::write (const std::vector<uint8_t> &data)
{
ScopedWriteLock lock(this->pimpl_);
return this->write_ (&data[0], data.size());
}
size_t
Serial::write (const uint8_t *data, size_t size)
{
ScopedWriteLock lock(this->pimpl_);
return this->write_(data, size);
}
size_t
Serial::write_ (const uint8_t *data, size_t length)
{
return pimpl_->write (data, length);
}
void
Serial::setPort (const string &port)
{
ScopedReadLock rlock(this->pimpl_);
ScopedWriteLock wlock(this->pimpl_);
bool was_open = pimpl_->isOpen ();
if (was_open) close();
pimpl_->setPort (port);
if (was_open) open ();
}
string
Serial::getPort () const
{
return pimpl_->getPort ();
}
void
Serial::setTimeout (serial::Timeout &timeout)
{
pimpl_->setTimeout (timeout);
}
serial::Timeout
Serial::getTimeout () const {
return pimpl_->getTimeout ();
}
void
Serial::setBaudrate (uint32_t baudrate)
{
pimpl_->setBaudrate (baudrate);
}
uint32_t
Serial::getBaudrate () const
{
return uint32_t(pimpl_->getBaudrate ());
}
void
Serial::setBytesize (bytesize_t bytesize)
{
pimpl_->setBytesize (bytesize);
}
bytesize_t
Serial::getBytesize () const
{
return pimpl_->getBytesize ();
}
void
Serial::setParity (parity_t parity)
{
pimpl_->setParity (parity);
}
parity_t
Serial::getParity () const
{
return pimpl_->getParity ();
}
void
Serial::setStopbits (stopbits_t stopbits)
{
pimpl_->setStopbits (stopbits);
}
stopbits_t
Serial::getStopbits () const
{
return pimpl_->getStopbits ();
}
void
Serial::setFlowcontrol (flowcontrol_t flowcontrol)
{
pimpl_->setFlowcontrol (flowcontrol);
}
flowcontrol_t
Serial::getFlowcontrol () const
{
return pimpl_->getFlowcontrol ();
}
void Serial::flush ()
{
ScopedReadLock rlock(this->pimpl_);
ScopedWriteLock wlock(this->pimpl_);
pimpl_->flush ();
}
void Serial::flushInput ()
{
ScopedReadLock lock(this->pimpl_);
pimpl_->flushInput ();
}
void Serial::flushOutput ()
{
ScopedWriteLock lock(this->pimpl_);
pimpl_->flushOutput ();
}
void Serial::sendBreak (int duration)
{
pimpl_->sendBreak (duration);
}
void Serial::setBreak (bool level)
{
pimpl_->setBreak (level);
}
void Serial::setRTS (bool level)
{
pimpl_->setRTS (level);
}
void Serial::setDTR (bool level)
{
pimpl_->setDTR (level);
}
bool Serial::waitForChange()
{
return pimpl_->waitForChange();
}
bool Serial::getCTS ()
{
return pimpl_->getCTS ();
}
bool Serial::getDSR ()
{
return pimpl_->getDSR ();
}
bool Serial::getRI ()
{
return pimpl_->getRI ();
}
bool Serial::getCD ()
{
return pimpl_->getCD ();
}

View File

@ -150,6 +150,7 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
[](errors::Unexpected_Keyword const&) { return "Unexpected keyword"; }, [](errors::Unexpected_Keyword const&) { return "Unexpected keyword"; },
[](errors::Unrecognized_Character const&) { return "Unrecognized character"; }, [](errors::Unrecognized_Character const&) { return "Unrecognized character"; },
[](errors::Wrong_Arity_Of const&) { return "Different arity then expected"; }, [](errors::Wrong_Arity_Of const&) { return "Different arity then expected"; },
[](errors::Temporary_Error_Serial_Port const&) { return "Serial port connection failed"; },
[](errors::internal::Unexpected_Token const&) { return "Unexpected token"; }, [](errors::internal::Unexpected_Token const&) { return "Unexpected token"; },
[](errors::Arithmetic const& err) { [](errors::Arithmetic const& err) {
switch (err.type) { switch (err.type) {
@ -271,6 +272,15 @@ std::ostream& operator<<(std::ostream& os, Error const& err)
} }
}, },
[&](errors::Temporary_Error_Serial_Port const& error) {
os << "Error serial port: " << error.message << '\n';
os << "\n";
pretty::begin_comment(os);
os << "This error message is temporary\n";
pretty::end(os);
},
[&](errors::Unsupported_Types_For const& err) { [&](errors::Unsupported_Types_For const& err) {
switch (err.type) { switch (err.type) {
break; case errors::Unsupported_Types_For::Function: break; case errors::Unsupported_Types_For::Function:

View File

@ -169,6 +169,11 @@ namespace errors
}; };
} }
struct Temporary_Error_Serial_Port
{
std::string message;
};
/// All possible error types /// All possible error types
using Details = std::variant< using Details = std::variant<
Arithmetic, Arithmetic,
@ -180,6 +185,7 @@ namespace errors
Not_Callable, Not_Callable,
Operation_Requires_Midi_Connection, Operation_Requires_Midi_Connection,
Out_Of_Range, Out_Of_Range,
Temporary_Error_Serial_Port,
Undefined_Operator, Undefined_Operator,
Unexpected_Empty_Source, Unexpected_Empty_Source,
Unexpected_Keyword, Unexpected_Keyword,

View File

@ -1615,6 +1615,18 @@ static Result<Value> builtin_peers(Interpreter &interpreter, std::vector<Value>)
return Number(interpreter.starter.peers()); return Number(interpreter.starter.peers());
} }
static std::optional<Error> ensure_serial_available(Interpreter &interpreter)
{
if (interpreter.serialport->error_message->empty())
return std::nullopt;
return Error{
errors::Temporary_Error_Serial_Port {
.message = *std::exchange(interpreter.serialport->error_message, std::nullopt),
}
};
}
//: Ustaw wyjście MIDI w danym kontekście na dany port //: Ustaw wyjście MIDI w danym kontekście na dany port
//: //:
//: Dostępne opcje to numer portu oraz symbol `'virtual` tworzacy port wirtualny MIDI //: Dostępne opcje to numer portu oraz symbol `'virtual` tworzacy port wirtualny MIDI
@ -1638,6 +1650,7 @@ static Result<Value> builtin_port(Interpreter &interpreter, std::vector<Value> a
if (port == interpreter.current_context->port) { if (port == interpreter.current_context->port) {
return std::visit(Overloaded { return std::visit(Overloaded {
[](midi::connections::Virtual_Port) { return Value(Symbol("virtual")); }, [](midi::connections::Virtual_Port) { return Value(Symbol("virtual")); },
[](midi::connections::Serial_Port) { return Value(Symbol("serial")); },
[](midi::connections::Established_Port port) { return Value(Number(port)); }, [](midi::connections::Established_Port port) { return Value(Number(port)); },
}, key); }, key);
} }
@ -1658,12 +1671,69 @@ static Result<Value> builtin_port(Interpreter &interpreter, std::vector<Value> a
return {}; return {};
} }
if (port_type == "serial") {
Try(ensure_serial_available(interpreter));
if (auto it = Context::established_connections.find(midi::connections::Serial_Port{}); it != Context::established_connections.end()) {
interpreter.current_context->port = it->second;
return {};
}
auto serial = std::make_shared<midi::Serial_Midi>();
serial->serialport = interpreter.serialport;
interpreter.current_context->port = serial;
Context::established_connections[midi::connections::Serial_Port{}] = serial;
return {};
}
unimplemented(); unimplemented();
} }
unimplemented(); unimplemented();
} }
//: Zwróć wybraną wartość z dedykowanego urządzenia.
//:
//: Pierwszy parametr to numer wejścia lub jego nazwa (kolejno: `'knob`, `'knob2`, `'btn1`, `'btn2`, `'btn3`)
//: Kolejne parametry tworzą zbiór z którego równomiernie wejście będzie wybierane na podstawie wartości
static Result<Value> builtin_ctrl(Interpreter &interpreter, std::vector<Value> args)
{
if (args.empty()) {
error:
return errors::Unsupported_Types_For {
.type = errors::Unsupported_Types_For::Function,
.name = "ctrl",
.possibilities = {
},
};
}
Try(ensure_serial_available(interpreter));
auto input_index = 0;
if (auto a = get_if<Number>(args.front())) {
input_index = a->floor().as_int();
} else if (auto a = get_if<Symbol>(args.front())) {
static std::string_view names[] = { "knob1", "knob2", "btn1", "btn2", "btn3" };
if (auto matched = std::find(std::begin(names), std::end(names), *a); matched != std::end(names)) {
input_index = std::distance(std::begin(names), matched);
} else {
goto error;
}
} else {
goto error;
}
auto const value = interpreter.serialport->get(input_index);
if (args.size() == 1) {
return value;
}
auto const arguments = Try(flatten(interpreter, std::span(args).subspan(1)));
auto const arguments_index = (Number(arguments.size()) * value).floor().as_int();
return arguments[std::min(arguments_index, Number::value_type(arguments.size()-1))];
}
void Interpreter::register_builtin_functions() void Interpreter::register_builtin_functions()
{ {
auto &global = *Env::global; auto &global = *Env::global;
@ -1672,6 +1742,7 @@ void Interpreter::register_builtin_functions()
global.force_define("call", builtin_call); global.force_define("call", builtin_call);
global.force_define("ceil", builtin_ceil); global.force_define("ceil", builtin_ceil);
global.force_define("chord", builtin_chord); global.force_define("chord", builtin_chord);
global.force_define("ctrl", builtin_ctrl);
global.force_define("digits", builtin_digits); global.force_define("digits", builtin_digits);
global.force_define("down", builtin_down); global.force_define("down", builtin_down);
global.force_define("duration", builtin_duration); global.force_define("duration", builtin_duration);

View File

@ -13,18 +13,15 @@ std::chrono::duration<float> Context::length_to_duration(std::optional<Number> l
return std::chrono::duration<float>(float(len.num * (60.f / (float(bpm) / 4))) / len.den); return std::chrono::duration<float>(float(len.num * (60.f / (float(bpm) / 4))) / len.den);
} }
template<> std::size_t std::hash<midi::connections::Key>::operator()(midi::connections::Key const& value) const
struct std::hash<midi::connections::Key>
{ {
std::size_t operator()(midi::connections::Key const& value) const
{
using namespace midi::connections; using namespace midi::connections;
return hash_combine(value.index(), std::visit(Overloaded { return hash_combine(value.index(), std::visit(Overloaded {
[](Virtual_Port) { return 0u; }, [](Virtual_Port) { return 0u; },
[](Serial_Port) { return 0u; },
[](Established_Port port) { return port; }, [](Established_Port port) { return port; },
}, value)); }, value));
} }
};
std::unordered_map<midi::connections::Key, std::shared_ptr<midi::Connection>> Context::established_connections; std::unordered_map<midi::connections::Key, std::shared_ptr<midi::Connection>> Context::established_connections;

View File

@ -18,7 +18,12 @@ namespace midi::connections
bool operator==(Virtual_Port const&) const = default; bool operator==(Virtual_Port const&) const = default;
}; };
using Key = std::variant<Established_Port, Virtual_Port>; struct Serial_Port
{
bool operator==(Serial_Port const&) const = default;
};
using Key = std::variant<Established_Port, Virtual_Port, Serial_Port>;
} }
/// Context holds default values for music related actions /// Context holds default values for music related actions
@ -55,4 +60,10 @@ struct Context
std::shared_ptr<Context> parent; std::shared_ptr<Context> parent;
}; };
template<>
struct std::hash<midi::connections::Key>
{
std::size_t operator()(midi::connections::Key const& value) const;
};
#endif #endif

View File

@ -6,6 +6,7 @@
#include <musique/midi/midi.hh> #include <musique/midi/midi.hh>
#include <musique/value/value.hh> #include <musique/value/value.hh>
#include <unordered_map> #include <unordered_map>
#include <musique/serialport/serialport.hh>
/// Given program tree evaluates it into Value /// Given program tree evaluates it into Value
struct Interpreter struct Interpreter
@ -22,6 +23,8 @@ struct Interpreter
std::function<std::optional<Error>(Interpreter&, Value)> default_action; std::function<std::optional<Error>(Interpreter&, Value)> default_action;
std::shared_ptr<serialport::State> serialport;
Starter starter; Starter starter;
Interpreter(); Interpreter();

View File

@ -17,6 +17,9 @@
#include <musique/try.hh> #include <musique/try.hh>
#include <musique/unicode.hh> #include <musique/unicode.hh>
#include <musique/value/block.hh> #include <musique/value/block.hh>
#include <musique/serialport/serialport.hh>
#include <serial/serial.h>
#ifdef _WIN32 #ifdef _WIN32
extern "C" { extern "C" {
@ -86,6 +89,7 @@ static T pop(std::span<char const*> &span)
" Sy Brand, https://sybrand.ink/, creator of tl::expected https://github.com/TartanLlama/expected\n" " Sy Brand, https://sybrand.ink/, creator of tl::expected https://github.com/TartanLlama/expected\n"
" Justine Tunney, https://justinetunney.com, creator of bestline readline library https://github.com/jart/bestline\n" " Justine Tunney, https://justinetunney.com, creator of bestline readline library https://github.com/jart/bestline\n"
" Gary P. Scavone, http://www.music.mcgill.ca/~gary/, creator of rtmidi https://github.com/thestk/rtmidi\n" " Gary P. Scavone, http://www.music.mcgill.ca/~gary/, creator of rtmidi https://github.com/thestk/rtmidi\n"
" W. Wodall and J. Harrison, creators of https://github.com/wjwwood/serial\n"
" Creators of ableton/link, https://github.com/Ableton/link\n" " Creators of ableton/link, https://github.com/Ableton/link\n"
; ;
std::exit(1); std::exit(1);
@ -161,6 +165,9 @@ struct Runner
static inline Runner *the; static inline Runner *the;
Interpreter interpreter; Interpreter interpreter;
std::shared_ptr<serialport::State> serial_state;
std::thread serial_event_loop;
std::atomic<bool> serial_event_loop_stop = false;
/// Setup interpreter and midi connection with given port /// Setup interpreter and midi connection with given port
explicit Runner() explicit Runner()
@ -169,6 +176,15 @@ struct Runner
ensure(the == nullptr, "Only one instance of runner is supported"); ensure(the == nullptr, "Only one instance of runner is supported");
the = this; the = this;
/// Setup communication over serial
serial_state = std::make_shared<serialport::State>();
interpreter.serialport = serial_state;
serialport::initialize();
serial_event_loop = std::thread([this]() mutable {
serialport::event_loop(serial_event_loop_stop, *serial_state);
});
interpreter.current_context->connect(std::nullopt); interpreter.current_context->connect(std::nullopt);
Env::global->force_define("say", +[](Interpreter &interpreter, std::vector<Value> args) -> Result<Value> { Env::global->force_define("say", +[](Interpreter &interpreter, std::vector<Value> args) -> Result<Value> {
@ -187,6 +203,13 @@ struct Runner
Runner& operator=(Runner const&) = delete; Runner& operator=(Runner const&) = delete;
Runner& operator=(Runner &&) = delete; Runner& operator=(Runner &&) = delete;
~Runner() {
serial_event_loop_stop = true;
if (serial_event_loop.joinable()) {
serial_event_loop.join();
}
}
/// Consider given input file as new definition of parametless function /// Consider given input file as new definition of parametless function
/// ///
/// Useful for deffering execution of files to the point when all configuration of midi devices /// Useful for deffering execution of files to the point when all configuration of midi devices

View File

@ -4,6 +4,7 @@
#include <functional> #include <functional>
#include <optional> #include <optional>
#include <string> #include <string>
#include <musique/serialport/serialport.hh>
// Documentation of midi messages available at http://midi.teragonaudio.com/tech/midispec.htm // Documentation of midi messages available at http://midi.teragonaudio.com/tech/midispec.htm
namespace midi namespace midi
@ -22,6 +23,20 @@ namespace midi
void send_all_sounds_off(uint8_t channel); void send_all_sounds_off(uint8_t channel);
}; };
struct Serial_Midi : Connection
{
~Serial_Midi() = default;
bool supports_output() const override;
void send_note_on (uint8_t channel, uint8_t note_number, uint8_t velocity) override;
void send_note_off(uint8_t channel, uint8_t note_number, uint8_t velocity) override;
void send_program_change(uint8_t channel, uint8_t program) override;
void send_controller_change(uint8_t channel, uint8_t controller_number, uint8_t value) override;
std::shared_ptr<serialport::State> serialport;
};
struct Rt_Midi : Connection struct Rt_Midi : Connection
{ {
~Rt_Midi() override = default; ~Rt_Midi() override = default;

View File

@ -0,0 +1,29 @@
#include <musique/errors.hh>
#include <musique/midi/midi.hh>
#include <musique/serialport/serialport.hh>
#include <musique/interpreter/interpreter.hh>
bool midi::Serial_Midi::supports_output() const
{
return true;
}
void midi::Serial_Midi::send_note_on (uint8_t channel, uint8_t note_number, uint8_t velocity)
{
serialport->send(0, note_number);
}
void midi::Serial_Midi::send_note_off(uint8_t channel, uint8_t note_number, uint8_t velocity)
{
serialport->send(1, note_number);
}
void midi::Serial_Midi::send_program_change(uint8_t channel, uint8_t program)
{
unimplemented();
}
void midi::Serial_Midi::send_controller_change(uint8_t channel, uint8_t controller_number, uint8_t value)
{
unimplemented();
}

View File

@ -0,0 +1,117 @@
#include <musique/serialport/serialport.hh>
#include <iostream>
#include <thread>
#include <serial/serial.h>
namespace serialport{
int State::test(){
return 5;
}
Number State::get(unsigned position) const
{
return Number(state[position], MAX_VALUE);
}
void State::set(unsigned position, std::uint32_t value)
{
state[position] = value;
return;
}
void State::send(uint8_t message_type, uint8_t note_number)
{
if((head + 1) % 128 != tail){
message_data[head] = note_number;
message_types[head] = message_type;
head = (head + 1)%128;
}
return;
}
void initialize()
{
// std::jthread testowy = std::jthread();
return;
}
std::uint8_t get_byte()
{
return 8;
}
void event_loop(std::atomic<bool> &stop, State &state)
{
while(!stop){
try {
/// Search for the right device
auto const ports = serial::list_ports();
/*for (serial::PortInfo const& port : ports) {
std::cout << "Port: " << port.port << '\n';
std::cout << "Description: " << port.description << '\n';
std::cout << "Hardware ID: " << port.hardware_id << '\n';
}*/
int found_port = 0;
std::string connection_port = "";
while(found_port == 0){
for (serial::PortInfo const& port : ports) {
if(port.description.find("STM32")){
connection_port = port.port;
found_port = 1;
break;
}
}
}
/// Start connection
serial::Serial serial_conn(connection_port, 115200, serial::Timeout::simpleTimeout(1000));
/*if(serial_conn.isOpen())
std::cout << "[SERIAL] Serial open\n";
else
std::cout << "[SERIAL] Serial not open\n";
std::cout << "[SERIAL] commence serial communication\n";
*/
/// Set up received data buffer
while(1){
/// After serial connection established
//serial_conn.flushInput();
std::string result = serial_conn.readline(65536, "\n");
int control = result.at(0);
uint32_t value = std::stoi(result.substr(1, 4), nullptr, 10);
state.set(control - 65, value);
while(state.head != state.tail){
if(state.message_types[state.tail] == 0){
uint8_t bytes_to_send[3] = {0b10010000, state.message_data[state.tail], 0b00000000};
serial_conn.write(bytes_to_send, 3);
}else if(state.message_types[state.tail] == 1){
uint8_t bytes_to_send[3] = {0b10000000, state.message_data[state.tail], 0b00000000};
serial_conn.write(bytes_to_send, 3);
}
state.tail = (state.tail + 1) % 128;
}
}
} catch (std::exception &e) {
/// No connection to the device
state.error_message = e.what();
// std::cerr << "Unhandled Exception: " << e.what() << '\n';
//
// Sleep until error message is cleared since we don't need to try always to reconnect
while (!state.error_message->empty()) {
std::this_thread::yield();
}
}
}
return;
}
}

View File

@ -0,0 +1,36 @@
#ifndef MUSIQUE_SERIALPORT_SERIALPORT_HH
#define MUSIQUE_SERIALPORT_SERIALPORT_HH
#include <array>
#include <atomic>
#include <cstdint>
#include <memory>
#include <musique/value/number.hh>
#include <optional>
#include <serial/serial.h>
#include <string>
namespace serialport{
constexpr std::size_t MAX_STATE_COUNT = 8;
constexpr std::size_t MAX_VALUE = 4095;
struct State{
std::array<std::atomic<std::uint32_t>, MAX_STATE_COUNT> state;
std::array<std::atomic<std::uint8_t>, 128> message_types;
std::array<std::atomic<std::uint8_t>, 128> message_data;
std::atomic<std::uint8_t> head = 0;
std::atomic<std::uint8_t> tail = 0;
std::optional<std::string> error_message;
int test();
Number get(unsigned position) const;
void send(uint8_t message_type, uint8_t note_number);
void set(unsigned position, std::uint32_t value);
};
void initialize();
void event_loop(std::atomic<bool> &stop, State &state);
std::uint8_t get_byte();
}
#endif //MUSIQUE_SERIALPORT_SERIALPORT_HH

View File

@ -1,5 +1,21 @@
Release_Obj=$(addprefix bin/$(os)/,$(Obj)) Release_Obj=$(addprefix bin/$(os)/,$(Obj))
# http://www.music.mcgill.ca/~gary/rtmidi/#compiling
bin/$(os)/rtmidi.o: lib/rtmidi/RtMidi.cpp lib/rtmidi/RtMidi.h
@echo "CXX $@"
@$(CXX) $< -c -O2 -o $@ $(CPPFLAGS) -std=c++20
bin/$(os)/serial/serial.o: lib/serial/src/serial.cc
$(CXX) $^ -c -O2 -o $@ $(CPPFLAGS) -std=c++20
bin/$(os)/serial/$(Serial_Impl).o: lib/serial/src/impl/$(Serial_Impl).cc
$(CXX) $^ -c -O2 -o $@ $(CPPFLAGS) -std=c++20
bin/$(os)/serial/$(Serial_List_Ports).o: lib/serial/src/impl/list_ports/$(Serial_List_Ports).cc
$(CXX) $^ -c -O2 -o $@ $(CPPFLAGS) -std=c++20
Serial_Obj=bin/$(os)/serial/serial.o bin/$(os)/serial/$(Serial_Impl).o bin/$(os)/serial/$(Serial_List_Ports).o
bin/$(os)/bestline.o: lib/bestline/bestline.c lib/bestline/bestline.h bin/$(os)/bestline.o: lib/bestline/bestline.c lib/bestline/bestline.h
@echo "CC $@" @echo "CC $@"
@$(CC) $< -c -O3 -o $@ @$(CC) $< -c -O3 -o $@
@ -8,15 +24,15 @@ bin/$(os)/%.o: musique/%.cc
@echo "CXX $@" @echo "CXX $@"
@$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $< -c @$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $< -c
bin/$(os)/$(Target): $(Release_Obj) bin/$(os)/main.o bin/$(os)/rtmidi.o $(Bestline) bin/$(os)/$(Target): $(Release_Obj) bin/$(os)/main.o bin/$(os)/rtmidi.o $(Bestline) $(Serial_Obj)
@echo "CXX $@" @echo "CXX $@"
@$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $(Release_Obj) bin/$(os)/rtmidi.o $(Bestline) $(LDFLAGS) $(LDLIBS) @$(CXX) $(CXXFLAGS) $(RELEASE_FLAGS) $(CPPFLAGS) -o $@ $(Release_Obj) bin/$(os)/rtmidi.o $(Bestline) $(Serial_Obj) $(LDFLAGS) $(LDLIBS)
Debug_Obj=$(addprefix bin/$(os)/debug/,$(Obj)) Debug_Obj=$(addprefix bin/$(os)/debug/,$(Obj))
bin/$(os)/debug/$(Target): $(Debug_Obj) bin/$(os)/debug/main.o bin/$(os)/rtmidi.o $(Bestline) bin/$(os)/debug/$(Target): $(Debug_Obj) bin/$(os)/debug/main.o bin/$(os)/rtmidi.o $(Bestline) $(Serial_Obj)
@echo "CXX $@" @echo "CXX $@"
@$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $(Debug_Obj) bin/$(os)/rtmidi.o $(Bestline) $(LDFLAGS) $(LDLIBS) @$(CXX) $(CXXFLAGS) $(DEBUG_FLAGS) $(CPPFLAGS) -o $@ $(Debug_Obj) bin/$(os)/rtmidi.o $(Bestline) $(Serial_Obj) $(LDFLAGS) $(LDLIBS)
bin/$(os)/debug/%.o: musique/%.cc bin/$(os)/debug/%.o: musique/%.cc
@echo "CXX $@" @echo "CXX $@"

View File

@ -1,6 +1,9 @@
CC=gcc CC=gcc
CXX=g++ CXX=g++
CPPFLAGS:=$(CPPFLAGS) -D __LINUX_ALSA__ -D LINK_PLATFORM_LINUX CPPFLAGS:=$(CPPFLAGS) -D __LINUX_ALSA__ -D LINK_PLATFORM_LINUX
LDLIBS:=-lasound $(LDLIBS) -static-libgcc -static-libstdc++ LDLIBS:=-lasound -lrt $(LDLIBS) -static-libgcc -static-libstdc++
Bestline=bin/$(os)/bestline.o Bestline=bin/$(os)/bestline.o
Target=musique Target=musique
Serial_List_Ports=list_ports_linux
Serial_Impl=unix

View File

@ -1,7 +1,9 @@
CC=clang CC=clang
CXX=clang++ CXX=clang++
CPPFLAGS:=$(CPPFLAGS) -D __MACOSX_CORE__ -D LINK_PLATFORM_MACOSX CPPFLAGS:=$(CPPFLAGS) -D __MACOSX_CORE__ -D LINK_PLATFORM_MACOSX
LDLIBS:=-framework CoreMIDI -framework CoreAudio -framework CoreFoundation $(LDLIBS) LDLIBS:=-framework IOKit -framework CoreMIDI -framework CoreAudio -framework CoreFoundation $(LDLIBS)
Release_Obj=$(addprefix bin/,$(Obj)) Release_Obj=$(addprefix bin/,$(Obj))
Bestline=bin/$(os)/bestline.o Bestline=bin/$(os)/bestline.o
Target=musique Target=musique
Serial_List_Ports=list_ports_osx
Serial_Impl=unix

View File

@ -1,5 +1,7 @@
CC=i686-w64-mingw32-gcc CC=i686-w64-mingw32-gcc
CXX=i686-w64-mingw32-g++ CXX=i686-w64-mingw32-g++
CPPFLAGS:=$(CPPFLAGS) -D__WINDOWS_MM__ -D LINK_PLATFORM_WINDOWS CPPFLAGS:=$(CPPFLAGS) -D__WINDOWS_MM__ -D LINK_PLATFORM_WINDOWS
LDLIBS:=-lwinmm -liphlpapi -lws2_32 $(LDLIBS) -static-libgcc -static-libstdc++ -static LDLIBS:=-lsetupapi -lhid -lwinmm -liphlpapi -lws2_32 $(LDLIBS) -static-libgcc -static-libstdc++ -static
Target=musique.exe Target=musique.exe
Serial_List_Ports=list_ports_win
Serial_Impl=win