├── .gitignore ├── .vscode └── settings.json ├── npl ├── README.md └── linux │ ├── README.md │ ├── include │ ├── console │ │ └── console.h │ └── nimble │ │ ├── nimble_npl_os.h │ │ ├── os_types.h │ │ └── nimble_npl.h │ ├── src │ ├── os_atomic.c │ ├── os_time.c │ ├── os_mutex.c │ ├── os_sem.c │ ├── wqueue.h │ ├── os_task.c │ ├── os_eventq.cc │ └── os_callout.c │ └── test │ ├── test_util.h │ ├── test_npl_task.c │ ├── test_npl_callout.c │ ├── Makefile │ ├── test_npl_mempool.c │ ├── test_npl_eventq.c │ └── test_npl_sem.c ├── component.mk ├── bouffalo.mk ├── .gitattributes ├── .github └── FUNDING.yml ├── include ├── sx126x-utilities.h ├── sx126x-board.h └── radio.h ├── LICENSE ├── Makefile └── src ├── main.c ├── sx126x-linux.c ├── sx126x.c └── sx126x-board.c /.gitignore: -------------------------------------------------------------------------------- 1 | src/*.o 2 | npl/linux/src/*.o 3 | lora-sx1262 4 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "radio.h": "c" 4 | } 5 | } -------------------------------------------------------------------------------- /npl/README.md: -------------------------------------------------------------------------------- 1 | # NimBLE Porting Layer for Multitasking Functions 2 | 3 | Based on https://github.com/apache/mynewt-nimble/tree/master/porting/npl 4 | -------------------------------------------------------------------------------- /npl/linux/README.md: -------------------------------------------------------------------------------- 1 | # NimBLE Porting Layer for Multitasking Functions (Linux) 2 | 3 | Based on https://github.com/apache/mynewt-nimble/tree/master/porting/npl/linux 4 | -------------------------------------------------------------------------------- /component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Component Makefile 3 | # 4 | 5 | # Include Folders 6 | COMPONENT_ADD_INCLUDEDIRS := include 7 | 8 | # Source Folders 9 | COMPONENT_SRCDIRS := src 10 | 11 | # Check the submodule is initialised 12 | COMPONENT_SUBMODULES := 13 | -------------------------------------------------------------------------------- /bouffalo.mk: -------------------------------------------------------------------------------- 1 | 2 | # Component Makefile 3 | # 4 | 5 | # Include Folders 6 | COMPONENT_ADD_INCLUDEDIRS := include 7 | 8 | # Object Files (*.o) 9 | COMPONENT_OBJS := $(patsubst %.c,%.o, $(COMPONENT_SRCS)) 10 | 11 | # Source Folders 12 | COMPONENT_SRCDIRS := src 13 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | toolchain/* linguist-vendored 2 | 3 | # From https://code.visualstudio.com/docs/remote/troubleshooting#_resolving-git-line-ending-issues-in-wsl-resulting-in-many-modified-files 4 | * text=auto eol=lf 5 | *.{cmd,[cC][mM][dD]} text eol=crlf 6 | *.{bat,[bB][aA][tT]} text eol=crlf 7 | *.ch8 binary 8 | *.bin binary 9 | *.cfg binary 10 | *.conf binary 11 | *.dll binary 12 | *.exe binary 13 | *.fzz binary 14 | *.gif binary 15 | *.jpg binary 16 | *.jpeg binary 17 | *.mp4 binary 18 | *.pdf binary 19 | *.pem binary 20 | *.png binary 21 | *.pyd binary 22 | *.txt binary 23 | *.woff binary 24 | flash_build binary 25 | genromfs_* binary 26 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [lupyuen] # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] 4 | patreon: # Replace with a single Patreon username 5 | open_collective: # Replace with a single Open Collective username 6 | ko_fi: # Replace with a single Ko-fi username 7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 9 | liberapay: # Replace with a single Liberapay username 10 | issuehunt: # Replace with a single IssueHunt username 11 | otechie: # Replace with a single Otechie username 12 | custom: ['paypal.me/lupyuen'] # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] 13 | -------------------------------------------------------------------------------- /npl/linux/include/console/console.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #ifndef __CONSOLE_H__ 21 | #define __CONSOLE_H__ 22 | 23 | #include 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #define console_printf(_fmt, ...) printf(_fmt, ##__VA_ARGS__) 30 | 31 | #ifdef __cplusplus 32 | } 33 | #endif 34 | 35 | #endif /* __CONSOLE_H__ */ 36 | -------------------------------------------------------------------------------- /npl/linux/src/os_atomic.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | #include "nimble/nimble_npl.h" 24 | 25 | static pthread_mutex_t s_mutex = PTHREAD_MUTEX_INITIALIZER; ////PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP; 26 | 27 | uint32_t ble_npl_hw_enter_critical(void) 28 | { 29 | pthread_mutex_lock(&s_mutex); 30 | return 0; 31 | } 32 | 33 | void ble_npl_hw_exit_critical(uint32_t ctx) 34 | { 35 | pthread_mutex_unlock(&s_mutex); 36 | } 37 | -------------------------------------------------------------------------------- /include/sx126x-utilities.h: -------------------------------------------------------------------------------- 1 | // Based on https://github.com/apache/mynewt-core/blob/master/hw/drivers/lora/include/lora/utilities.h 2 | /*! 3 | * \file utilities.h 4 | * 5 | * \brief Helper functions implementation 6 | * 7 | * \copyright Revised BSD License, see section \ref LICENSE. 8 | * 9 | * \code 10 | * ______ _ 11 | * / _____) _ | | 12 | * ( (____ _____ ____ _| |_ _____ ____| |__ 13 | * \____ \| ___ | (_ _) ___ |/ ___) _ \ 14 | * _____) ) ____| | | || |_| ____( (___| | | | 15 | * (______/|_____)_|_|_| \__)_____)\____)_| |_| 16 | * (C)2013-2017 Semtech 17 | * 18 | * \endcode 19 | * 20 | * \author Miguel Luis ( Semtech ) 21 | * 22 | * \author Gregory Cristian ( Semtech ) 23 | */ 24 | #ifndef __SX126X_UTILITIES_H__ 25 | #define __SX126X_UTILITIES_H__ 26 | 27 | #include 28 | 29 | /*! 30 | * \brief Read the current time 31 | * 32 | * \retval time returns current time 33 | */ 34 | uint32_t timer_get_current_time(void); 35 | 36 | /*! 37 | * \brief Return the Time elapsed since a fix moment in Time 38 | * 39 | * \param [IN] saved_time fix moment in Time 40 | * \retval time returns elapsed time 41 | */ 42 | uint32_t timer_get_elapsed_time(uint32_t saved_time); 43 | 44 | #endif // __SX126X_UTILITIES_H__ 45 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Revised BSD License 2 | Copyright Semtech Corporation 2013. All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | * Redistributions of source code must retain the above copyright 7 | notice, this list of conditions and the following disclaimer. 8 | * Redistributions in binary form must reproduce the above copyright 9 | notice, this list of conditions and the following disclaimer in the 10 | documentation and/or other materials provided with the distribution. 11 | * Neither the name of the Semtech corporation nor the 12 | names of its contributors may be used to endorse or promote products 13 | derived from this software without specific prior written permission. 14 | 15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 | ARE DISCLAIMED. IN NO EVENT SHALL SEMTECH CORPORATION BE LIABLE FOR ANY DIRECT, 19 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 20 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 21 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 22 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | TARGETS:= lora-sx1262 2 | 3 | # npl/linux/src/os_eventq.cc 4 | CSRCS := \ 5 | src/main.c \ 6 | src/radio.c \ 7 | src/sx126x.c \ 8 | src/sx126x-linux.c \ 9 | npl/linux/src/os_callout.c \ 10 | npl/linux/src/os_sem.c \ 11 | npl/linux/src/os_task.c \ 12 | npl/linux/src/os_atomic.c \ 13 | npl/linux/src/os_time.c \ 14 | npl/linux/src/os_mutex.c \ 15 | 16 | DEPS := \ 17 | include/radio.h \ 18 | include/sx126x-board.h \ 19 | include/sx126x.h \ 20 | include/sx126x-utilities.h \ 21 | npl/linux/include/nimble/nimble_npl_os.h \ 22 | npl/linux/include/nimble/nimble_npl.h \ 23 | npl/linux/include/nimble/os_types.h \ 24 | npl/linux/include/console/console.h \ 25 | npl/linux/src/wqueue.h \ 26 | 27 | # TODO: -Werror=all 28 | CCFLAGS:= \ 29 | -g \ 30 | -Wall \ 31 | -Wextra \ 32 | -Wno-unused-parameter \ 33 | -Wno-sign-compare \ 34 | -Wno-old-style-declaration \ 35 | -I include \ 36 | -I npl/linux/include \ 37 | -I npl/linux/include/nimble \ 38 | 39 | LDFLAGS:= \ 40 | -pthread \ 41 | -lrt \ 42 | -lstdc++ \ 43 | 44 | CC := gcc 45 | CPP := gcc 46 | 47 | MAINS := $(addsuffix .o, $(TARGETS) ) 48 | OBJ := \ 49 | $(MAINS) \ 50 | $(CSRCS:.c=.o) 51 | 52 | .PHONY: all clean 53 | 54 | all: $(TARGETS) 55 | 56 | clean: 57 | rm src/*.o || true 58 | rm npl/linux/src/*.o || true 59 | 60 | $(OBJ): %.o : %.c $(DEPS) 61 | $(CC) -c -o $@ $< $(CCFLAGS) 62 | 63 | # $(OBJ): %.o : %.cc $(DEPS) 64 | # $(CPP) -c -o $@ $< $(CCFLAGS) 65 | 66 | $(TARGETS): % : $(filter-out $(MAINS), $(OBJ)) 67 | $(CC) -o $@ \ 68 | npl/linux/src/os_eventq.cc \ 69 | $(LIBS) \ 70 | $^ \ 71 | $(CCFLAGS) \ 72 | $(LDFLAGS) \ 73 | -------------------------------------------------------------------------------- /npl/linux/include/nimble/nimble_npl_os.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #ifndef _NIMBLE_NPL_OS_H_ 21 | #define _NIMBLE_NPL_OS_H_ 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "os_types.h" 29 | 30 | #ifdef __cplusplus 31 | extern "C" { 32 | #endif 33 | 34 | #define BLE_NPL_OS_ALIGNMENT (__WORDSIZE / 8) 35 | 36 | #define BLE_NPL_TIME_FOREVER INT32_MAX 37 | 38 | #define SYSINIT_PANIC_MSG(msg) __assert_fail(msg, __FILE__, __LINE__, __func__) 39 | 40 | #define SYSINIT_PANIC_ASSERT_MSG(rc, msg) do \ 41 | { \ 42 | if (!(rc)) { \ 43 | SYSINIT_PANIC_MSG(msg); \ 44 | } \ 45 | } while (0) 46 | 47 | #ifdef __cplusplus 48 | } 49 | #endif 50 | 51 | #endif /* _NPL_H_ */ 52 | -------------------------------------------------------------------------------- /npl/linux/src/os_time.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | ////#include "os/os.h" 24 | #include "nimble/nimble_npl.h" 25 | 26 | #include 27 | #include 28 | 29 | /** 30 | * Return ticks [ms] since system start as uint32_t. 31 | */ 32 | ble_npl_time_t 33 | ble_npl_time_get(void) 34 | { 35 | struct timespec now; 36 | if (clock_gettime(CLOCK_MONOTONIC, &now)) { 37 | return 0; 38 | } 39 | return now.tv_sec * 1000.0 + now.tv_nsec / 1000000.0; 40 | } 41 | 42 | 43 | ble_npl_error_t ble_npl_time_ms_to_ticks(uint32_t ms, ble_npl_time_t *out_ticks) 44 | { 45 | *out_ticks = ms; 46 | 47 | return BLE_NPL_OK; 48 | } 49 | 50 | 51 | ble_npl_error_t ble_npl_time_ticks_to_ms(ble_npl_time_t ticks, uint32_t *out_ms) 52 | { 53 | *out_ms = ticks; 54 | 55 | return BLE_NPL_OK; 56 | } 57 | 58 | ble_npl_time_t ble_npl_time_ms_to_ticks32(uint32_t ms) 59 | { 60 | return ms; 61 | } 62 | 63 | uint32_t ble_npl_time_ticks_to_ms32(ble_npl_time_t ticks) 64 | { 65 | return ticks; 66 | } 67 | 68 | void 69 | ble_npl_time_delay(ble_npl_time_t ticks) 70 | { 71 | struct timespec sleep_time; 72 | long ms = ble_npl_time_ticks_to_ms32(ticks); 73 | uint32_t s = ms / 1000; 74 | 75 | ms -= s * 1000; 76 | sleep_time.tv_sec = s; 77 | sleep_time.tv_nsec = ms * 1000000; 78 | 79 | nanosleep(&sleep_time, NULL); 80 | } 81 | -------------------------------------------------------------------------------- /npl/linux/test/test_util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #ifndef _TEST_UTIL_H_ 21 | #define _TEST_UTIL_H_ 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | #include 28 | #include 29 | 30 | #define PASS (0) 31 | #define FAIL (-1) 32 | 33 | #define SuccessOrQuit(ERR, MSG) \ 34 | do { \ 35 | if ((ERR)) \ 36 | { \ 37 | fprintf(stderr, "\nFAILED %s:%d - %s\n", __FUNCTION__, __LINE__, MSG); \ 38 | exit(-1); \ 39 | } \ 40 | } while (false) 41 | 42 | #define VerifyOrQuit(TST, MSG) \ 43 | do { \ 44 | if (!(TST)) \ 45 | { \ 46 | fprintf(stderr, "\nFAILED %s:%d - %s\n", __FUNCTION__, __LINE__, MSG); \ 47 | exit(-1); \ 48 | } \ 49 | } while (false) 50 | 51 | 52 | #ifdef __cplusplus 53 | } 54 | #endif 55 | 56 | #endif /* _TEST_UTIL_H_ */ 57 | -------------------------------------------------------------------------------- /npl/linux/src/os_mutex.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #include 21 | #include 22 | 23 | ////#include "os/os.h" 24 | #include "nimble/nimble_npl.h" 25 | 26 | ble_npl_error_t 27 | ble_npl_mutex_init(struct ble_npl_mutex *mu) 28 | { 29 | if (!mu) { 30 | return BLE_NPL_INVALID_PARAM; 31 | } 32 | 33 | pthread_mutexattr_init(&mu->attr); 34 | pthread_mutexattr_settype(&mu->attr, PTHREAD_MUTEX_RECURSIVE_NP); 35 | pthread_mutex_init(&mu->lock, &mu->attr); 36 | 37 | return BLE_NPL_OK; 38 | } 39 | 40 | ble_npl_error_t 41 | ble_npl_mutex_release(struct ble_npl_mutex *mu) 42 | { 43 | if (!mu) { 44 | return BLE_NPL_INVALID_PARAM; 45 | } 46 | 47 | if (pthread_mutex_unlock(&mu->lock)) { 48 | return BLE_NPL_BAD_MUTEX; 49 | } 50 | 51 | return BLE_NPL_OK; 52 | } 53 | 54 | ble_npl_error_t 55 | ble_npl_mutex_pend(struct ble_npl_mutex *mu, uint32_t timeout) 56 | { 57 | int err; 58 | 59 | if (!mu) { 60 | return BLE_NPL_INVALID_PARAM; 61 | } 62 | 63 | if (timeout == BLE_NPL_TIME_FOREVER) { 64 | err = pthread_mutex_lock(&mu->lock); 65 | } else { 66 | err = clock_gettime(CLOCK_REALTIME, &mu->wait); 67 | if (err) { 68 | return BLE_NPL_ERROR; 69 | } 70 | 71 | mu->wait.tv_sec += timeout / 1000; 72 | mu->wait.tv_nsec += (timeout % 1000) * 1000000; 73 | 74 | err = pthread_mutex_timedlock(&mu->lock, &mu->wait); 75 | if (err == ETIMEDOUT) { 76 | return BLE_NPL_TIMEOUT; 77 | } 78 | } 79 | 80 | return (err) ? BLE_NPL_ERROR : BLE_NPL_OK; 81 | } 82 | -------------------------------------------------------------------------------- /npl/linux/src/os_sem.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | ////#include "os/os.h" 25 | #include "nimble/nimble_npl.h" 26 | 27 | ble_npl_error_t 28 | ble_npl_sem_init(struct ble_npl_sem *sem, uint16_t tokens) 29 | { 30 | if (!sem) { 31 | return BLE_NPL_INVALID_PARAM; 32 | } 33 | 34 | sem_init(&sem->lock, 0, tokens); 35 | 36 | return BLE_NPL_OK; 37 | } 38 | 39 | ble_npl_error_t 40 | ble_npl_sem_release(struct ble_npl_sem *sem) 41 | { 42 | int err; 43 | 44 | if (!sem) { 45 | return BLE_NPL_INVALID_PARAM; 46 | } 47 | 48 | err = sem_post(&sem->lock); 49 | 50 | return (err) ? BLE_NPL_ERROR : BLE_NPL_OK; 51 | } 52 | 53 | ble_npl_error_t 54 | ble_npl_sem_pend(struct ble_npl_sem *sem, uint32_t timeout) 55 | { 56 | int err = 0; 57 | struct timespec wait; 58 | 59 | if (!sem) { 60 | return BLE_NPL_INVALID_PARAM; 61 | } 62 | 63 | if (timeout == BLE_NPL_TIME_FOREVER) { 64 | err = sem_wait(&sem->lock); 65 | } else { 66 | err = clock_gettime(CLOCK_REALTIME, &wait); 67 | if (err) { 68 | return BLE_NPL_ERROR; 69 | } 70 | 71 | wait.tv_sec += timeout / 1000; 72 | wait.tv_nsec += (timeout % 1000) * 1000000; 73 | 74 | err = sem_timedwait(&sem->lock, &wait); 75 | if (err && errno == ETIMEDOUT) { 76 | return BLE_NPL_TIMEOUT; 77 | } 78 | } 79 | 80 | return (err) ? BLE_NPL_ERROR : BLE_NPL_OK; 81 | } 82 | 83 | uint16_t 84 | ble_npl_sem_get_count(struct ble_npl_sem *sem) 85 | { 86 | int count; 87 | 88 | assert(sem); 89 | assert(&sem->lock); 90 | sem_getvalue(&sem->lock, &count); 91 | 92 | return count; 93 | } 94 | -------------------------------------------------------------------------------- /npl/linux/include/nimble/os_types.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #ifndef _NPL_OS_TYPES_H 21 | #define _NPL_OS_TYPES_H 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | /* The highest and lowest task priorities */ 30 | #define OS_TASK_PRI_HIGHEST (sched_get_priority_max(SCHED_RR)) 31 | #define OS_TASK_PRI_LOWEST (sched_get_priority_min(SCHED_RR)) 32 | 33 | typedef uint32_t ble_npl_time_t; 34 | typedef int32_t ble_npl_stime_t; 35 | 36 | //typedef int os_sr_t; 37 | typedef int ble_npl_stack_t; 38 | 39 | 40 | struct ble_npl_event { 41 | uint8_t ev_queued; 42 | ble_npl_event_fn *ev_cb; 43 | void *ev_arg; 44 | }; 45 | 46 | struct ble_npl_eventq { 47 | void *q; 48 | }; 49 | 50 | struct ble_npl_callout { 51 | struct ble_npl_event c_ev; 52 | struct ble_npl_eventq *c_evq; 53 | uint32_t c_ticks; 54 | timer_t c_timer; 55 | bool c_active; 56 | }; 57 | 58 | struct ble_npl_mutex { 59 | pthread_mutex_t lock; 60 | pthread_mutexattr_t attr; 61 | struct timespec wait; 62 | }; 63 | 64 | struct ble_npl_sem { 65 | sem_t lock; 66 | }; 67 | 68 | struct ble_npl_task { 69 | pthread_t handle; 70 | pthread_attr_t attr; 71 | struct sched_param param; 72 | const char* name; 73 | }; 74 | 75 | typedef void *(*ble_npl_task_func_t)(void *); 76 | 77 | int ble_npl_task_init(struct ble_npl_task *t, const char *name, ble_npl_task_func_t func, 78 | void *arg, uint8_t prio, ble_npl_time_t sanity_itvl, 79 | ble_npl_stack_t *stack_bottom, uint16_t stack_size); 80 | 81 | int ble_npl_task_remove(struct ble_npl_task *t); 82 | 83 | uint8_t ble_npl_task_count(void); 84 | 85 | void ble_npl_task_yield(void); 86 | 87 | #endif // _NPL_OS_TYPES_H 88 | -------------------------------------------------------------------------------- /npl/linux/src/wqueue.h: -------------------------------------------------------------------------------- 1 | /* 2 | wqueue.h 3 | Worker thread queue based on the Standard C++ library list 4 | template class. 5 | ------------------------------------------ 6 | Copyright (c) 2013 Vic Hargrave 7 | Licensed under the Apache License, Version 2.0 (the "License"); 8 | you may not use this file except in compliance with the License. 9 | You may obtain a copy of the License at 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | Unless required by applicable law or agreed to in writing, software 12 | distributed under the License is distributed on an "AS IS" BASIS, 13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | See the License for the specific language governing permissions and 15 | limitations under the License. 16 | */ 17 | 18 | // https://vichargrave.github.io/articles/2013-01/multithreaded-work-queue-in-cpp 19 | // https://github.com/vichargrave/wqueue/blob/master/wqueue.h 20 | 21 | 22 | #ifndef __wqueue_h__ 23 | #define __wqueue_h__ 24 | 25 | #include 26 | #include 27 | 28 | using namespace std; 29 | 30 | template class wqueue 31 | { 32 | list m_queue; 33 | pthread_mutex_t m_mutex; 34 | pthread_mutexattr_t m_mutex_attr; 35 | pthread_cond_t m_condv; 36 | 37 | public: 38 | wqueue() 39 | { 40 | pthread_mutexattr_init(&m_mutex_attr); 41 | pthread_mutexattr_settype(&m_mutex_attr, PTHREAD_MUTEX_RECURSIVE); 42 | pthread_mutex_init(&m_mutex, &m_mutex_attr); 43 | pthread_cond_init(&m_condv, NULL); 44 | } 45 | 46 | ~wqueue() { 47 | pthread_mutex_destroy(&m_mutex); 48 | pthread_cond_destroy(&m_condv); 49 | } 50 | 51 | void put(T item) { 52 | pthread_mutex_lock(&m_mutex); 53 | m_queue.push_back(item); 54 | pthread_cond_signal(&m_condv); 55 | pthread_mutex_unlock(&m_mutex); 56 | } 57 | 58 | T get(uint32_t tmo) { 59 | pthread_mutex_lock(&m_mutex); 60 | if (tmo) { 61 | while (m_queue.size() == 0) { 62 | pthread_cond_wait(&m_condv, &m_mutex); 63 | } 64 | } 65 | 66 | T item = NULL; 67 | 68 | if (m_queue.size() != 0) { 69 | item = m_queue.front(); 70 | m_queue.pop_front(); 71 | } 72 | 73 | pthread_mutex_unlock(&m_mutex); 74 | return item; 75 | } 76 | 77 | void remove(T item) { 78 | pthread_mutex_lock(&m_mutex); 79 | m_queue.remove(item); 80 | pthread_mutex_unlock(&m_mutex); 81 | } 82 | 83 | int size() { 84 | pthread_mutex_lock(&m_mutex); 85 | int size = m_queue.size(); 86 | pthread_mutex_unlock(&m_mutex); 87 | return size; 88 | } 89 | }; 90 | 91 | #endif 92 | -------------------------------------------------------------------------------- /npl/linux/test/test_npl_task.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #include "test_util.h" 21 | #include "nimble/nimble_npl.h" 22 | 23 | #include 24 | 25 | #define TASK0_ARG 55 26 | #define TASK1_ARG 66 27 | 28 | static struct ble_npl_task s_task[2]; 29 | static int s_task_arg[2] = 30 | { 31 | TASK0_ARG, TASK1_ARG 32 | }; 33 | 34 | 35 | void *task0_run(void *args) 36 | { 37 | int i = 10000; 38 | VerifyOrQuit(args == &s_task_arg[0], "Wrong args passed to task0"); 39 | 40 | while (i--) 41 | { 42 | } 43 | 44 | return NULL; 45 | } 46 | 47 | void *task1_run(void *args) 48 | { 49 | int i = 10000; 50 | VerifyOrQuit(args == &s_task_arg[1], "Wrong args passed to task0"); 51 | 52 | while (i--) 53 | { 54 | } 55 | 56 | printf("All tests passed\n"); 57 | exit(PASS); 58 | 59 | return NULL; 60 | } 61 | 62 | /** 63 | * Unit test for initializing a task. 64 | * 65 | * int ble_npl_task_init(struct ble_npl_task *t, const char *name, ble_npl_task_func_t func, 66 | * void *arg, uint8_t prio, ble_npl_time_t sanity_itvl, 67 | * ble_npl_stack_t *stack_bottom, uint16_t stack_size) 68 | * 69 | */ 70 | int test_init(void) 71 | { 72 | int err; 73 | err = ble_npl_task_init(NULL, 74 | "Null task", 75 | NULL, NULL, 1, 0, NULL, 0); 76 | VerifyOrQuit(err, "ble_npl_task_init accepted NULL parameters."); 77 | 78 | err = ble_npl_task_init(&s_task[0], 79 | "s_task[0]", 80 | task0_run, &s_task_arg[0], 1, 0, NULL, 0); 81 | SuccessOrQuit(err, "ble_npl_task_init failed."); 82 | 83 | err = ble_npl_task_init(&s_task[1], 84 | "s_task[1]", 85 | task1_run, &s_task_arg[1], 1, 0, NULL, 0); 86 | 87 | return err; 88 | } 89 | 90 | int main(void) 91 | { 92 | int ret = PASS; 93 | SuccessOrQuit(test_init(), "Failed: ble_npl_task_init"); 94 | 95 | pthread_exit(&ret); 96 | 97 | return ret; 98 | } 99 | -------------------------------------------------------------------------------- /npl/linux/test/test_npl_callout.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | /** 21 | Unit tests for the ble_npl_callout api: 22 | 23 | void ble_npl_callout_init(struct ble_npl_callout *cf, struct ble_npl_eventq *evq, 24 | ble_npl_event_fn *ev_cb, void *ev_arg); 25 | int ble_npl_callout_reset(struct ble_npl_callout *, int32_t); 26 | int ble_npl_callout_queued(struct ble_npl_callout *c); 27 | void ble_npl_callout_stop(struct ble_npl_callout *c); 28 | */ 29 | 30 | #include "test_util.h" 31 | #include "nimble/nimble_npl.h" 32 | 33 | #define TEST_ARGS_VALUE (55) 34 | #define TEST_INTERVAL (100) 35 | 36 | static bool s_tests_running = true; 37 | static struct ble_npl_task s_task; 38 | static struct ble_npl_callout s_callout; 39 | static int s_callout_args = TEST_ARGS_VALUE; 40 | 41 | static struct ble_npl_eventq s_eventq; 42 | 43 | 44 | void on_callout(struct ble_npl_event *ev) 45 | { 46 | VerifyOrQuit(ev->ev_arg == &s_callout_args, 47 | "callout: wrong args passed"); 48 | 49 | VerifyOrQuit(*(int*)ev->ev_arg == TEST_ARGS_VALUE, 50 | "callout: args corrupted"); 51 | 52 | s_tests_running = false; 53 | } 54 | 55 | /** 56 | * ble_npl_callout_init(struct ble_npl_callout *c, struct ble_npl_eventq *evq, 57 | * ble_npl_event_fn *ev_cb, void *ev_arg) 58 | */ 59 | int test_init(void) 60 | { 61 | ble_npl_callout_init(&s_callout, 62 | &s_eventq, 63 | on_callout, 64 | &s_callout_args); 65 | return PASS; 66 | } 67 | 68 | int test_queued(void) 69 | { 70 | //VerifyOrQuit(ble_npl_callout_queued(&s_callout), 71 | // "callout: not queued when expected"); 72 | return PASS; 73 | } 74 | 75 | int test_reset(void) 76 | { 77 | return ble_npl_callout_reset(&s_callout, TEST_INTERVAL); 78 | } 79 | 80 | int test_stop(void) 81 | { 82 | return PASS; 83 | } 84 | 85 | 86 | /** 87 | * ble_npl_callout_init(struct ble_npl_callout *c, struct ble_npl_eventq *evq, 88 | * ble_npl_event_fn *ev_cb, void *ev_arg) 89 | */ 90 | void *test_task_run(void *args) 91 | { 92 | SuccessOrQuit(test_init(), "callout_init failed"); 93 | SuccessOrQuit(test_queued(), "callout_queued failed"); 94 | SuccessOrQuit(test_reset(), "callout_reset failed"); 95 | 96 | while (s_tests_running) 97 | { 98 | ble_npl_eventq_run(&s_eventq); 99 | } 100 | 101 | printf("All tests passed\n"); 102 | exit(PASS); 103 | 104 | return NULL; 105 | } 106 | 107 | int main(void) 108 | { 109 | ble_npl_eventq_init(&s_eventq); 110 | 111 | SuccessOrQuit(ble_npl_task_init(&s_task, "s_task", test_task_run, 112 | NULL, 1, 0, NULL, 0), 113 | "task: error initializing"); 114 | 115 | while (1) {} 116 | } 117 | -------------------------------------------------------------------------------- /npl/linux/test/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed to the Apache Software Foundation (ASF) under one 3 | # or more contributor license agreements. See the NOTICE file 4 | # distributed with this work for additional information 5 | # regarding copyright ownership. The ASF licenses this file 6 | # to you under the Apache License, Version 2.0 (the 7 | # "License"); you may not use this file except in compliance 8 | # with the License. You may obtain a copy of the License at 9 | # * http://www.apache.org/licenses/LICENSE-2.0 10 | # * Unless required by applicable law or agreed to in writing, 11 | # software distributed under the License is distributed on an 12 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 13 | # KIND, either express or implied. See the License for the 14 | # specific language governing permissions and limitations 15 | # under the License. 16 | # 17 | 18 | # Makefile 19 | 20 | PROJ_ROOT = ../../../.. 21 | 22 | ### ===== Toolchain ===== 23 | 24 | CROSS_COMPILE ?= 25 | CC = ccache $(CROSS_COMPILE)gcc 26 | CPP = ccache $(CROSS_COMPILE)g++ 27 | LD = $(CROSS_COMPILE)gcc 28 | AR = $(CROSS_COMPILE)ar 29 | 30 | ### ===== Compiler Flags ===== 31 | 32 | INCLUDES = \ 33 | -I. \ 34 | -I$(PROJ_ROOT)/nimble/include \ 35 | -I$(PROJ_ROOT)/porting/npl/linux/include \ 36 | -I$(PROJ_ROOT)/porting/npl/linux/src \ 37 | -I$(PROJ_ROOT)/porting/nimble/include \ 38 | $(NULL) 39 | 40 | DEFINES = 41 | 42 | CFLAGS = \ 43 | $(INCLUDES) $(DEFINES) \ 44 | -g \ 45 | -D_GNU_SOURCE \ 46 | $(NULL) 47 | 48 | LIBS = -lrt -lpthread -lstdc++ 49 | 50 | LDFLAGS = 51 | 52 | ### ===== Sources ===== 53 | 54 | OSAL_PATH = $(PROJ_ROOT)/porting/npl/linux/src 55 | 56 | SRCS = $(shell find $(OSAL_PATH) -maxdepth 1 -name '*.c') 57 | SRCS += $(shell find $(OSAL_PATH) -maxdepth 1 -name '*.cc') 58 | SRCS += $(PROJ_ROOT)/porting/nimble/src/os_mempool.c 59 | 60 | OBJS = $(patsubst %.c, %.o,$(filter %.c, $(SRCS))) 61 | OBJS += $(patsubst %.cc,%.o,$(filter %.cc, $(SRCS))) 62 | 63 | TEST_SRCS = $(shell find . -maxdepth 1 -name '*.c') 64 | TEST_SRCS += $(shell find . -maxdepth 1 -name '*.cc') 65 | 66 | TEST_OBJS = $(patsubst %.c, %.o,$(filter %.c, $(SRCS))) 67 | TEST_OBJS += $(patsubst %.cc,%.o,$(filter %.cc, $(SRCS))) 68 | 69 | ### ===== Rules ===== 70 | 71 | all: depend \ 72 | test_npl_task.exe \ 73 | test_npl_callout.exe \ 74 | test_npl_eventq.exe \ 75 | test_npl_sem.exe \ 76 | $(NULL) 77 | 78 | test_npl_task.exe: test_npl_task.o $(OBJS) 79 | $(LD) -o $@ $^ $(LDFLAGS) $(LIBS) 80 | 81 | test_npl_eventq.exe: test_npl_eventq.o $(OBJS) 82 | $(LD) -o $@ $^ $(LDFLAGS) $(LIBS) 83 | 84 | test_npl_callout.exe: test_npl_callout.o $(OBJS) 85 | $(LD) -o $@ $^ $(LDFLAGS) $(LIBS) 86 | 87 | test_npl_sem.exe: test_npl_sem.o $(OBJS) 88 | $(LD) -o $@ $^ $(LDFLAGS) $(LIBS) 89 | 90 | test: all 91 | ./test_npl_task.exe 92 | ./test_npl_callout.exe 93 | ./test_npl_eventq.exe 94 | ./test_npl_sem.exe 95 | 96 | show_objs: 97 | @echo $(OBJS) 98 | 99 | ### ===== Clean ===== 100 | clean: 101 | @echo "Cleaning artifacts." 102 | rm *~ .depend $(OBJS) *.o *.exe 103 | 104 | ### ===== Dependencies ===== 105 | ### Rebuild if headers change 106 | depend: .depend 107 | 108 | .depend: $(SRCS) $(TEST_SRCS) 109 | @echo "Building dependencies." 110 | rm -f ./.depend 111 | $(CC) $(CFLAGS) -MM $^ > ./.depend; 112 | 113 | include .depend 114 | 115 | ### Generic rules based on extension 116 | %.o: %.c 117 | $(CC) -c $(CFLAGS) $< -o $@ 118 | 119 | %.o: %.cc 120 | $(CPP) -c $(CFLAGS) $< -o $@ 121 | -------------------------------------------------------------------------------- /npl/linux/src/os_task.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | ////#include "os/os.h" 21 | #define _GNU_SOURCE //// 22 | #include //// 23 | #include "nimble/nimble_npl.h" 24 | 25 | #define OS_INVALID_PARM -1 //// 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | /** 32 | * Initialize a task. 33 | * 34 | * This function initializes the task structure pointed to by t, 35 | * clearing and setting it's stack pointer, provides sane defaults 36 | * and sets the task as ready to run, and inserts it into the operating 37 | * system scheduler. 38 | * 39 | * @param t The task to initialize 40 | * @param name The name of the task to initialize 41 | * @param func The task function to call 42 | * @param arg The argument to pass to this task function 43 | * @param prio The priority at which to run this task 44 | * @param sanity_itvl The time at which this task should check in with the 45 | * sanity task. OS_WAIT_FOREVER means never check in 46 | * here. 47 | * @param stack_bottom A pointer to the bottom of a task's stack 48 | * @param stack_size The overall size of the task's stack. 49 | * 50 | * @return 0 on success, non-zero on failure. 51 | */ 52 | int 53 | ble_npl_task_init(struct ble_npl_task *t, const char *name, ble_npl_task_func_t func, 54 | void *arg, uint8_t prio, ble_npl_time_t sanity_itvl, 55 | ble_npl_stack_t *stack_bottom, uint16_t stack_size) 56 | { 57 | int err; 58 | if ((t == NULL) || (func == NULL)) { 59 | return OS_INVALID_PARM; 60 | } 61 | 62 | err = pthread_attr_init(&t->attr); 63 | if (err) return err; 64 | err = pthread_attr_getschedparam (&t->attr, &t->param); 65 | if (err) return err; 66 | err = pthread_attr_setschedpolicy(&t->attr, SCHED_RR); 67 | if (err) return err; 68 | t->param.sched_priority = prio; 69 | err = pthread_attr_setschedparam (&t->attr, &t->param); 70 | if (err) return err; 71 | 72 | t->name = name; 73 | err = pthread_create(&t->handle, &t->attr, func, arg); 74 | 75 | return err; 76 | } 77 | 78 | /* 79 | * Removes specified task 80 | * XXX 81 | * NOTE: This interface is currently experimental and not ready for common use 82 | */ 83 | int 84 | ble_npl_task_remove(struct ble_npl_task *t) 85 | { 86 | return pthread_cancel(t->handle); 87 | } 88 | 89 | /** 90 | * Return the number of tasks initialized. 91 | * 92 | * @return number of tasks initialized 93 | */ 94 | uint8_t 95 | ble_npl_task_count(void) 96 | { 97 | return 0; 98 | } 99 | 100 | void * 101 | ble_npl_get_current_task_id(void) 102 | { 103 | return (void *)pthread_self(); 104 | } 105 | 106 | bool ble_npl_os_started(void) 107 | { 108 | return true; 109 | } 110 | 111 | void ble_npl_task_yield(void) 112 | { 113 | pthread_yield(); 114 | } 115 | 116 | #ifdef __cplusplus 117 | } 118 | #endif 119 | -------------------------------------------------------------------------------- /npl/linux/test/test_npl_mempool.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #include "test_util.h" 21 | #include "nimble/nimble_npl.h" 22 | 23 | #define TEST_MEMPOOL_BLOCKS 4 24 | #define TEST_MEMPOOL_BLOCK_SIZE 128 25 | 26 | static struct ble_npl_mempool s_mempool; 27 | 28 | static os_membuf_t s_mempool_mem[OS_MEMPOOL_SIZE(TEST_MEMPOOL_BLOCKS, 29 | TEST_MEMPOOL_BLOCK_SIZE)]; 30 | 31 | static void *s_memblock[TEST_MEMPOOL_BLOCKS]; 32 | 33 | /** 34 | * Unit test for initializing a mempool. 35 | * 36 | * ble_npl_error_t ble_npl_mempool_init(struct ble_npl_mempool *mp, int blocks, 37 | * int block_size, void *membuf, char *name); 38 | * 39 | */ 40 | int test_init(void) 41 | { 42 | int err; 43 | err = ble_npl_mempool_init(NULL, 44 | TEST_MEMPOOL_BLOCKS, 45 | TEST_MEMPOOL_BLOCK_SIZE, 46 | NULL, 47 | "Null mempool"); 48 | VerifyOrQuit(err, "ble_npl_mempool_init accepted NULL parameters."); 49 | 50 | err = ble_npl_mempool_init(&s_mempool, 51 | TEST_MEMPOOL_BLOCKS, 52 | TEST_MEMPOOL_BLOCK_SIZE, 53 | s_mempool_mem, 54 | "s_mempool"); 55 | return err; 56 | } 57 | 58 | /** 59 | * Test integrity check of a mempool. 60 | * 61 | * bool ble_npl_mempool_is_sane(const struct ble_npl_mempool *mp); 62 | */ 63 | int test_is_sane(void) 64 | { 65 | return (ble_npl_mempool_is_sane(&s_mempool)) ? PASS : FAIL; 66 | } 67 | 68 | /** 69 | * Test getting a memory block from the pool, putting it back, 70 | * and checking if it is still valid. 71 | * 72 | * void *ble_npl_memblock_get(struct ble_npl_mempool *mp); 73 | * 74 | * ble_npl_error_t ble_npl_memblock_put(struct ble_npl_mempool *mp, void *block_addr); 75 | * 76 | * int ble_npl_memblock_from(const struct ble_npl_mempool *mp, const void *block_addr); 77 | */ 78 | int test_stress(void) 79 | { 80 | int loops = 3; 81 | while(loops--) 82 | { 83 | for (int i = 0; i < 4; i++) 84 | { 85 | s_memblock[i] = ble_npl_memblock_get(&s_mempool); 86 | VerifyOrQuit(ble_npl_memblock_from(&s_mempool, s_memblock[i]), 87 | "ble_npl_memblock_get return invalid block."); 88 | } 89 | 90 | 91 | for (int i = 0; i < 4; i++) 92 | { 93 | SuccessOrQuit(ble_npl_memblock_put(&s_mempool, s_memblock[i]), 94 | "ble_npl_memblock_put refused to take valid block."); 95 | //VerifyOrQuit(!ble_npl_memblock_from(&s_mempool, s_memblock[i]), 96 | // "Block still valid after ble_npl_memblock_put."); 97 | } 98 | 99 | } 100 | return PASS; 101 | } 102 | 103 | int main(void) 104 | { 105 | SuccessOrQuit(test_init(), "Failed: ble_npl_mempool_init"); 106 | SuccessOrQuit(test_is_sane(), "Failed: ble_npl_mempool_is_sane"); 107 | SuccessOrQuit(test_stress(), "Failed: ble_npl_mempool stree test"); 108 | SuccessOrQuit(test_is_sane(), "Failed: ble_npl_mempool_is_sane"); 109 | printf("All tests passed\n"); 110 | return PASS; 111 | } 112 | -------------------------------------------------------------------------------- /npl/linux/src/os_eventq.cc: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "nimble/nimble_npl.h" 25 | #include "wqueue.h" 26 | 27 | extern "C" { 28 | 29 | typedef wqueue wqueue_t; 30 | 31 | static struct ble_npl_eventq dflt_evq; 32 | 33 | struct ble_npl_eventq * 34 | ble_npl_eventq_dflt_get(void) 35 | { 36 | if (!dflt_evq.q) { 37 | dflt_evq.q = new wqueue_t(); 38 | } 39 | 40 | return &dflt_evq; 41 | } 42 | 43 | void 44 | ble_npl_eventq_init(struct ble_npl_eventq *evq) 45 | { 46 | evq->q = new wqueue_t(); 47 | } 48 | 49 | bool 50 | ble_npl_eventq_is_empty(struct ble_npl_eventq *evq) 51 | { 52 | wqueue_t *q = static_cast(evq->q); 53 | 54 | if (q->size()) { 55 | return 1; 56 | } else { 57 | return 0; 58 | } 59 | } 60 | 61 | int 62 | ble_npl_eventq_inited(const struct ble_npl_eventq *evq) 63 | { 64 | return (evq->q != NULL); 65 | } 66 | 67 | void 68 | ble_npl_eventq_put(struct ble_npl_eventq *evq, struct ble_npl_event *ev) 69 | { 70 | wqueue_t *q = static_cast(evq->q); 71 | 72 | if (ev->ev_queued) { 73 | return; 74 | } 75 | 76 | ev->ev_queued = 1; 77 | q->put(ev); 78 | } 79 | 80 | struct ble_npl_event *ble_npl_eventq_get(struct ble_npl_eventq *evq, 81 | ble_npl_time_t tmo) 82 | { 83 | struct ble_npl_event *ev; 84 | wqueue_t *q = static_cast(evq->q); 85 | 86 | ev = q->get(tmo); 87 | 88 | if (ev) { 89 | ev->ev_queued = 0; 90 | } 91 | 92 | return ev; 93 | } 94 | 95 | void 96 | ble_npl_eventq_run(struct ble_npl_eventq *evq) 97 | { 98 | struct ble_npl_event *ev; 99 | 100 | ev = ble_npl_eventq_get(evq, BLE_NPL_TIME_FOREVER); 101 | ble_npl_event_run(ev); 102 | } 103 | 104 | 105 | // ======================================================================== 106 | // Event Implementation 107 | // ======================================================================== 108 | 109 | void 110 | ble_npl_event_init(struct ble_npl_event *ev, ble_npl_event_fn *fn, 111 | void *arg) 112 | { 113 | memset(ev, 0, sizeof(*ev)); 114 | ev->ev_cb = fn; 115 | ev->ev_arg = arg; 116 | } 117 | 118 | bool 119 | ble_npl_event_is_queued(struct ble_npl_event *ev) 120 | { 121 | return ev->ev_queued; 122 | } 123 | 124 | void * 125 | ble_npl_event_get_arg(struct ble_npl_event *ev) 126 | { 127 | return ev->ev_arg; 128 | } 129 | 130 | void 131 | ble_npl_event_set_arg(struct ble_npl_event *ev, void *arg) 132 | { 133 | ev->ev_arg = arg; 134 | } 135 | 136 | void 137 | ble_npl_event_run(struct ble_npl_event *ev) 138 | { 139 | assert(ev->ev_cb != NULL); 140 | 141 | ev->ev_cb(ev); 142 | } 143 | 144 | void 145 | ble_npl_eventq_remove(struct ble_npl_eventq *evq, struct ble_npl_event *ev) 146 | { 147 | wqueue_t *q = static_cast(evq->q); 148 | 149 | if (!ev->ev_queued) { 150 | return; 151 | } 152 | 153 | ev->ev_queued = 0; 154 | q->remove(ev); 155 | } 156 | 157 | } 158 | -------------------------------------------------------------------------------- /npl/linux/test/test_npl_eventq.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | /** 21 | Unit tests for the ble_npl_eventq api: 22 | 23 | void ble_npl_eventq_init(struct ble_npl_eventq *); 24 | void ble_npl_eventq_put(struct ble_npl_eventq *, struct ble_npl_event *); 25 | struct ble_npl_event *ble_npl_eventq_get_no_wait(struct ble_npl_eventq *evq); 26 | struct ble_npl_event *ble_npl_eventq_get(struct ble_npl_eventq *); 27 | void ble_npl_eventq_run(struct ble_npl_eventq *evq); 28 | struct ble_npl_event *ble_npl_eventq_poll(struct ble_npl_eventq **, int, ble_npl_time_t); 29 | void ble_npl_eventq_remove(struct ble_npl_eventq *, struct ble_npl_event *); 30 | struct ble_npl_eventq *ble_npl_eventq_dflt_get(void); 31 | */ 32 | 33 | #include 34 | #include 35 | #include "test_util.h" 36 | #include "nimble/nimble_npl.h" 37 | 38 | #define TEST_ARGS_VALUE (55) 39 | #define TEST_STACK_SIZE (1024) 40 | 41 | static bool s_tests_running = true; 42 | static struct ble_npl_task s_task_runner; 43 | static struct ble_npl_task s_task_dispatcher; 44 | 45 | static struct ble_npl_eventq s_eventq; 46 | static struct ble_npl_event s_event; 47 | static int s_event_args = TEST_ARGS_VALUE; 48 | 49 | 50 | void on_event(struct ble_npl_event *ev) 51 | { 52 | VerifyOrQuit(ev->ev_arg == &s_event_args, 53 | "callout: wrong args passed"); 54 | 55 | VerifyOrQuit(*(int*)ev->ev_arg == TEST_ARGS_VALUE, 56 | "callout: args corrupted"); 57 | 58 | s_tests_running = false; 59 | } 60 | 61 | int test_init(void) 62 | { 63 | //VerifyOrQuit(!ble_npl_eventq_inited(&s_eventq), "eventq: empty q initialized"); 64 | ble_npl_eventq_init(&s_eventq); 65 | //VerifyOrQuit(ble_npl_eventq_inited(&s_eventq), "eventq: not initialized"); 66 | 67 | return PASS; 68 | } 69 | 70 | int test_run(void) 71 | { 72 | while (s_tests_running) 73 | { 74 | ble_npl_eventq_run(&s_eventq); 75 | } 76 | 77 | return PASS; 78 | } 79 | 80 | int test_put(void) 81 | { 82 | s_event.ev_cb = on_event; 83 | s_event.ev_arg = &s_event_args; 84 | ble_npl_eventq_put(&s_eventq, &s_event); 85 | return PASS; 86 | } 87 | 88 | int test_get_no_wait(void) 89 | { 90 | //struct ble_npl_event *ev = ble_npl_eventq_get_no_wait(&s_eventq); 91 | return FAIL; 92 | } 93 | 94 | int test_get(void) 95 | { 96 | struct ble_npl_event *ev = ble_npl_eventq_get(&s_eventq, 97 | BLE_NPL_TIME_FOREVER); 98 | 99 | VerifyOrQuit(ev == &s_event, 100 | "callout: wrong event passed"); 101 | 102 | return PASS; 103 | } 104 | 105 | 106 | void *task_test_runner(void *args) 107 | { 108 | int count = 1000000000; 109 | 110 | SuccessOrQuit(test_init(), "eventq_init failed"); 111 | SuccessOrQuit(test_put(), "eventq_put failed"); 112 | SuccessOrQuit(test_get(), "eventq_get failed"); 113 | SuccessOrQuit(test_put(), "eventq_put failed"); 114 | SuccessOrQuit(test_run(), "eventq_run failed"); 115 | 116 | printf("All tests passed\n"); 117 | exit(PASS); 118 | 119 | return NULL; 120 | } 121 | 122 | int main(void) 123 | { 124 | SuccessOrQuit(ble_npl_task_init(&s_task_runner, 125 | "task_test_runner", 126 | task_test_runner, 127 | NULL, 1, 0, NULL, 0), 128 | "task: error initializing"); 129 | 130 | while (1) {} 131 | } 132 | -------------------------------------------------------------------------------- /npl/linux/src/os_callout.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include 26 | #include 27 | 28 | #include "nimble/nimble_npl.h" 29 | 30 | static void 31 | ble_npl_callout_timer_cb(union sigval sv) 32 | { 33 | struct ble_npl_callout *c = (struct ble_npl_callout *)sv.sival_ptr; 34 | assert(c); 35 | 36 | if (c->c_evq) { 37 | ble_npl_eventq_put(c->c_evq, &c->c_ev); 38 | } else { 39 | c->c_ev.ev_cb(&c->c_ev); 40 | } 41 | } 42 | 43 | void ble_npl_callout_init(struct ble_npl_callout *c, 44 | struct ble_npl_eventq *evq, 45 | ble_npl_event_fn *ev_cb, 46 | void *ev_arg) 47 | { 48 | struct sigevent event; 49 | 50 | /* Initialize the callout. */ 51 | memset(c, 0, sizeof(*c)); 52 | c->c_ev.ev_cb = ev_cb; 53 | c->c_ev.ev_arg = ev_arg; 54 | c->c_evq = evq; 55 | c->c_active = false; 56 | 57 | event.sigev_notify = SIGEV_THREAD; 58 | event.sigev_value.sival_ptr = c; // put callout obj in signal args 59 | event.sigev_notify_function = ble_npl_callout_timer_cb; 60 | event.sigev_notify_attributes = NULL; 61 | 62 | timer_create(CLOCK_REALTIME, &event, &c->c_timer); 63 | } 64 | 65 | bool ble_npl_callout_is_active(struct ble_npl_callout *c) 66 | { 67 | // TODO: seek native posix method to determine whether timer_t is active. 68 | // TODO: fix bug where one-shot timer is still active after fired. 69 | return c->c_active; 70 | } 71 | 72 | int ble_npl_callout_inited(struct ble_npl_callout *c) 73 | { 74 | return (c->c_timer != NULL); 75 | } 76 | 77 | ble_npl_error_t ble_npl_callout_reset(struct ble_npl_callout *c, 78 | ble_npl_time_t ticks) 79 | { 80 | struct itimerspec its; 81 | 82 | if (ticks < 0) { 83 | return BLE_NPL_EINVAL; 84 | } 85 | 86 | if (ticks == 0) { 87 | ticks = 1; 88 | } 89 | 90 | c->c_ticks = ble_npl_time_get() + ticks; 91 | 92 | its.it_interval.tv_sec = 0; 93 | its.it_interval.tv_nsec = 0; // one shot 94 | its.it_value.tv_sec = (ticks / 1000); 95 | its.it_value.tv_nsec = (ticks % 1000) * 1000000; // expiration 96 | its.it_value.tv_nsec %= 1000000000; 97 | c->c_active = true; 98 | timer_settime(c->c_timer, 0, &its, NULL); 99 | 100 | return BLE_NPL_OK; 101 | } 102 | 103 | int ble_npl_callout_queued(struct ble_npl_callout *c) 104 | { 105 | struct itimerspec its; 106 | timer_gettime(c->c_timer, &its); 107 | 108 | return ((its.it_value.tv_sec > 0) || 109 | (its.it_value.tv_nsec > 0)); 110 | } 111 | 112 | void ble_npl_callout_stop(struct ble_npl_callout *c) 113 | { 114 | if (!ble_npl_callout_inited(c)) { 115 | return; 116 | } 117 | 118 | struct itimerspec its; 119 | its.it_interval.tv_sec = 0; 120 | its.it_interval.tv_nsec = 0; 121 | its.it_value.tv_sec = 0; 122 | its.it_value.tv_nsec = 0; 123 | timer_settime(c->c_timer, 0, &its, NULL); 124 | c->c_active = false; 125 | } 126 | 127 | ble_npl_time_t 128 | ble_npl_callout_get_ticks(struct ble_npl_callout *co) 129 | { 130 | return co->c_ticks; 131 | } 132 | 133 | void 134 | ble_npl_callout_set_arg(struct ble_npl_callout *co, void *arg) 135 | { 136 | co->c_ev.ev_arg = arg; 137 | } 138 | 139 | uint32_t 140 | ble_npl_callout_remaining_ticks(struct ble_npl_callout *co, 141 | ble_npl_time_t now) 142 | { 143 | ble_npl_time_t rt; 144 | uint32_t exp; 145 | 146 | struct itimerspec its; 147 | timer_gettime(co->c_timer, &its); 148 | 149 | exp = its.it_value.tv_sec * 1000; 150 | 151 | if (exp > now) { 152 | rt = exp - now; 153 | } else { 154 | rt = 0; 155 | } 156 | 157 | return rt; 158 | } 159 | -------------------------------------------------------------------------------- /npl/linux/test/test_npl_sem.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | /** 21 | Unit tests for the Semaphore api (ble_npl_sem): 22 | 23 | ble_npl_error_t ble_npl_sem_init(struct ble_npl_sem *sem, uint16_t tokens); 24 | ble_npl_error_t ble_npl_sem_release(struct ble_npl_sem *sem); 25 | ble_npl_error_t ble_npl_sem_pend(struct ble_npl_sem *sem, uint32_t timeout); 26 | uint16_t ble_npl_sem_get_count(struct ble_npl_sem *sem); 27 | */ 28 | 29 | #include "test_util.h" 30 | #include "nimble/nimble_npl.h" 31 | //#include "os/os.h" 32 | 33 | #define TEST_ITERATIONS 10 34 | 35 | #define TASK1_PRIO 1 36 | #define TASK2_PRIO 1 37 | 38 | #define TASK1_STACK_SIZE 1028 39 | #define TASK2_STACK_SIZE 1028 40 | 41 | static struct ble_npl_task task1; 42 | static struct ble_npl_task task2; 43 | 44 | static ble_npl_stack_t task1_stack[TASK1_STACK_SIZE]; 45 | static ble_npl_stack_t task2_stack[TASK2_STACK_SIZE]; 46 | 47 | struct ble_npl_sem task1_sem; 48 | struct ble_npl_sem task2_sem; 49 | 50 | /* Task 1 handler function */ 51 | void * 52 | task1_handler(void *arg) 53 | { 54 | for (int i = 0; i < TEST_ITERATIONS; i++) 55 | { 56 | /* Release semaphore to task 2 */ 57 | SuccessOrQuit(ble_npl_sem_release(&task1_sem), 58 | "ble_npl_sem_release: error releasing task2_sem."); 59 | 60 | /* Wait for semaphore from task 2 */ 61 | SuccessOrQuit(ble_npl_sem_pend(&task2_sem, BLE_NPL_TIME_FOREVER), 62 | "ble_npl_sem_pend: error waiting for task2_sem."); 63 | } 64 | 65 | printf("All tests passed\n"); 66 | exit(PASS); 67 | 68 | return NULL; 69 | } 70 | 71 | /* Task 2 handler function */ 72 | void * 73 | task2_handler(void *arg) 74 | { 75 | while(1) 76 | { 77 | /* Wait for semaphore from task1 */ 78 | SuccessOrQuit(ble_npl_sem_pend(&task1_sem, BLE_NPL_TIME_FOREVER), 79 | "ble_npl_sem_pend: error waiting for task1_sem."); 80 | 81 | /* Release task2 semaphore */ 82 | SuccessOrQuit(ble_npl_sem_release(&task2_sem), 83 | "ble_npl_sem_release: error releasing task1_sem."); 84 | } 85 | 86 | return NULL; 87 | } 88 | 89 | 90 | /* Initialize task 1 exposed data objects */ 91 | void 92 | task1_init(void) 93 | { 94 | /* Initialize task1 semaphore */ 95 | SuccessOrQuit(ble_npl_sem_init(&task1_sem, 0), 96 | "ble_npl_sem_init: task1 returned error."); 97 | } 98 | 99 | /* Initialize task 2 exposed data objects */ 100 | void 101 | task2_init(void) 102 | { 103 | /* Initialize task1 semaphore */ 104 | SuccessOrQuit(ble_npl_sem_init(&task2_sem, 0), 105 | "ble_npl_sem_init: task2 returned error."); 106 | } 107 | 108 | /** 109 | * init_app_tasks 110 | * 111 | * This function performs initializations that are required before tasks run. 112 | * 113 | * @return int 0 success; error otherwise. 114 | */ 115 | static int 116 | init_app_tasks(void) 117 | { 118 | /* 119 | * Call task specific initialization functions to initialize any shared objects 120 | * before initializing the tasks with the OS. 121 | */ 122 | task1_init(); 123 | task2_init(); 124 | 125 | /* 126 | * Initialize tasks 1 and 2 with the OS. 127 | */ 128 | ble_npl_task_init(&task1, "task1", task1_handler, NULL, TASK1_PRIO, 129 | BLE_NPL_TIME_FOREVER, task1_stack, TASK1_STACK_SIZE); 130 | 131 | ble_npl_task_init(&task2, "task2", task2_handler, NULL, TASK2_PRIO, 132 | BLE_NPL_TIME_FOREVER, task2_stack, TASK2_STACK_SIZE); 133 | 134 | return 0; 135 | } 136 | 137 | /** 138 | * main 139 | * 140 | * The main function for the application. This function initializes the system and packages, 141 | * calls the application specific task initialization function, then waits and dispatches 142 | * events from the OS default event queue in an infinite loop. 143 | */ 144 | int 145 | main(int argc, char **arg) 146 | { 147 | /* Initialize application specific tasks */ 148 | init_app_tasks(); 149 | 150 | while (1) 151 | { 152 | ble_npl_eventq_run(ble_npl_eventq_dflt_get()); 153 | } 154 | /* main never returns */ 155 | } 156 | -------------------------------------------------------------------------------- /npl/linux/include/nimble/nimble_npl.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one 3 | * or more contributor license agreements. See the NOTICE file 4 | * distributed with this work for additional information 5 | * regarding copyright ownership. The ASF licenses this file 6 | * to you under the Apache License, Version 2.0 (the 7 | * "License"); you may not use this file except in compliance 8 | * with the License. You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, 13 | * software distributed under the License is distributed on an 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | * KIND, either express or implied. See the License for the 16 | * specific language governing permissions and limitations 17 | * under the License. 18 | */ 19 | 20 | #ifndef _NIMBLE_NPL_H_ 21 | #define _NIMBLE_NPL_H_ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | #ifdef __cplusplus 28 | extern "C" { 29 | #endif 30 | 31 | struct ble_npl_event; 32 | typedef void ble_npl_event_fn(struct ble_npl_event *ev); 33 | 34 | enum ble_npl_error { 35 | BLE_NPL_OK = 0, 36 | BLE_NPL_ENOMEM = 1, 37 | BLE_NPL_EINVAL = 2, 38 | BLE_NPL_INVALID_PARAM = 3, 39 | BLE_NPL_MEM_NOT_ALIGNED = 4, 40 | BLE_NPL_BAD_MUTEX = 5, 41 | BLE_NPL_TIMEOUT = 6, 42 | BLE_NPL_ERR_IN_ISR = 7, 43 | BLE_NPL_ERR_PRIV = 8, 44 | BLE_NPL_OS_NOT_STARTED = 9, 45 | BLE_NPL_ENOENT = 10, 46 | BLE_NPL_EBUSY = 11, 47 | BLE_NPL_ERROR = 12, 48 | }; 49 | 50 | typedef enum ble_npl_error ble_npl_error_t; 51 | 52 | /* Include OS-specific definitions */ 53 | #include "nimble/nimble_npl_os.h" 54 | 55 | /* 56 | * Generic 57 | */ 58 | 59 | bool ble_npl_os_started(void); 60 | 61 | void *ble_npl_get_current_task_id(void); 62 | 63 | /* 64 | * Event queue 65 | */ 66 | 67 | void ble_npl_eventq_init(struct ble_npl_eventq *evq); 68 | 69 | struct ble_npl_event *ble_npl_eventq_get(struct ble_npl_eventq *evq, 70 | ble_npl_time_t tmo); 71 | 72 | void ble_npl_eventq_put(struct ble_npl_eventq *evq, struct ble_npl_event *ev); 73 | 74 | void ble_npl_eventq_remove(struct ble_npl_eventq *evq, 75 | struct ble_npl_event *ev); 76 | 77 | void ble_npl_event_init(struct ble_npl_event *ev, ble_npl_event_fn *fn, 78 | void *arg); 79 | 80 | bool ble_npl_event_is_queued(struct ble_npl_event *ev); 81 | 82 | void *ble_npl_event_get_arg(struct ble_npl_event *ev); 83 | 84 | void ble_npl_event_set_arg(struct ble_npl_event *ev, void *arg); 85 | 86 | bool ble_npl_eventq_is_empty(struct ble_npl_eventq *evq); 87 | 88 | void ble_npl_event_run(struct ble_npl_event *ev); 89 | 90 | /* 91 | * Mutexes 92 | */ 93 | 94 | ble_npl_error_t ble_npl_mutex_init(struct ble_npl_mutex *mu); 95 | 96 | ble_npl_error_t ble_npl_mutex_pend(struct ble_npl_mutex *mu, 97 | ble_npl_time_t timeout); 98 | 99 | ble_npl_error_t ble_npl_mutex_release(struct ble_npl_mutex *mu); 100 | 101 | /* 102 | * Semaphores 103 | */ 104 | 105 | ble_npl_error_t ble_npl_sem_init(struct ble_npl_sem *sem, uint16_t tokens); 106 | 107 | ble_npl_error_t ble_npl_sem_pend(struct ble_npl_sem *sem, 108 | ble_npl_time_t timeout); 109 | 110 | ble_npl_error_t ble_npl_sem_release(struct ble_npl_sem *sem); 111 | 112 | uint16_t ble_npl_sem_get_count(struct ble_npl_sem *sem); 113 | 114 | /* 115 | * Callouts 116 | */ 117 | 118 | void ble_npl_callout_init(struct ble_npl_callout *co, struct ble_npl_eventq *evq, 119 | ble_npl_event_fn *ev_cb, void *ev_arg); 120 | 121 | ble_npl_error_t ble_npl_callout_reset(struct ble_npl_callout *co, 122 | ble_npl_time_t ticks); 123 | 124 | void ble_npl_callout_stop(struct ble_npl_callout *co); 125 | 126 | bool ble_npl_callout_is_active(struct ble_npl_callout *co); 127 | 128 | ble_npl_time_t ble_npl_callout_get_ticks(struct ble_npl_callout *co); 129 | 130 | ble_npl_time_t ble_npl_callout_remaining_ticks(struct ble_npl_callout *co, 131 | ble_npl_time_t time); 132 | 133 | void ble_npl_callout_set_arg(struct ble_npl_callout *co, 134 | void *arg); 135 | /* 136 | * Time functions 137 | */ 138 | 139 | ble_npl_time_t ble_npl_time_get(void); 140 | 141 | ble_npl_error_t ble_npl_time_ms_to_ticks(uint32_t ms, ble_npl_time_t *out_ticks); 142 | 143 | ble_npl_error_t ble_npl_time_ticks_to_ms(ble_npl_time_t ticks, uint32_t *out_ms); 144 | 145 | ble_npl_time_t ble_npl_time_ms_to_ticks32(uint32_t ms); 146 | 147 | uint32_t ble_npl_time_ticks_to_ms32(ble_npl_time_t ticks); 148 | 149 | void ble_npl_time_delay(ble_npl_time_t ticks); 150 | 151 | /* 152 | * Hardware-specific 153 | * 154 | * These symbols should be most likely defined by application since they are 155 | * specific to hardware, not to OS. 156 | */ 157 | 158 | #if NIMBLE_CFG_CONTROLLER 159 | 160 | void ble_npl_hw_set_isr(int irqn, void (*addr)(void)); 161 | 162 | #endif 163 | 164 | uint32_t ble_npl_hw_enter_critical(void); 165 | 166 | void ble_npl_hw_exit_critical(uint32_t ctx); 167 | 168 | bool ble_npl_hw_is_in_critical(void); 169 | 170 | #ifdef __cplusplus 171 | } 172 | #endif 173 | 174 | #endif /* _NIMBLE_NPL_H_ */ 175 | -------------------------------------------------------------------------------- /include/sx126x-board.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file sx126x-board.h 3 | * 4 | * \brief Target board SX126x driver implementation 5 | * 6 | * \copyright Revised BSD License, see section \ref LICENSE. 7 | * 8 | * \code 9 | * ______ _ 10 | * / _____) _ | | 11 | * ( (____ _____ ____ _| |_ _____ ____| |__ 12 | * \____ \| ___ | (_ _) ___ |/ ___) _ \ 13 | * _____) ) ____| | | || |_| ____( (___| | | | 14 | * (______/|_____)_|_|_| \__)_____)\____)_| |_| 15 | * (C)2013-2017 Semtech 16 | * 17 | * \endcode 18 | * 19 | * \author Miguel Luis ( Semtech ) 20 | * 21 | * \author Gregory Cristian ( Semtech ) 22 | */ 23 | #ifndef __SX126x_BOARD_H__ 24 | #define __SX126x_BOARD_H__ 25 | 26 | #ifdef __cplusplus 27 | extern "C" 28 | { 29 | #endif 30 | 31 | #include 32 | #include 33 | #include "sx126x.h" 34 | 35 | // Below are the pin numbers for PineDio Stack BL604 with onboard SX1262. 36 | // TODO: Change the pin numbers for your SX1262 connection to BL602 / BL604 37 | 38 | #define SX126X_SPI_IDX 0 // SPI Port 0 39 | #define SX126X_SPI_SDI_PIN 0 // SPI Serial Data In Pin (formerly MISO) 40 | #define SX126X_SPI_SDO_PIN 17 // SPI Serial Data Out Pin (formerly MOSI) 41 | #define SX126X_SPI_CLK_PIN 11 // SPI Clock Pin 42 | #define SX126X_SPI_CS_PIN 15 // SPI Chip Select Pin 43 | #define SX126X_SPI_CS_OLD 8 // Unused SPI Chip Select Pin 44 | #define SX126X_NRESET 18 // Reset Pin 45 | #define SX126X_DIO1 19 // DIO1 46 | #define SX126X_BUSY_PIN 10 // Busy Pin 47 | #define SX126X_DEBUG_CS_PIN 5 // Debug Chip Select Pin, mirrors the High / Low State of SX1262 Chip Select Pin. Set to -1 if not needed. 48 | #define SX126X_TCXO_WAKEUP_TIME 5 // Time required for the TCXO to wakeup (milliseconds) 49 | #define SX126X_SPI_BAUDRATE (200 * 1000) // SPI Frequency (200 kHz) 50 | 51 | // Timer definition for BL602 52 | #include "nimble_npl.h" // For NimBLE Porting Layer (timer functions) 53 | typedef struct ble_npl_callout TimerEvent_t; 54 | 55 | /////////////////////////////////////////////////////////////////////////////// 56 | // BL602 Functions 57 | 58 | // TODO: Implement critical section functions 59 | #define CRITICAL_SECTION_BEGIN(...) 60 | #define CRITICAL_SECTION_END(...) 61 | 62 | /// Initialise a timer 63 | void TimerInit( 64 | struct ble_npl_callout *timer, // The timer to initialize. Cannot be NULL. 65 | ble_npl_event_fn *f); // The timer callback function. Cannot be NULL. 66 | 67 | /// Stops a timer from running. Can be called even if timer is not running. 68 | void TimerStop( 69 | struct ble_npl_callout *timer); // Pointer to timer to stop. Cannot be NULL. 70 | 71 | /// Sets a timer that will expire ‘usecs’ microseconds from the current time. 72 | void TimerStart( 73 | struct ble_npl_callout *timer, // Pointer to timer. Cannot be NULL. 74 | uint32_t microsecs); // The number of microseconds from now at which the timer will expire. 75 | 76 | /// Wait until ‘millisecs’ milliseconds has elapsed. This is a blocking delay. 77 | void DelayMs(uint32_t millisecs); // The number of milliseconds to wait. 78 | 79 | /// Return current time in microseconds 80 | uint32_t TimerGetCurrentTime(void); 81 | 82 | /// Return elased time in microseconds 83 | uint32_t TimerGetElapsedTime(uint32_t saved_time); 84 | 85 | /////////////////////////////////////////////////////////////////////////////// 86 | 87 | /*! 88 | * \brief Initializes the radio I/Os pins interface 89 | */ 90 | void SX126xIoInit( void ); 91 | 92 | /*! 93 | * \brief Initializes DIO IRQ handlers 94 | * 95 | * \param [IN] irqHandlers Array containing the IRQ callback functions 96 | */ 97 | void SX126xIoIrqInit( DioIrqHandler dioIrq ); 98 | 99 | /*! 100 | * \brief De-initializes the radio I/Os pins interface. 101 | * 102 | * \remark Useful when going in MCU low power modes 103 | */ 104 | void SX126xIoDeInit( void ); 105 | 106 | /*! 107 | * \brief Initializes the TCXO power pin. 108 | */ 109 | void SX126xIoTcxoInit( void ); 110 | 111 | /*! 112 | * \brief Initializes RF switch control pins. 113 | */ 114 | void SX126xIoRfSwitchInit( void ); 115 | 116 | /*! 117 | * \brief Initializes the radio debug pins. 118 | */ 119 | void SX126xIoDbgInit( void ); 120 | 121 | /*! 122 | * \brief HW Reset of the radio 123 | */ 124 | void SX126xReset( void ); 125 | 126 | /*! 127 | * \brief Blocking loop to wait while the Busy pin in high 128 | */ 129 | void SX126xWaitOnBusy( void ); 130 | 131 | /*! 132 | * \brief Wakes up the radio 133 | */ 134 | void SX126xWakeup( void ); 135 | 136 | /*! 137 | * \brief Send a command that write data to the radio 138 | * 139 | * \param [in] opcode Opcode of the command 140 | * \param [in] buffer Buffer to be send to the radio 141 | * \param [in] size Size of the buffer to send 142 | */ 143 | void SX126xWriteCommand( RadioCommands_t opcode, uint8_t *buffer, uint16_t size ); 144 | 145 | /*! 146 | * \brief Send a command that read data from the radio 147 | * 148 | * \param [in] opcode Opcode of the command 149 | * \param [out] buffer Buffer holding data from the radio 150 | * \param [in] size Size of the buffer 151 | * 152 | * \retval status Return command radio status 153 | */ 154 | uint8_t SX126xReadCommand( RadioCommands_t opcode, uint8_t *buffer, uint16_t size ); 155 | 156 | /*! 157 | * \brief Write a single byte of data to the radio memory 158 | * 159 | * \param [in] address The address of the first byte to write in the radio 160 | * \param [in] value The data to be written in radio's memory 161 | */ 162 | void SX126xWriteRegister( uint16_t address, uint8_t value ); 163 | 164 | /*! 165 | * \brief Read a single byte of data from the radio memory 166 | * 167 | * \param [in] address The address of the first byte to write in the radio 168 | * 169 | * \retval value The value of the byte at the given address in radio's memory 170 | */ 171 | uint8_t SX126xReadRegister( uint16_t address ); 172 | 173 | /*! 174 | * \brief Sets the radio output power. 175 | * 176 | * \param [IN] power Sets the RF output power 177 | */ 178 | void SX126xSetRfTxPower( int8_t power ); 179 | 180 | /*! 181 | * \brief Gets the device ID 182 | * 183 | * \retval id Connected device ID 184 | */ 185 | uint8_t SX126xGetDeviceId( void ); 186 | 187 | /*! 188 | * \brief Initializes the RF Switch I/Os pins interface 189 | */ 190 | void SX126xAntSwOn( void ); 191 | 192 | /*! 193 | * \brief De-initializes the RF Switch I/Os pins interface 194 | * 195 | * \remark Needed to decrease the power consumption in MCU low power modes 196 | */ 197 | void SX126xAntSwOff( void ); 198 | 199 | /*! 200 | * \brief Checks if the given RF frequency is supported by the hardware 201 | * 202 | * \param [IN] frequency RF frequency to be checked 203 | * \retval isSupported [true: supported, false: unsupported] 204 | */ 205 | bool SX126xCheckRfFrequency( uint32_t frequency ); 206 | 207 | /*! 208 | * \brief Gets the Defines the time required for the TCXO to wakeup [ms]. 209 | * 210 | * \retval time Board TCXO wakeup time in ms. 211 | */ 212 | uint32_t SX126xGetBoardTcxoWakeupTime( void ); 213 | 214 | /*! 215 | * \brief Gets current state of DIO1 pin state. 216 | * 217 | * \retval state DIO1 pin current state. 218 | */ 219 | uint32_t SX126xGetDio1PinState( void ); 220 | 221 | /*! 222 | * \brief Gets the current Radio OperationMode variable 223 | * 224 | * \retval RadioOperatingModes_t last operating mode 225 | */ 226 | RadioOperatingModes_t SX126xGetOperatingMode( void ); 227 | 228 | /*! 229 | * \brief Sets/Updates the current Radio OperationMode variable. 230 | * 231 | * \remark WARNING: This function is only required to reflect the current radio 232 | * operating mode when processing interrupts. 233 | * 234 | * \param [in] mode New operating mode 235 | */ 236 | void SX126xSetOperatingMode( RadioOperatingModes_t mode ); 237 | 238 | /*! 239 | * Radio hardware and global parameters 240 | */ 241 | extern SX126x_t SX126x; 242 | 243 | #ifdef __cplusplus 244 | } 245 | #endif 246 | 247 | #endif // __SX126x_BOARD_H__ 248 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | // Demo Program for LoRa SX1262 on Linux (PineDio USB) 2 | #ifndef ARCH_RISCV // This file is for Linux only 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "radio.h" 8 | #include "sx126x-board.h" 9 | 10 | /// TODO: We are using LoRa Frequency 923 MHz for Singapore. Change this for your region. 11 | #define USE_BAND_923 12 | 13 | #if defined(USE_BAND_433) 14 | #define RF_FREQUENCY 434000000 /* Hz */ 15 | #elif defined(USE_BAND_780) 16 | #define RF_FREQUENCY 780000000 /* Hz */ 17 | #elif defined(USE_BAND_868) 18 | #define RF_FREQUENCY 868000000 /* Hz */ 19 | #elif defined(USE_BAND_915) 20 | #define RF_FREQUENCY 915000000 /* Hz */ 21 | #elif defined(USE_BAND_923) 22 | #define RF_FREQUENCY 923000000 /* Hz */ 23 | #else 24 | #error "Please define a frequency band in the compiler options." 25 | #endif 26 | 27 | /// LoRa Parameters 28 | #define LORAPING_TX_OUTPUT_POWER 14 /* dBm */ 29 | 30 | #define LORAPING_BANDWIDTH 0 /* [0: 125 kHz, */ 31 | /* 1: 250 kHz, */ 32 | /* 2: 500 kHz, */ 33 | /* 3: Reserved] */ 34 | #define LORAPING_SPREADING_FACTOR 7 /* [SF7..SF12] */ 35 | #define LORAPING_CODINGRATE 1 /* [1: 4/5, */ 36 | /* 2: 4/6, */ 37 | /* 3: 4/7, */ 38 | /* 4: 4/8] */ 39 | #define LORAPING_PREAMBLE_LENGTH 8 /* Same for Tx and Rx */ 40 | #define LORAPING_SYMBOL_TIMEOUT 5 /* Symbols */ 41 | #define LORAPING_FIX_LENGTH_PAYLOAD_ON false 42 | #define LORAPING_IQ_INVERSION_ON false 43 | 44 | #define LORAPING_TX_TIMEOUT_MS 3000 /* ms */ 45 | #define LORAPING_RX_TIMEOUT_MS 10000 /* ms */ 46 | #define LORAPING_BUFFER_SIZE 64 /* LoRa message size */ 47 | 48 | const uint8_t loraping_ping_msg[] = "PING"; // We send a "PING" message 49 | const uint8_t loraping_pong_msg[] = "PONG"; // We expect a "PONG" response 50 | 51 | static uint8_t loraping_buffer[LORAPING_BUFFER_SIZE]; // 64-byte buffer for our LoRa messages 52 | static int loraping_rx_size; 53 | 54 | /// LoRa Statistics 55 | struct { 56 | int rx_timeout; 57 | int rx_ping; 58 | int rx_pong; 59 | int rx_other; 60 | int rx_error; 61 | int tx_timeout; 62 | int tx_success; 63 | } loraping_stats; 64 | 65 | /////////////////////////////////////////////////////////////////////////////// 66 | // Main Function 67 | 68 | static void read_registers(void); 69 | static void init_driver(void); 70 | static void send_message(void); 71 | static void receive_message(void); 72 | static void create_task(void); 73 | 74 | int main(void) { 75 | 76 | // Uncomment to read SX1262 registers 77 | // #define READ_REGISTERS 78 | #ifdef READ_REGISTERS 79 | // Read SX1262 registers 0x00 to 0x0F 80 | read_registers(); 81 | #endif // READ_REGISTERS 82 | 83 | // TODO: Create a Background Thread to handle LoRa Events 84 | create_task(); 85 | 86 | // Init SX1262 driver 87 | init_driver(); 88 | 89 | // TODO: Do we need to wait? 90 | sleep(1); 91 | 92 | // Uncomment to send a LoRa message 93 | // #define SEND_MESSAGE 94 | #ifdef SEND_MESSAGE 95 | // Send a LoRa message 96 | send_message(); 97 | #endif // SEND_MESSAGE 98 | 99 | // Uncomment to receive a LoRa message 100 | #define RECEIVE_MESSAGE 101 | #ifdef RECEIVE_MESSAGE 102 | // Handle LoRa events for the next 10 seconds 103 | for (int i = 0; i < 10; i++) { 104 | // Prepare to receive a LoRa message 105 | receive_message(); 106 | 107 | // Process the received LoRa message, if any 108 | RadioOnDioIrq(NULL); 109 | 110 | // Sleep for 1 second 111 | usleep(1000 * 1000); 112 | } 113 | #endif // RECEIVE_MESSAGE 114 | 115 | // TODO: Close the SPI Bus 116 | // close(spi); 117 | puts("Done!"); 118 | return 0; 119 | } 120 | 121 | /////////////////////////////////////////////////////////////////////////////// 122 | // LoRa Commands 123 | 124 | static void send_once(int is_ping); 125 | static void on_tx_done(void); 126 | static void on_rx_done(uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr); 127 | static void on_tx_timeout(void); 128 | static void on_rx_timeout(void); 129 | static void on_rx_error(void); 130 | 131 | /// Read SX1262 registers 132 | static void read_registers(void) { 133 | puts("read_registers"); 134 | 135 | // Init the SPI port 136 | SX126xIoInit(); 137 | 138 | // Read and print the first 16 registers: 0 to 15 139 | for (uint16_t addr = 0; addr < 0x10; addr++) { 140 | // Read the register 141 | uint8_t val = SX126xReadRegister(addr); // For SX1262 142 | // uint8_t val = SX1276Read(addr); // For SX1276 143 | 144 | // Print the register value 145 | printf("Register 0x%02x = 0x%02x\r\n", addr, val); 146 | } 147 | } 148 | 149 | /// Initialise the SX1262 driver. 150 | /// Assume that create_task has been called to init the Event Queue. 151 | static void init_driver(void) { 152 | puts("init_driver"); 153 | 154 | // Set the LoRa Callback Functions 155 | RadioEvents_t radio_events; 156 | memset(&radio_events, 0, sizeof(radio_events)); // Must init radio_events to null, because radio_events lives on stack! 157 | radio_events.TxDone = on_tx_done; 158 | radio_events.RxDone = on_rx_done; 159 | radio_events.TxTimeout = on_tx_timeout; 160 | radio_events.RxTimeout = on_rx_timeout; 161 | radio_events.RxError = on_rx_error; 162 | 163 | // Init the SPI Port and the LoRa Transceiver 164 | Radio.Init(&radio_events); 165 | 166 | // Set the LoRa Frequency 167 | Radio.SetChannel(RF_FREQUENCY); 168 | 169 | // Configure the LoRa Transceiver for transmitting messages 170 | Radio.SetTxConfig( 171 | MODEM_LORA, 172 | LORAPING_TX_OUTPUT_POWER, 173 | 0, // Frequency deviation: Unused with LoRa 174 | LORAPING_BANDWIDTH, 175 | LORAPING_SPREADING_FACTOR, 176 | LORAPING_CODINGRATE, 177 | LORAPING_PREAMBLE_LENGTH, 178 | LORAPING_FIX_LENGTH_PAYLOAD_ON, 179 | true, // CRC enabled 180 | 0, // Frequency hopping disabled 181 | 0, // Hop period: N/A 182 | LORAPING_IQ_INVERSION_ON, 183 | LORAPING_TX_TIMEOUT_MS 184 | ); 185 | 186 | // Configure the LoRa Transceiver for receiving messages 187 | Radio.SetRxConfig( 188 | MODEM_LORA, 189 | LORAPING_BANDWIDTH, 190 | LORAPING_SPREADING_FACTOR, 191 | LORAPING_CODINGRATE, 192 | 0, // AFC bandwidth: Unused with LoRa 193 | LORAPING_PREAMBLE_LENGTH, 194 | LORAPING_SYMBOL_TIMEOUT, 195 | LORAPING_FIX_LENGTH_PAYLOAD_ON, 196 | 0, // Fixed payload length: N/A 197 | true, // CRC enabled 198 | 0, // Frequency hopping disabled 199 | 0, // Hop period: N/A 200 | LORAPING_IQ_INVERSION_ON, 201 | true // Continuous receive mode 202 | ); 203 | } 204 | 205 | /// Send a LoRa message. Assume that SX1262 driver has been initialised. 206 | static void send_message(void) { 207 | puts("send_message"); 208 | 209 | // Send the "PING" message 210 | send_once(1); 211 | } 212 | 213 | /// Send a LoRa message. If is_ping is 0, send "PONG". Otherwise send "PING". 214 | static void send_once(int is_ping) { 215 | // Copy the "PING" or "PONG" message to the transmit buffer 216 | if (is_ping) { 217 | memcpy(loraping_buffer, loraping_ping_msg, 4); 218 | } else { 219 | memcpy(loraping_buffer, loraping_pong_msg, 4); 220 | } 221 | 222 | // Fill up the remaining space in the transmit buffer (64 bytes) with values 0, 1, 2, ... 223 | for (int i = 4; i < sizeof loraping_buffer; i++) { 224 | loraping_buffer[i] = i - 4; 225 | } 226 | 227 | // We send the transmit buffer, limited to 29 bytes. 228 | // CAUTION: Anything more will cause message corruption! 229 | #define MAX_MESSAGE_SIZE 29 230 | uint8_t size = sizeof loraping_buffer > MAX_MESSAGE_SIZE 231 | ? MAX_MESSAGE_SIZE 232 | : sizeof loraping_buffer; 233 | Radio.Send(loraping_buffer, size); 234 | 235 | // TODO: Previously we send 64 bytes, which gets garbled consistently. 236 | // Does CH341 limit SPI transfers to 31 bytes? 237 | // (Including 2 bytes for SX1262 SPI command header) 238 | // Radio.Send(loraping_buffer, sizeof loraping_buffer); 239 | } 240 | 241 | /// Receive a LoRa message. Assume that SX1262 driver has been initialised. 242 | /// Assume that create_task has been called to init the Event Queue. 243 | static void receive_message(void) { 244 | puts("receive_message"); 245 | 246 | // Receive a LoRa message within the timeout period 247 | Radio.Rx(LORAPING_RX_TIMEOUT_MS); 248 | } 249 | 250 | /////////////////////////////////////////////////////////////////////////////// 251 | // LoRa Callback Functions 252 | 253 | /// Callback Function that is called when our LoRa message has been transmitted 254 | static void on_tx_done(void) 255 | { 256 | printf("Tx done\r\n"); 257 | 258 | // Log the success status 259 | loraping_stats.tx_success++; 260 | 261 | // Switch the LoRa Transceiver to low power, sleep mode 262 | Radio.Sleep(); 263 | 264 | // TODO: Receive a "PING" or "PONG" LoRa message 265 | // os_eventq_put(os_eventq_dflt_get(), &loraping_ev_rx); 266 | } 267 | 268 | /// Callback Function that is called when a LoRa message has been received 269 | static void on_rx_done( 270 | uint8_t *payload, // Buffer containing received LoRa message 271 | uint16_t size, // Size of the LoRa message 272 | int16_t rssi, // Signal strength 273 | int8_t snr) // Signal To Noise ratio 274 | { 275 | printf("Rx done: \r\n"); 276 | 277 | // Switch the LoRa Transceiver to low power, sleep mode 278 | Radio.Sleep(); 279 | 280 | // Copy the received packet 281 | if (size > sizeof loraping_buffer) { 282 | size = sizeof loraping_buffer; 283 | } 284 | loraping_rx_size = size; 285 | memcpy(loraping_buffer, payload, size); 286 | 287 | // Log the signal strength, signal to noise ratio 288 | // TODO: loraping_rxinfo_rxed(rssi, snr); 289 | 290 | // Dump the contents of the received packet 291 | for (int i = 0; i < loraping_rx_size; i++) { 292 | printf("%02x ", loraping_buffer[i]); 293 | } 294 | printf("\r\n"); 295 | 296 | // TODO: Send a "PING" or "PONG" LoRa message 297 | // os_eventq_put(os_eventq_dflt_get(), &loraping_ev_tx); 298 | } 299 | 300 | /// Callback Function that is called when our LoRa message couldn't be transmitted due to timeout 301 | static void on_tx_timeout(void) { 302 | printf("Tx timeout\r\n"); 303 | 304 | // Switch the LoRa Transceiver to low power, sleep mode 305 | Radio.Sleep(); 306 | 307 | // Log the timeout 308 | loraping_stats.tx_timeout++; 309 | 310 | // TODO: Receive a "PING" or "PONG" LoRa message 311 | // os_eventq_put(os_eventq_dflt_get(), &loraping_ev_rx); 312 | } 313 | 314 | /// Callback Function that is called when no LoRa messages could be received due to timeout 315 | static void on_rx_timeout(void) { 316 | printf("Rx timeout\r\n"); 317 | 318 | // Switch the LoRa Transceiver to low power, sleep mode 319 | Radio.Sleep(); 320 | 321 | // Log the timeout 322 | loraping_stats.rx_timeout++; 323 | // TODO: loraping_rxinfo_timeout(); 324 | 325 | // TODO: Send a "PING" or "PONG" LoRa message 326 | // os_eventq_put(os_eventq_dflt_get(), &loraping_ev_tx); 327 | } 328 | 329 | /// Callback Function that is called when we couldn't receive a LoRa message due to error 330 | static void on_rx_error(void) { 331 | printf("Rx error\r\n"); 332 | 333 | // Log the error 334 | loraping_stats.rx_error++; 335 | 336 | // Switch the LoRa Transceiver to low power, sleep mode 337 | Radio.Sleep(); 338 | 339 | // TODO: Send a "PING" or "PONG" LoRa message 340 | // os_eventq_put(os_eventq_dflt_get(), &loraping_ev_tx); 341 | } 342 | 343 | /////////////////////////////////////////////////////////////////////////////// 344 | // Multitasking Commands 345 | 346 | /// Event Queue containing Events to be processed 347 | struct ble_npl_eventq event_queue; 348 | 349 | /// Event to be added to the Event Queue 350 | struct ble_npl_event event; 351 | 352 | static void task_callback(void *arg); 353 | static void handle_event(struct ble_npl_event *ev); 354 | 355 | /// TODO: Create a Background Task to handle LoRa Events 356 | /// This is unused because we don't have a Background Thread to process the Event Queue. 357 | static void create_task(void) { 358 | puts("create_task"); 359 | 360 | // Init the Event Queue 361 | ble_npl_eventq_init(&event_queue); 362 | 363 | // Init the Event 364 | ble_npl_event_init( 365 | &event, // Event 366 | handle_event, // Event Handler Function 367 | NULL // Argument to be passed to Event Handler 368 | ); 369 | 370 | // TODO: Create a Background Thread to process the Event Queue 371 | // nimble_port_freertos_init(task_callback); 372 | } 373 | 374 | /// TODO: Enqueue an Event into the Event Queue. 375 | /// This is unused because we don't have a Background Thread to process the Event Queue. 376 | static void put_event(char *buf, int len, int argc, char **argv) { 377 | puts("put_event"); 378 | 379 | // Add the Event to the Event Queue 380 | ble_npl_eventq_put(&event_queue, &event); 381 | } 382 | 383 | /// TODO: Task Function that dequeues Events from the Event Queue and processes the Events. 384 | /// This is unused because we don't have a Background Thread to process the Event Queue. 385 | static void task_callback(void *arg) { 386 | puts("task_callback"); 387 | 388 | // Loop forever handling Events from the Event Queue 389 | for (;;) { 390 | // Get the next Event from the Event Queue 391 | struct ble_npl_event *ev = ble_npl_eventq_get( 392 | &event_queue, // Event Queue 393 | 1000 // Timeout in 1,000 ticks 394 | ); 395 | 396 | // If no Event due to timeout, wait for next Event 397 | if (ev == NULL) { continue; } 398 | 399 | // Remove the Event from the Event Queue 400 | ble_npl_eventq_remove(&event_queue, ev); 401 | 402 | // Trigger the Event Handler Function (handle_event) 403 | ble_npl_event_run(ev); 404 | } 405 | } 406 | 407 | /// TODO: Handle an Event 408 | /// This is unused because we don't have a Background Thread to process the Event Queue. 409 | static void handle_event(struct ble_npl_event *ev) { 410 | puts("handle_event"); 411 | printf("\r\nHandle an event\r\n"); 412 | } 413 | 414 | #endif // !ARCH_RISCV -------------------------------------------------------------------------------- /include/radio.h: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file radio.h 3 | * 4 | * \brief Radio driver API definition 5 | * 6 | * \copyright Revised BSD License, see section \ref LICENSE. 7 | * 8 | * \code 9 | * ______ _ 10 | * / _____) _ | | 11 | * ( (____ _____ ____ _| |_ _____ ____| |__ 12 | * \____ \| ___ | (_ _) ___ |/ ___) _ \ 13 | * _____) ) ____| | | || |_| ____( (___| | | | 14 | * (______/|_____)_|_|_| \__)_____)\____)_| |_| 15 | * (C)2013-2017 Semtech 16 | * 17 | * \endcode 18 | * 19 | * \author Miguel Luis ( Semtech ) 20 | * 21 | * \author Gregory Cristian ( Semtech ) 22 | */ 23 | #ifndef __RADIO_H__ 24 | #define __RADIO_H__ 25 | 26 | #ifdef __cplusplus 27 | extern "C" 28 | { 29 | #endif 30 | 31 | #include 32 | #include 33 | 34 | /*! 35 | * Radio driver supported modems 36 | */ 37 | typedef enum 38 | { 39 | MODEM_FSK = 0, 40 | MODEM_LORA, 41 | }RadioModems_t; 42 | 43 | /*! 44 | * Radio driver internal state machine states definition 45 | */ 46 | typedef enum 47 | { 48 | RF_IDLE = 0, //!< The radio is idle 49 | RF_RX_RUNNING, //!< The radio is in reception state 50 | RF_TX_RUNNING, //!< The radio is in transmission state 51 | RF_CAD, //!< The radio is doing channel activity detection 52 | }RadioState_t; 53 | 54 | /*! 55 | * \brief Radio driver callback functions 56 | */ 57 | typedef struct 58 | { 59 | /*! 60 | * \brief Tx Done callback prototype. 61 | */ 62 | void ( *TxDone )( void ); 63 | /*! 64 | * \brief Tx Timeout callback prototype. 65 | */ 66 | void ( *TxTimeout )( void ); 67 | /*! 68 | * \brief Rx Done callback prototype. 69 | * 70 | * \param [IN] payload Received buffer pointer 71 | * \param [IN] size Received buffer size 72 | * \param [IN] rssi RSSI value computed while receiving the frame [dBm] 73 | * \param [IN] snr SNR value computed while receiving the frame [dB] 74 | * FSK : N/A ( set to 0 ) 75 | * LoRa: SNR value in dB 76 | */ 77 | void ( *RxDone )( uint8_t *payload, uint16_t size, int16_t rssi, int8_t snr ); 78 | /*! 79 | * \brief Rx Timeout callback prototype. 80 | */ 81 | void ( *RxTimeout )( void ); 82 | /*! 83 | * \brief Rx Error callback prototype. 84 | */ 85 | void ( *RxError )( void ); 86 | /*! 87 | * \brief FHSS Change Channel callback prototype. 88 | * 89 | * \param [IN] currentChannel Index number of the current channel 90 | */ 91 | void ( *FhssChangeChannel )( uint8_t currentChannel ); 92 | 93 | /*! 94 | * \brief CAD Done callback prototype. 95 | * 96 | * \param [IN] channelDetected Channel Activity detected during the CAD 97 | */ 98 | void ( *CadDone ) ( bool channelActivityDetected ); 99 | 100 | /*! 101 | * \brief Gnss Done Done callback prototype. 102 | */ 103 | void ( *GnssDone )( void ); 104 | 105 | /*! 106 | * \brief Gnss Done Done callback prototype. 107 | */ 108 | void ( *WifiDone )( void ); 109 | }RadioEvents_t; 110 | 111 | /*! 112 | * \brief Radio driver definition 113 | */ 114 | struct Radio_s 115 | { 116 | /*! 117 | * \brief Initializes the radio 118 | * 119 | * \param [IN] events Structure containing the driver callback functions 120 | */ 121 | void ( *Init )( RadioEvents_t *events ); 122 | /*! 123 | * Return current radio status 124 | * 125 | * \param status Radio status.[RF_IDLE, RF_RX_RUNNING, RF_TX_RUNNING] 126 | */ 127 | RadioState_t ( *GetStatus )( void ); 128 | /*! 129 | * \brief Configures the radio with the given modem 130 | * 131 | * \param [IN] modem Modem to be used [0: FSK, 1: LoRa] 132 | */ 133 | void ( *SetModem )( RadioModems_t modem ); 134 | /*! 135 | * \brief Sets the channel frequency 136 | * 137 | * \param [IN] freq Channel RF frequency 138 | */ 139 | void ( *SetChannel )( uint32_t freq ); 140 | /*! 141 | * \brief Checks if the channel is free for the given time 142 | * 143 | * \remark The FSK modem is always used for this task as we can select the Rx bandwidth at will. 144 | * 145 | * \param [IN] freq Channel RF frequency in Hertz 146 | * \param [IN] rxBandwidth Rx bandwidth in Hertz 147 | * \param [IN] rssiThresh RSSI threshold in dBm 148 | * \param [IN] maxCarrierSenseTime Max time in milliseconds while the RSSI is measured 149 | * 150 | * \retval isFree [true: Channel is free, false: Channel is not free] 151 | */ 152 | bool ( *IsChannelFree )( uint32_t freq, uint32_t rxBandwidth, int16_t rssiThresh, uint32_t maxCarrierSenseTime ); 153 | /*! 154 | * \brief Generates a 32 bits random value based on the RSSI readings 155 | * 156 | * \remark This function sets the radio in LoRa modem mode and disables 157 | * all interrupts. 158 | * After calling this function either Radio.SetRxConfig or 159 | * Radio.SetTxConfig functions must be called. 160 | * 161 | * \retval randomValue 32 bits random value 162 | */ 163 | uint32_t ( *Random )( void ); 164 | /*! 165 | * \brief Sets the reception parameters 166 | * 167 | * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] 168 | * \param [IN] bandwidth Sets the bandwidth 169 | * FSK : >= 2600 and <= 250000 Hz 170 | * LoRa: [0: 125 kHz, 1: 250 kHz, 171 | * 2: 500 kHz, 3: Reserved] 172 | * \param [IN] datarate Sets the Datarate 173 | * FSK : 600..300000 bits/s 174 | * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, 175 | * 10: 1024, 11: 2048, 12: 4096 chips] 176 | * \param [IN] coderate Sets the coding rate (LoRa only) 177 | * FSK : N/A ( set to 0 ) 178 | * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] 179 | * \param [IN] bandwidthAfc Sets the AFC Bandwidth (FSK only) 180 | * FSK : >= 2600 and <= 250000 Hz 181 | * LoRa: N/A ( set to 0 ) 182 | * \param [IN] preambleLen Sets the Preamble length 183 | * FSK : Number of bytes 184 | * LoRa: Length in symbols (the hardware adds 4 more symbols) 185 | * \param [IN] symbTimeout Sets the RxSingle timeout value 186 | * FSK : timeout in number of bytes 187 | * LoRa: timeout in symbols 188 | * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] 189 | * \param [IN] payloadLen Sets payload length when fixed length is used 190 | * \param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON] 191 | * \param [IN] freqHopOn Enables disables the intra-packet frequency hopping 192 | * FSK : N/A ( set to 0 ) 193 | * LoRa: [0: OFF, 1: ON] 194 | * \param [IN] hopPeriod Number of symbols between each hop 195 | * FSK : N/A ( set to 0 ) 196 | * LoRa: Number of symbols 197 | * \param [IN] iqInverted Inverts IQ signals (LoRa only) 198 | * FSK : N/A ( set to 0 ) 199 | * LoRa: [0: not inverted, 1: inverted] 200 | * \param [IN] rxContinuous Sets the reception in continuous mode 201 | * [false: single mode, true: continuous mode] 202 | */ 203 | void ( *SetRxConfig )( RadioModems_t modem, uint32_t bandwidth, 204 | uint32_t datarate, uint8_t coderate, 205 | uint32_t bandwidthAfc, uint16_t preambleLen, 206 | uint16_t symbTimeout, bool fixLen, 207 | uint8_t payloadLen, 208 | bool crcOn, bool freqHopOn, uint8_t hopPeriod, 209 | bool iqInverted, bool rxContinuous ); 210 | /*! 211 | * \brief Sets the transmission parameters 212 | * 213 | * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] 214 | * \param [IN] power Sets the output power [dBm] 215 | * \param [IN] fdev Sets the frequency deviation (FSK only) 216 | * FSK : [Hz] 217 | * LoRa: 0 218 | * \param [IN] bandwidth Sets the bandwidth (LoRa only) 219 | * FSK : 0 220 | * LoRa: [0: 125 kHz, 1: 250 kHz, 221 | * 2: 500 kHz, 3: Reserved] 222 | * \param [IN] datarate Sets the Datarate 223 | * FSK : 600..300000 bits/s 224 | * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, 225 | * 10: 1024, 11: 2048, 12: 4096 chips] 226 | * \param [IN] coderate Sets the coding rate (LoRa only) 227 | * FSK : N/A ( set to 0 ) 228 | * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] 229 | * \param [IN] preambleLen Sets the preamble length 230 | * FSK : Number of bytes 231 | * LoRa: Length in symbols (the hardware adds 4 more symbols) 232 | * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] 233 | * \param [IN] crcOn Enables disables the CRC [0: OFF, 1: ON] 234 | * \param [IN] freqHopOn Enables disables the intra-packet frequency hopping 235 | * FSK : N/A ( set to 0 ) 236 | * LoRa: [0: OFF, 1: ON] 237 | * \param [IN] hopPeriod Number of symbols between each hop 238 | * FSK : N/A ( set to 0 ) 239 | * LoRa: Number of symbols 240 | * \param [IN] iqInverted Inverts IQ signals (LoRa only) 241 | * FSK : N/A ( set to 0 ) 242 | * LoRa: [0: not inverted, 1: inverted] 243 | * \param [IN] timeout Transmission timeout [ms] 244 | */ 245 | void ( *SetTxConfig )( RadioModems_t modem, int8_t power, uint32_t fdev, 246 | uint32_t bandwidth, uint32_t datarate, 247 | uint8_t coderate, uint16_t preambleLen, 248 | bool fixLen, bool crcOn, bool freqHopOn, 249 | uint8_t hopPeriod, bool iqInverted, uint32_t timeout ); 250 | /*! 251 | * \brief Checks if the given RF frequency is supported by the hardware 252 | * 253 | * \param [IN] frequency RF frequency to be checked 254 | * \retval isSupported [true: supported, false: unsupported] 255 | */ 256 | bool ( *CheckRfFrequency )( uint32_t frequency ); 257 | /*! 258 | * \brief Computes the packet time on air in ms for the given payload 259 | * 260 | * \Remark Can only be called once SetRxConfig or SetTxConfig have been called 261 | * 262 | * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] 263 | * \param [IN] bandwidth Sets the bandwidth 264 | * FSK : >= 2600 and <= 250000 Hz 265 | * LoRa: [0: 125 kHz, 1: 250 kHz, 266 | * 2: 500 kHz, 3: Reserved] 267 | * \param [IN] datarate Sets the Datarate 268 | * FSK : 600..300000 bits/s 269 | * LoRa: [6: 64, 7: 128, 8: 256, 9: 512, 270 | * 10: 1024, 11: 2048, 12: 4096 chips] 271 | * \param [IN] coderate Sets the coding rate (LoRa only) 272 | * FSK : N/A ( set to 0 ) 273 | * LoRa: [1: 4/5, 2: 4/6, 3: 4/7, 4: 4/8] 274 | * \param [IN] preambleLen Sets the Preamble length 275 | * FSK : Number of bytes 276 | * LoRa: Length in symbols (the hardware adds 4 more symbols) 277 | * \param [IN] fixLen Fixed length packets [0: variable, 1: fixed] 278 | * \param [IN] payloadLen Sets payload length when fixed length is used 279 | * \param [IN] crcOn Enables/Disables the CRC [0: OFF, 1: ON] 280 | * 281 | * \retval airTime Computed airTime (ms) for the given packet payload length 282 | */ 283 | uint32_t ( *TimeOnAir )( RadioModems_t modem, uint32_t bandwidth, 284 | uint32_t datarate, uint8_t coderate, 285 | uint16_t preambleLen, bool fixLen, uint8_t payloadLen, 286 | bool crcOn ); 287 | /*! 288 | * \brief Sends the buffer of size. Prepares the packet to be sent and sets 289 | * the radio in transmission 290 | * 291 | * \param [IN]: buffer Buffer pointer 292 | * \param [IN]: size Buffer size 293 | */ 294 | void ( *Send )( uint8_t *buffer, uint8_t size ); 295 | /*! 296 | * \brief Sets the radio in sleep mode 297 | */ 298 | void ( *Sleep )( void ); 299 | /*! 300 | * \brief Sets the radio in standby mode 301 | */ 302 | void ( *Standby )( void ); 303 | /*! 304 | * \brief Sets the radio in reception mode for the given time 305 | * \param [IN] timeout Reception timeout [ms] 306 | * [0: continuous, others timeout] 307 | */ 308 | void ( *Rx )( uint32_t timeout ); 309 | /*! 310 | * \brief Start a Channel Activity Detection 311 | */ 312 | void ( *StartCad )( void ); 313 | /*! 314 | * \brief Sets the radio in continuous wave transmission mode 315 | * 316 | * \param [IN]: freq Channel RF frequency 317 | * \param [IN]: power Sets the output power [dBm] 318 | * \param [IN]: time Transmission mode timeout [s] 319 | */ 320 | void ( *SetTxContinuousWave )( uint32_t freq, int8_t power, uint16_t time ); 321 | /*! 322 | * \brief Reads the current RSSI value 323 | * 324 | * \retval rssiValue Current RSSI value in [dBm] 325 | */ 326 | int16_t ( *Rssi )( RadioModems_t modem ); 327 | /*! 328 | * \brief Writes the radio register at the specified address 329 | * 330 | * \param [IN]: addr Register address 331 | * \param [IN]: data New register value 332 | */ 333 | void ( *Write )( uint32_t addr, uint8_t data ); 334 | /*! 335 | * \brief Reads the radio register at the specified address 336 | * 337 | * \param [IN]: addr Register address 338 | * \retval data Register value 339 | */ 340 | uint8_t ( *Read )( uint32_t addr ); 341 | /*! 342 | * \brief Writes multiple radio registers starting at address 343 | * 344 | * \param [IN] addr First Radio register address 345 | * \param [IN] buffer Buffer containing the new register's values 346 | * \param [IN] size Number of registers to be written 347 | */ 348 | void ( *WriteBuffer )( uint32_t addr, uint8_t *buffer, uint8_t size ); 349 | /*! 350 | * \brief Reads multiple radio registers starting at address 351 | * 352 | * \param [IN] addr First Radio register address 353 | * \param [OUT] buffer Buffer where to copy the registers data 354 | * \param [IN] size Number of registers to be read 355 | */ 356 | void ( *ReadBuffer )( uint32_t addr, uint8_t *buffer, uint8_t size ); 357 | /*! 358 | * \brief Sets the maximum payload length. 359 | * 360 | * \param [IN] modem Radio modem to be used [0: FSK, 1: LoRa] 361 | * \param [IN] max Maximum payload length in bytes 362 | */ 363 | void ( *SetMaxPayloadLength )( RadioModems_t modem, uint8_t max ); 364 | /*! 365 | * \brief Sets the network to public or private. Updates the sync byte. 366 | * 367 | * \remark Applies to LoRa modem only 368 | * 369 | * \param [IN] enable if true, it enables a public network 370 | */ 371 | void ( *SetPublicNetwork )( bool enable ); 372 | /*! 373 | * \brief Gets the time required for the board plus radio to get out of sleep.[ms] 374 | * 375 | * \retval time Radio plus board wakeup time in ms. 376 | */ 377 | uint32_t ( *GetWakeupTime )( void ); 378 | /*! 379 | * \brief Process radio irq 380 | */ 381 | void ( *IrqProcess )( void ); 382 | /* 383 | * The next functions are available only on SX126x radios. 384 | */ 385 | /*! 386 | * \brief Sets the radio in reception mode with Max LNA gain for the given time 387 | * 388 | * \remark Available on SX126x radios only. 389 | * 390 | * \param [IN] timeout Reception timeout [ms] 391 | * [0: continuous, others timeout] 392 | */ 393 | void ( *RxBoosted )( uint32_t timeout ); 394 | /*! 395 | * \brief Sets the Rx duty cycle management parameters 396 | * 397 | * \remark Available on SX126x radios only. 398 | * 399 | * \param [in] rxTime Structure describing reception timeout value 400 | * \param [in] sleepTime Structure describing sleep timeout value 401 | */ 402 | void ( *SetRxDutyCycle ) ( uint32_t rxTime, uint32_t sleepTime ); 403 | }; 404 | 405 | /*! 406 | * \brief Radio driver 407 | * 408 | * \remark This variable is defined and initialized in the specific radio 409 | * board implementation 410 | */ 411 | extern const struct Radio_s Radio; 412 | 413 | struct ble_npl_event; 414 | 415 | /// Callback Function for Transmit and Receive Interrupts. 416 | /// This function runs in the context of the Background Application Task. 417 | /// So we are safe to call printf and SPI Functions now. 418 | void RadioOnDioIrq( struct ble_npl_event *ev ); 419 | 420 | #ifdef __cplusplus 421 | } 422 | #endif 423 | 424 | #endif // __RADIO_H__ 425 | -------------------------------------------------------------------------------- /src/sx126x-linux.c: -------------------------------------------------------------------------------- 1 | // LoRa SX1262 Board Functions for Linux (PineDio USB) 2 | #ifndef ARCH_RISCV // This file is for Linux only 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include "radio.h" 13 | #include "sx126x.h" 14 | #include "sx126x-board.h" 15 | 16 | #if defined( USE_RADIO_DEBUG ) 17 | /*! 18 | * \brief Writes new Tx debug pin state 19 | * 20 | * \param [IN] state Debug pin state 21 | */ 22 | static void SX126xDbgPinTxWrite( uint8_t state ); 23 | 24 | /*! 25 | * \brief Writes new Rx debug pin state 26 | * 27 | * \param [IN] state Debug pin state 28 | */ 29 | static void SX126xDbgPinRxWrite( uint8_t state ); 30 | #endif 31 | 32 | /*! 33 | * \brief Holds the internal operating mode of the radio 34 | */ 35 | static RadioOperatingModes_t OperatingMode; 36 | 37 | /*! 38 | * Antenna switch GPIO pins objects 39 | */ 40 | Gpio_t AntPow; 41 | Gpio_t DeviceSel; 42 | 43 | /*! 44 | * Debug GPIO pins objects 45 | */ 46 | #if defined( USE_RADIO_DEBUG ) 47 | Gpio_t DbgPinTx; 48 | Gpio_t DbgPinRx; 49 | #endif 50 | 51 | static int sx126x_write_register( const void* context, const uint16_t address, const uint8_t* buffer, const uint8_t size ); 52 | static int sx126x_read_register( const void* context, const uint16_t address, uint8_t* buffer, const uint8_t size ); 53 | static int sx126x_write_buffer( const void* context, const uint8_t offset, const uint8_t* buffer, const uint8_t size ); 54 | static int sx126x_read_buffer( const void* context, const uint8_t offset, uint8_t* buffer, const uint8_t size ); 55 | static int sx126x_hal_write( 56 | const void* context, const uint8_t* command, const uint16_t command_length, 57 | const uint8_t* data, const uint16_t data_length ); 58 | static int sx126x_hal_read( 59 | const void* context, const uint8_t* command, const uint16_t command_length, 60 | uint8_t* data, const uint16_t data_length, uint8_t *status ); 61 | static int init_spi(void); 62 | 63 | /////////////////////////////////////////////////////////////////////////////// 64 | 65 | /// Initialise GPIO Pins and SPI Port. Called by SX126xIoIrqInit. 66 | /// Note: This is different from the Reference Implementation, 67 | /// which initialises the GPIO Pins and SPI Port at startup. 68 | void SX126xIoInit( void ) 69 | { 70 | printf("SX126xIoInit\r\n"); 71 | 72 | // Init SPI Bus 73 | int rc = init_spi(); 74 | assert(rc == 0); 75 | 76 | // TODO: Init GPIO Pins 77 | } 78 | 79 | /// Initialise GPIO Pins and SPI Port. Register GPIO Interrupt Handler for DIO1. 80 | /// Note: This is different from the Reference Implementation, 81 | /// which initialises the GPIO Pins and SPI Port at startup. 82 | void SX126xIoIrqInit( DioIrqHandler dioIrq ) 83 | { 84 | // Initialise GPIO Pins and SPI Port. 85 | // Note: This is different from the Reference Implementation, 86 | // which initialises the GPIO Pins and SPI Port at startup. 87 | SX126xIoInit(); 88 | 89 | // TODO: Register GPIO Interrupt Handler for DIO1 90 | printf("TODO: SX126X interrupt init\r\n"); 91 | } 92 | 93 | void SX126xIoDeInit( void ) 94 | { 95 | printf("SX126xIoDeInit\r\n"); 96 | } 97 | 98 | void SX126xIoDbgInit( void ) 99 | { 100 | #if defined( USE_RADIO_DEBUG ) 101 | GpioInitOutput( SX126X_DBG_PIN_TX, 0 ); 102 | GpioInitOutput( SX126X_DBG_PIN_RX, 0 ); 103 | #endif 104 | } 105 | 106 | void SX126xIoTcxoInit( void ) 107 | { 108 | // No TCXO component available on this board design. 109 | } 110 | 111 | uint32_t SX126xGetBoardTcxoWakeupTime( void ) 112 | { 113 | return SX126X_TCXO_WAKEUP_TIME; 114 | } 115 | 116 | void SX126xIoRfSwitchInit( void ) 117 | { 118 | SX126xSetDio2AsRfSwitchCtrl( true ); 119 | } 120 | 121 | RadioOperatingModes_t SX126xGetOperatingMode( void ) 122 | { 123 | return OperatingMode; 124 | } 125 | 126 | void SX126xSetOperatingMode( RadioOperatingModes_t mode ) 127 | { 128 | OperatingMode = mode; 129 | #if defined( USE_RADIO_DEBUG ) 130 | switch( mode ) 131 | { 132 | case MODE_TX: 133 | SX126xDbgPinTxWrite( 1 ); 134 | SX126xDbgPinRxWrite( 0 ); 135 | break; 136 | case MODE_RX: 137 | case MODE_RX_DC: 138 | SX126xDbgPinTxWrite( 0 ); 139 | SX126xDbgPinRxWrite( 1 ); 140 | break; 141 | default: 142 | SX126xDbgPinTxWrite( 0 ); 143 | SX126xDbgPinRxWrite( 0 ); 144 | break; 145 | } 146 | #endif 147 | } 148 | 149 | void SX126xReset(void) 150 | { 151 | //// #warning Check SX126xReset 152 | 153 | printf("TODO: SX126xReset\r\n"); 154 | #ifdef TODO 155 | // Set Reset pin to Low 156 | rc = bl_gpio_output_set(SX126X_NRESET, 1); 157 | assert(rc == 0); 158 | 159 | // Wait 1 ms 160 | DelayMs(1); 161 | 162 | // Configure Reset pin as a GPIO Input Pin, no pullup, no pulldown 163 | rc = bl_gpio_enable_input(SX126X_NRESET, 0, 0); 164 | assert(rc == 0); 165 | 166 | // Wait 6 ms 167 | DelayMs(6); 168 | #endif // TODO 169 | } 170 | 171 | void SX126xWaitOnBusy( void ) 172 | { 173 | printf("TODO: SX126xWaitOnBusy\r\n"); 174 | 175 | // TODO: Fix the GPIO check for busy state. 176 | // Meanwhile we sleep 10 milliseconds. 177 | usleep(10 * 1000); 178 | 179 | #ifdef TODO 180 | while( bl_gpio_input_get_value( SX126X_BUSY_PIN ) == 1 ); 181 | #endif // TODO 182 | } 183 | 184 | void SX126xWakeup( void ) 185 | { 186 | printf("SX126xWakeup\r\n"); 187 | CRITICAL_SECTION_BEGIN( ); 188 | 189 | // Write RADIO_GET_STATUS command followed by 0 190 | uint8_t commands[1] = { RADIO_GET_STATUS }; 191 | uint8_t buffer[1] = { 0 }; 192 | int rc = sx126x_hal_write(NULL, commands, sizeof(commands), buffer, sizeof(buffer)); 193 | assert(rc == 0); 194 | 195 | // Wait for chip to be ready. 196 | SX126xWaitOnBusy( ); 197 | 198 | // Update operating mode context variable 199 | SX126xSetOperatingMode( MODE_STDBY_RC ); 200 | 201 | CRITICAL_SECTION_END( ); 202 | } 203 | 204 | void SX126xWriteCommand( RadioCommands_t command, uint8_t *buffer, uint16_t size ) 205 | { 206 | SX126xCheckDeviceReady( ); 207 | 208 | // Write the command followed by buffer 209 | uint8_t commands[1] = { (uint8_t) command }; 210 | int rc = sx126x_hal_write(NULL, commands, sizeof(commands), buffer, size); 211 | assert(rc == 0); 212 | 213 | if( command != RADIO_SET_SLEEP ) 214 | { 215 | SX126xWaitOnBusy( ); 216 | } 217 | } 218 | 219 | uint8_t SX126xReadCommand( RadioCommands_t command, uint8_t *buffer, uint16_t size ) 220 | { 221 | printf("SX126xReadCommand: command=0x%02x, size=%d\r\n", command, size); 222 | uint8_t status = 0; 223 | 224 | SX126xCheckDeviceReady( ); 225 | 226 | // Write the command, read the status and read the buffer 227 | uint8_t commandStatus[2] = { 228 | (uint8_t) command, // Command 229 | 0 // Status 230 | }; 231 | int rc = sx126x_hal_read(NULL, commandStatus, sizeof(commandStatus), buffer, size, &status); 232 | assert(rc == 0); 233 | printf("status=0x%02x\n", status); 234 | 235 | #ifdef NOTUSED // Previously... 236 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 0 ); 237 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 0 ); } 238 | 239 | SpiInOut( SX126X_SPI_IDX, ( uint8_t )command ); 240 | status = SpiInOut( SX126X_SPI_IDX, 0x00 ); 241 | for( uint16_t i = 0; i < size; i++ ) 242 | { 243 | buffer[i] = SpiInOut( SX126X_SPI_IDX, 0 ); 244 | } 245 | 246 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 1 ); 247 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 1 ); } 248 | #endif // NOTUSED 249 | 250 | SX126xWaitOnBusy( ); 251 | 252 | return status; 253 | } 254 | 255 | void SX126xWriteRegisters( uint16_t address, uint8_t *buffer, uint16_t size ) 256 | { 257 | SX126xCheckDeviceReady( ); 258 | int rc = sx126x_write_register(NULL, address, buffer, size); 259 | assert(rc == 0); 260 | SX126xWaitOnBusy( ); 261 | } 262 | 263 | void SX126xWriteRegister( uint16_t address, uint8_t value ) 264 | { 265 | SX126xWriteRegisters( address, &value, 1 ); 266 | } 267 | 268 | void SX126xReadRegisters( uint16_t address, uint8_t *buffer, uint16_t size ) 269 | { 270 | SX126xCheckDeviceReady( ); 271 | int rc = sx126x_read_register(NULL, address, buffer, size); 272 | assert(rc == 0); 273 | SX126xWaitOnBusy( ); 274 | } 275 | 276 | uint8_t SX126xReadRegister( uint16_t address ) 277 | { 278 | uint8_t data; 279 | SX126xReadRegisters( address, &data, 1 ); 280 | return data; 281 | } 282 | 283 | void SX126xWriteBuffer( uint8_t offset, uint8_t *buffer, uint8_t size ) 284 | { 285 | SX126xCheckDeviceReady( ); 286 | int rc = sx126x_write_buffer(NULL, offset, buffer, size); 287 | assert(rc == 0); 288 | SX126xWaitOnBusy( ); 289 | } 290 | 291 | void SX126xReadBuffer( uint8_t offset, uint8_t *buffer, uint8_t size ) 292 | { 293 | SX126xCheckDeviceReady( ); 294 | int rc = sx126x_read_buffer(NULL, offset, buffer, size); 295 | assert(rc == 0); 296 | SX126xWaitOnBusy( ); 297 | } 298 | 299 | void SX126xSetRfTxPower( int8_t power ) 300 | { 301 | printf("SX126xSetRfTxPower\r\n"); 302 | ////TODO: SX126xSetTxParams( power, RADIO_RAMP_40_US ); 303 | SX126xSetTxParams( power, RADIO_RAMP_3400_US );////TODO 304 | } 305 | 306 | uint8_t SX126xGetDeviceId( void ) 307 | { 308 | // For SX1262 309 | printf("SX126xGetDeviceId: SX1262\r\n"); 310 | return SX1262; 311 | 312 | // For SX1261 313 | // printf("SX126xGetDeviceId: SX1261\r\n"); 314 | // return SX1261; 315 | } 316 | 317 | void SX126xAntSwOn( void ) 318 | { 319 | #if SX126X_HAS_ANT_SW 320 | GpioInit( &AntPow, RADIO_ANT_SWITCH_POWER, PIN_OUTPUT, PIN_PUSH_PULL, PIN_PULL_UP, 1 ); 321 | #endif // SX126X_HAS_ANT_SW 322 | } 323 | 324 | void SX126xAntSwOff( void ) 325 | { 326 | #if SX126X_HAS_ANT_SW 327 | GpioInit( &AntPow, RADIO_ANT_SWITCH_POWER, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); 328 | #endif // SX126X_HAS_ANT_SW 329 | } 330 | 331 | bool SX126xCheckRfFrequency( uint32_t frequency ) 332 | { 333 | // Implement check. Currently all frequencies are supported 334 | return true; 335 | } 336 | 337 | uint32_t SX126xGetDio1PinState( void ) 338 | { 339 | puts("TODO: SX126xGetDio1PinState"); 340 | return 0; 341 | #ifdef TODO 342 | return bl_gpio_input_get_value( SX126X_DIO1 ); 343 | #endif // TODO 344 | } 345 | 346 | #if defined( USE_RADIO_DEBUG ) 347 | static void SX126xDbgPinTxWrite( uint8_t state ) 348 | { 349 | bl_gpio_output_set( SX126X_DBG_PIN_TX, state ); 350 | } 351 | 352 | static void SX126xDbgPinRxWrite( uint8_t state ) 353 | { 354 | bl_gpio_output_set( SX126X_DBG_PIN_RX, state ); 355 | } 356 | #endif 357 | 358 | /////////////////////////////////////////////////////////////////////////////// 359 | // Timer Functions 360 | 361 | /// Initialise a timer 362 | void TimerInit( 363 | struct ble_npl_callout *timer, // The timer to initialize. Cannot be NULL. 364 | ble_npl_event_fn *f) // The timer callback function. Cannot be NULL. 365 | { 366 | puts("TimerInit"); 367 | assert(timer != NULL); 368 | assert(f != NULL); 369 | 370 | // Event Queue containing Events to be processed, defined in demo.c. TODO: Move to header file. 371 | extern struct ble_npl_eventq event_queue; 372 | 373 | // Init the Callout Timer with the Callback Function 374 | ble_npl_callout_init( 375 | timer, // Callout Timer 376 | &event_queue, // Event Queue that will handle the Callout upon timeout 377 | f, // Callback Function 378 | NULL // Argument to be passed to Callback Function 379 | ); 380 | } 381 | 382 | /// Stops a timer from running. Can be called even if timer is not running. 383 | void TimerStop( 384 | struct ble_npl_callout *timer) // Pointer to timer to stop. Cannot be NULL. 385 | { 386 | puts("TimerStop"); 387 | assert(timer != NULL); 388 | 389 | // If Callout Timer is still running... 390 | if (ble_npl_callout_is_active(timer)) { 391 | // Stop the Callout Timer 392 | ble_npl_callout_stop(timer); 393 | } 394 | } 395 | 396 | /// Sets a timer that will expire ‘millisecs’ milliseconds from the current time. 397 | void TimerStart( 398 | struct ble_npl_callout *timer, // Pointer to timer. Cannot be NULL. 399 | uint32_t millisecs) // The number of milliseconds from now at which the timer will expire. 400 | { 401 | puts("TimerStart"); 402 | assert(timer != NULL); 403 | 404 | // Stop the timer if running 405 | TimerStop(timer); 406 | 407 | // Convert milliseconds to ticks 408 | ble_npl_time_t ticks = ble_npl_time_ms_to_ticks32( 409 | millisecs // Duration in milliseconds 410 | ); 411 | 412 | // Wait at least 1 tick 413 | if (ticks == 0) { ticks = 1; } 414 | 415 | // Trigger the Callout Timer after the elapsed ticks 416 | ble_npl_error_t rc = ble_npl_callout_reset( 417 | timer, // Callout Timer 418 | ticks // Number of ticks 419 | ); 420 | assert(rc == 0); 421 | } 422 | 423 | /// Wait until ‘millisecs’ milliseconds has elapsed. This is a blocking delay. 424 | void DelayMs(uint32_t millisecs) // The number of milliseconds to wait. 425 | { 426 | // Implement with Timer Functions from NimBLE Porting Layer. 427 | // Convert milliseconds to ticks. 428 | ble_npl_time_t ticks = ble_npl_time_ms_to_ticks32( 429 | millisecs // Duration in milliseconds 430 | ); 431 | 432 | // Wait at least 1 tick 433 | if (ticks == 0) { ticks = 1; } 434 | 435 | // Wait for the ticks 436 | ble_npl_time_delay(ticks); 437 | } 438 | 439 | /// Return current time in microseconds 440 | uint32_t TimerGetCurrentTime(void) 441 | { 442 | assert(false); return 0; 443 | #ifdef TODO 444 | // Convert ticks to milliseconds then microseconds 445 | return xTaskGetTickCount() * portTICK_PERIOD_MS * 1000; 446 | #endif // TODO 447 | } 448 | 449 | /// Return elased time in microseconds 450 | uint32_t TimerGetElapsedTime(uint32_t saved_time) 451 | { 452 | assert(false); return 0; 453 | #ifdef TODO 454 | return TimerGetCurrentTime() - saved_time; 455 | #endif // TODO 456 | } 457 | 458 | /////////////////////////////////////////////////////////////////////////////// 459 | // Register and Buffer Functions 460 | 461 | /** 462 | * Commands Interface buffer sizes 463 | */ 464 | typedef enum sx126x_commands_size_e 465 | { 466 | // Registers and buffer Access 467 | // Full size: this value plus buffer size 468 | SX126X_SIZE_WRITE_REGISTER = 3, 469 | // Full size: this value plus buffer size 470 | SX126X_SIZE_READ_REGISTER = 4, 471 | // Full size: this value plus buffer size 472 | SX126X_SIZE_WRITE_BUFFER = 2, 473 | // Full size: this value plus buffer size 474 | SX126X_SIZE_READ_BUFFER = 3, 475 | } sx126x_commands_size_t; 476 | 477 | static int sx126x_write_register( const void* context, const uint16_t address, const uint8_t* buffer, 478 | const uint8_t size ) { 479 | uint8_t buf[SX126X_SIZE_WRITE_REGISTER] = { 0 }; 480 | buf[0] = RADIO_WRITE_REGISTER; 481 | buf[1] = ( uint8_t )( address >> 8 ); 482 | buf[2] = ( uint8_t )( address >> 0 ); 483 | return sx126x_hal_write( context, buf, SX126X_SIZE_WRITE_REGISTER, buffer, size ); 484 | } 485 | 486 | static int sx126x_read_register( const void* context, const uint16_t address, uint8_t* buffer, const uint8_t size ) { 487 | uint8_t buf[SX126X_SIZE_READ_REGISTER] = { 0 }; 488 | int status = -1; 489 | buf[0] = RADIO_READ_REGISTER; 490 | buf[1] = ( uint8_t )( address >> 8 ); 491 | buf[2] = ( uint8_t )( address >> 0 ); 492 | buf[3] = 0; 493 | status = sx126x_hal_read( context, buf, SX126X_SIZE_READ_REGISTER, buffer, size, NULL ); 494 | return status; 495 | } 496 | 497 | static int sx126x_write_buffer( const void* context, const uint8_t offset, const uint8_t* buffer, 498 | const uint8_t size ) { 499 | uint8_t buf[SX126X_SIZE_WRITE_BUFFER] = { 0 }; 500 | buf[0] = RADIO_WRITE_BUFFER; 501 | buf[1] = offset; 502 | return sx126x_hal_write( context, buf, SX126X_SIZE_WRITE_BUFFER, buffer, size ); 503 | } 504 | 505 | static int sx126x_read_buffer( const void* context, const uint8_t offset, uint8_t* buffer, const uint8_t size ) { 506 | uint8_t buf[SX126X_SIZE_READ_BUFFER] = { 0 }; 507 | int status = -1; 508 | buf[0] = RADIO_READ_BUFFER; 509 | buf[1] = offset; 510 | buf[2] = 0; 511 | status = sx126x_hal_read( context, buf, SX126X_SIZE_READ_BUFFER, buffer, size, NULL ); 512 | return status; 513 | } 514 | 515 | /////////////////////////////////////////////////////////////////////////////// 516 | // SPI Functions 517 | 518 | /// SPI Bus 519 | static int spi = 0; 520 | 521 | /// Max size of SPI transfers 522 | #define SPI_BUFFER_SIZE 1024 523 | 524 | /// SPI Transmit Buffer 525 | static uint8_t spi_tx_buf[SPI_BUFFER_SIZE]; 526 | 527 | /// SPI Receive Buffer 528 | static uint8_t spi_rx_buf[SPI_BUFFER_SIZE]; 529 | 530 | static int transfer_spi(const uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len); 531 | 532 | /** 533 | * Radio data transfer - write 534 | * 535 | * @remark Shall be implemented by the user 536 | * 537 | * @param [in] context Radio implementation parameters 538 | * @param [in] command Pointer to the buffer to be transmitted 539 | * @param [in] command_length Buffer size to be transmitted 540 | * @param [in] data Pointer to the buffer to be transmitted 541 | * @param [in] data_length Buffer size to be transmitted 542 | * 543 | * @returns Operation status 544 | */ 545 | static int sx126x_hal_write( 546 | const void* context, const uint8_t* command, const uint16_t command_length, 547 | const uint8_t* data, const uint16_t data_length ) { 548 | printf("sx126x_hal_write: command_length=%d, data_length=%d\n", command_length, data_length); 549 | 550 | // Total length is command + data length 551 | uint16_t len = command_length + data_length; 552 | assert(len > 0); 553 | assert(len <= SPI_BUFFER_SIZE); 554 | 555 | // Clear the SPI Transmit and Receive buffers 556 | memset(&spi_tx_buf, 0, len); 557 | memset(&spi_rx_buf, 0, len); 558 | 559 | // Copy command bytes to SPI Transmit Buffer 560 | memcpy(&spi_tx_buf, command, command_length); 561 | 562 | // Copy data bytes to SPI Transmit Buffer 563 | memcpy(&spi_tx_buf[command_length], data, data_length); 564 | 565 | // Transmit and receive the SPI buffers 566 | int rc = transfer_spi(spi_tx_buf, spi_rx_buf, len); 567 | assert(rc == 0); 568 | return 0; 569 | } 570 | 571 | /** 572 | * Radio data transfer - read 573 | * 574 | * @remark Shall be implemented by the user 575 | * 576 | * @param [in] context Radio implementation parameters 577 | * @param [in] command Pointer to the buffer to be transmitted 578 | * @param [in] command_length Buffer size to be transmitted 579 | * @param [in] data Pointer to the buffer to be received 580 | * @param [in] data_length Buffer size to be received 581 | * @param [out] status If not null, return the second SPI byte received as status 582 | * 583 | * @returns Operation status 584 | */ 585 | static int sx126x_hal_read( 586 | const void* context, const uint8_t* command, const uint16_t command_length, 587 | uint8_t* data, const uint16_t data_length, uint8_t *status ) { 588 | printf("sx126x_hal_read: command_length=%d, data_length=%d\n", command_length, data_length); 589 | 590 | // Total length is command + data length 591 | uint16_t len = command_length + data_length; 592 | assert(len > 0); 593 | assert(len <= SPI_BUFFER_SIZE); 594 | 595 | // Clear the SPI Transmit and Receive buffers 596 | memset(&spi_tx_buf, 0, len); 597 | memset(&spi_rx_buf, 0, len); 598 | 599 | // Copy command bytes to SPI Transmit Buffer 600 | memcpy(&spi_tx_buf, command, command_length); 601 | 602 | // Transmit and receive the SPI buffers 603 | int rc = transfer_spi(spi_tx_buf, spi_rx_buf, len); 604 | assert(rc == 0); 605 | 606 | // Copy SPI Receive buffer to data buffer 607 | memcpy(data, &spi_rx_buf[command_length], data_length); 608 | 609 | // Return the second SPI byte received as status 610 | if (status != NULL) { 611 | assert(len >= 2); 612 | *status = spi_rx_buf[1]; 613 | } 614 | return 0; 615 | } 616 | 617 | #ifdef TODO 618 | /** 619 | * Reset the radio 620 | * 621 | * @remark Shall be implemented by the user 622 | * 623 | * @param [in] context Radio implementation parameters 624 | * 625 | * @returns Operation status 626 | */ 627 | static int sx126x_hal_reset( const void* context ) { 628 | puts("sx126x_hal_reset"); 629 | assert(false); 630 | return 0; 631 | } 632 | #endif // TODO 633 | 634 | #ifdef TODO 635 | /** 636 | * Wake the radio up. 637 | * 638 | * @remark Shall be implemented by the user 639 | * 640 | * @param [in] context Radio implementation parameters 641 | * 642 | * @returns Operation status 643 | */ 644 | static int sx126x_hal_wakeup( const void* context ) { 645 | puts("sx126x_hal_wakeup"); 646 | assert(false); 647 | return 0; 648 | } 649 | #endif // TODO 650 | 651 | /// Init the SPI Bus. Return 0 on success. 652 | static int init_spi(void) { 653 | // Open the SPI Bus 654 | spi = open("/dev/spidev1.0", O_RDWR); 655 | assert(spi > 0); 656 | 657 | // Set to SPI Mode 0 658 | uint8_t mmode = SPI_MODE_0; 659 | int rc = ioctl(spi, SPI_IOC_WR_MODE, &mmode); 660 | assert(rc == 0); 661 | 662 | // Set LSB/MSB Mode 663 | uint8_t lsb = 0; 664 | rc = ioctl(spi, SPI_IOC_WR_LSB_FIRST, &lsb); 665 | assert(rc == 0); 666 | return 0; 667 | } 668 | 669 | /// Blocking call to transmit and receive buffers on SPI. Return 0 on success. 670 | static int transfer_spi(const uint8_t *tx_buf, uint8_t *rx_buf, uint16_t len) { 671 | assert(spi > 0); 672 | assert(len > 0); 673 | assert(len <= 31); // CAUTION: CH341 SPI doesn't seem to support 32-byte SPI transfers 674 | 675 | // Prepare SPI Transfer 676 | struct spi_ioc_transfer spi_trans; 677 | memset(&spi_trans, 0, sizeof(spi_trans)); 678 | spi_trans.tx_buf = (unsigned long) tx_buf; // Transmit Buffer 679 | spi_trans.rx_buf = (unsigned long) rx_buf; // Receive Buffer 680 | spi_trans.cs_change = true; // Set SPI Chip Select to Low 681 | spi_trans.len = len; // How many bytes 682 | printf("spi tx: "); for (int i = 0; i < len; i++) { printf("%02x ", tx_buf[i]); } printf("\n"); 683 | 684 | // Transfer and receive the SPI buffers 685 | int rc = ioctl(spi, SPI_IOC_MESSAGE(1), &spi_trans); 686 | assert(rc >= 0); 687 | assert(rc == len); 688 | 689 | printf("spi rx: "); for (int i = 0; i < len; i++) { printf("%02x ", rx_buf[i]); } printf("\n"); 690 | return 0; 691 | } 692 | 693 | #endif // !ARCH_RISCV -------------------------------------------------------------------------------- /src/sx126x.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file sx126x.c 3 | * 4 | * \brief SX126x driver implementation 5 | * 6 | * \copyright Revised BSD License, see section \ref LICENSE. 7 | * 8 | * \code 9 | * ______ _ 10 | * / _____) _ | | 11 | * ( (____ _____ ____ _| |_ _____ ____| |__ 12 | * \____ \| ___ | (_ _) ___ |/ ___) _ \ 13 | * _____) ) ____| | | || |_| ____( (___| | | | 14 | * (______/|_____)_|_|_| \__)_____)\____)_| |_| 15 | * (C)2013-2017 Semtech 16 | * 17 | * \endcode 18 | * 19 | * \author Miguel Luis ( Semtech ) 20 | * 21 | * \author Gregory Cristian ( Semtech ) 22 | */ 23 | #include 24 | #include 25 | #include "sx126x-utilities.h" 26 | #include "radio.h" 27 | #include "sx126x.h" 28 | #include "sx126x-board.h" 29 | 30 | /*! 31 | * \brief Internal frequency of the radio 32 | */ 33 | #define SX126X_XTAL_FREQ 32000000UL 34 | 35 | /*! 36 | * \brief Scaling factor used to perform fixed-point operations 37 | */ 38 | #define SX126X_PLL_STEP_SHIFT_AMOUNT ( 14 ) 39 | 40 | /*! 41 | * \brief PLL step - scaled with SX126X_PLL_STEP_SHIFT_AMOUNT 42 | */ 43 | #define SX126X_PLL_STEP_SCALED ( SX126X_XTAL_FREQ >> ( 25 - SX126X_PLL_STEP_SHIFT_AMOUNT ) ) 44 | 45 | /*! 46 | * \brief Maximum value for parameter symbNum in \ref SX126xSetLoRaSymbNumTimeout 47 | */ 48 | #define SX126X_MAX_LORA_SYMB_NUM_TIMEOUT 248 49 | 50 | /*! 51 | * \brief Radio registers definition 52 | */ 53 | typedef struct 54 | { 55 | uint16_t Addr; //!< The address of the register 56 | uint8_t Value; //!< The value of the register 57 | }RadioRegisters_t; 58 | 59 | /*! 60 | * \brief Stores the current packet type set in the radio 61 | */ 62 | static RadioPacketTypes_t PacketType; 63 | 64 | /*! 65 | * \brief Stores the current packet header type set in the radio 66 | */ 67 | static volatile RadioLoRaPacketLengthsMode_t LoRaHeaderType; 68 | 69 | /*! 70 | * \brief Stores the last frequency error measured on LoRa received packet 71 | */ 72 | volatile uint32_t FrequencyError = 0; 73 | 74 | /*! 75 | * \brief Hold the status of the Image calibration 76 | */ 77 | static bool ImageCalibrated = false; 78 | 79 | /*! 80 | * \brief Get the number of PLL steps for a given frequency in Hertz 81 | * 82 | * \param [in] freqInHz Frequency in Hertz 83 | * 84 | * \returns Number of PLL steps 85 | */ 86 | static uint32_t SX126xConvertFreqInHzToPllStep( uint32_t freqInHz ); 87 | 88 | /* 89 | * SX126x DIO IRQ callback functions prototype 90 | */ 91 | 92 | /*! 93 | * \brief DIO 0 IRQ callback 94 | */ 95 | void SX126xOnDioIrq( void ); 96 | 97 | /*! 98 | * \brief DIO 0 IRQ callback 99 | */ 100 | void SX126xSetPollingMode( void ); 101 | 102 | /*! 103 | * \brief DIO 0 IRQ callback 104 | */ 105 | void SX126xSetInterruptMode( void ); 106 | 107 | /* 108 | * \brief Process the IRQ if handled by the driver 109 | */ 110 | void SX126xProcessIrqs( void ); 111 | 112 | void SX126xInit( DioIrqHandler dioIrq ) 113 | { 114 | SX126xReset( ); 115 | 116 | SX126xIoIrqInit( dioIrq ); 117 | 118 | SX126xWakeup( ); 119 | SX126xSetStandby( STDBY_RC ); 120 | 121 | // Initialize TCXO control 122 | SX126xIoTcxoInit( ); 123 | 124 | // Initialize RF switch control 125 | SX126xIoRfSwitchInit( ); 126 | 127 | // Force image calibration 128 | ImageCalibrated = false; 129 | 130 | SX126xSetOperatingMode( MODE_STDBY_RC ); 131 | } 132 | 133 | void SX126xCheckDeviceReady( void ) 134 | { 135 | if( ( SX126xGetOperatingMode( ) == MODE_SLEEP ) || ( SX126xGetOperatingMode( ) == MODE_RX_DC ) ) 136 | { 137 | SX126xWakeup( ); 138 | // Switch is turned off when device is in sleep mode and turned on is all other modes 139 | SX126xAntSwOn( ); 140 | } 141 | SX126xWaitOnBusy( ); 142 | } 143 | 144 | void SX126xSetPayload( uint8_t *payload, uint8_t size ) 145 | { 146 | SX126xWriteBuffer( 0x00, payload, size ); 147 | } 148 | 149 | uint8_t SX126xGetPayload( uint8_t *buffer, uint8_t *size, uint8_t maxSize ) 150 | { 151 | uint8_t offset = 0; 152 | 153 | SX126xGetRxBufferStatus( size, &offset ); 154 | if( *size > maxSize ) 155 | { 156 | return 1; 157 | } 158 | SX126xReadBuffer( offset, buffer, *size ); 159 | return 0; 160 | } 161 | 162 | void SX126xSendPayload( uint8_t *payload, uint8_t size, uint32_t timeout ) 163 | { 164 | SX126xSetPayload( payload, size ); 165 | SX126xSetTx( timeout ); 166 | } 167 | 168 | uint8_t SX126xSetSyncWord( uint8_t *syncWord ) 169 | { 170 | SX126xWriteRegisters( REG_LR_SYNCWORDBASEADDRESS, syncWord, 8 ); 171 | return 0; 172 | } 173 | 174 | void SX126xSetCrcSeed( uint16_t seed ) 175 | { 176 | uint8_t buf[2]; 177 | 178 | buf[0] = ( uint8_t )( ( seed >> 8 ) & 0xFF ); 179 | buf[1] = ( uint8_t )( seed & 0xFF ); 180 | 181 | switch( SX126xGetPacketType( ) ) 182 | { 183 | case PACKET_TYPE_GFSK: 184 | SX126xWriteRegisters( REG_LR_CRCSEEDBASEADDR, buf, 2 ); 185 | break; 186 | 187 | default: 188 | break; 189 | } 190 | } 191 | 192 | void SX126xSetCrcPolynomial( uint16_t polynomial ) 193 | { 194 | uint8_t buf[2]; 195 | 196 | buf[0] = ( uint8_t )( ( polynomial >> 8 ) & 0xFF ); 197 | buf[1] = ( uint8_t )( polynomial & 0xFF ); 198 | 199 | switch( SX126xGetPacketType( ) ) 200 | { 201 | case PACKET_TYPE_GFSK: 202 | SX126xWriteRegisters( REG_LR_CRCPOLYBASEADDR, buf, 2 ); 203 | break; 204 | 205 | default: 206 | break; 207 | } 208 | } 209 | 210 | void SX126xSetWhiteningSeed( uint16_t seed ) 211 | { 212 | uint8_t regValue = 0; 213 | 214 | switch( SX126xGetPacketType( ) ) 215 | { 216 | case PACKET_TYPE_GFSK: 217 | regValue = SX126xReadRegister( REG_LR_WHITSEEDBASEADDR_MSB ) & 0xFE; 218 | regValue = ( ( seed >> 8 ) & 0x01 ) | regValue; 219 | SX126xWriteRegister( REG_LR_WHITSEEDBASEADDR_MSB, regValue ); // only 1 bit. 220 | SX126xWriteRegister( REG_LR_WHITSEEDBASEADDR_LSB, ( uint8_t )seed ); 221 | break; 222 | 223 | default: 224 | break; 225 | } 226 | } 227 | 228 | uint32_t SX126xGetRandom( void ) 229 | { 230 | uint32_t number = 0; 231 | uint8_t regAnaLna = 0; 232 | uint8_t regAnaMixer = 0; 233 | 234 | regAnaLna = SX126xReadRegister( REG_ANA_LNA ); 235 | SX126xWriteRegister( REG_ANA_LNA, regAnaLna & ~( 1 << 0 ) ); 236 | 237 | regAnaMixer = SX126xReadRegister( REG_ANA_MIXER ); 238 | SX126xWriteRegister( REG_ANA_MIXER, regAnaMixer & ~( 1 << 7 ) ); 239 | 240 | // Set radio in continuous reception 241 | SX126xSetRx( 0xFFFFFF ); // Rx Continuous 242 | 243 | SX126xReadRegisters( RANDOM_NUMBER_GENERATORBASEADDR, ( uint8_t* )&number, 4 ); 244 | 245 | SX126xSetStandby( STDBY_RC ); 246 | 247 | SX126xWriteRegister( REG_ANA_LNA, regAnaLna ); 248 | SX126xWriteRegister( REG_ANA_MIXER, regAnaMixer ); 249 | 250 | return number; 251 | } 252 | 253 | void SX126xSetSleep( SleepParams_t sleepConfig ) 254 | { 255 | SX126xAntSwOff( ); 256 | 257 | uint8_t value = ( ( ( uint8_t )sleepConfig.Fields.WarmStart << 2 ) | 258 | ( ( uint8_t )sleepConfig.Fields.Reset << 1 ) | 259 | ( ( uint8_t )sleepConfig.Fields.WakeUpRTC ) ); 260 | 261 | if( sleepConfig.Fields.WarmStart == 0 ) 262 | { 263 | // Force image calibration 264 | ImageCalibrated = false; 265 | } 266 | SX126xWriteCommand( RADIO_SET_SLEEP, &value, 1 ); 267 | SX126xSetOperatingMode( MODE_SLEEP ); 268 | } 269 | 270 | void SX126xSetStandby( RadioStandbyModes_t standbyConfig ) 271 | { 272 | SX126xWriteCommand( RADIO_SET_STANDBY, ( uint8_t* )&standbyConfig, 1 ); 273 | if( standbyConfig == STDBY_RC ) 274 | { 275 | SX126xSetOperatingMode( MODE_STDBY_RC ); 276 | } 277 | else 278 | { 279 | SX126xSetOperatingMode( MODE_STDBY_XOSC ); 280 | } 281 | } 282 | 283 | void SX126xSetFs( void ) 284 | { 285 | SX126xWriteCommand( RADIO_SET_FS, 0, 0 ); 286 | SX126xSetOperatingMode( MODE_FS ); 287 | } 288 | 289 | void SX126xSetTx( uint32_t timeout ) 290 | { 291 | uint8_t buf[3]; 292 | 293 | SX126xSetOperatingMode( MODE_TX ); 294 | 295 | buf[0] = ( uint8_t )( ( timeout >> 16 ) & 0xFF ); 296 | buf[1] = ( uint8_t )( ( timeout >> 8 ) & 0xFF ); 297 | buf[2] = ( uint8_t )( timeout & 0xFF ); 298 | SX126xWriteCommand( RADIO_SET_TX, buf, 3 ); 299 | } 300 | 301 | void SX126xSetRx( uint32_t timeout ) 302 | { 303 | uint8_t buf[3]; 304 | 305 | SX126xSetOperatingMode( MODE_RX ); 306 | 307 | SX126xWriteRegister( REG_RX_GAIN, 0x94 ); // default gain 308 | 309 | buf[0] = ( uint8_t )( ( timeout >> 16 ) & 0xFF ); 310 | buf[1] = ( uint8_t )( ( timeout >> 8 ) & 0xFF ); 311 | buf[2] = ( uint8_t )( timeout & 0xFF ); 312 | SX126xWriteCommand( RADIO_SET_RX, buf, 3 ); 313 | } 314 | 315 | void SX126xSetRxBoosted( uint32_t timeout ) 316 | { 317 | uint8_t buf[3]; 318 | 319 | SX126xSetOperatingMode( MODE_RX ); 320 | 321 | SX126xWriteRegister( REG_RX_GAIN, 0x96 ); // max LNA gain, increase current by ~2mA for around ~3dB in sensitivity 322 | 323 | buf[0] = ( uint8_t )( ( timeout >> 16 ) & 0xFF ); 324 | buf[1] = ( uint8_t )( ( timeout >> 8 ) & 0xFF ); 325 | buf[2] = ( uint8_t )( timeout & 0xFF ); 326 | SX126xWriteCommand( RADIO_SET_RX, buf, 3 ); 327 | } 328 | 329 | void SX126xSetRxDutyCycle( uint32_t rxTime, uint32_t sleepTime ) 330 | { 331 | uint8_t buf[6]; 332 | 333 | buf[0] = ( uint8_t )( ( rxTime >> 16 ) & 0xFF ); 334 | buf[1] = ( uint8_t )( ( rxTime >> 8 ) & 0xFF ); 335 | buf[2] = ( uint8_t )( rxTime & 0xFF ); 336 | buf[3] = ( uint8_t )( ( sleepTime >> 16 ) & 0xFF ); 337 | buf[4] = ( uint8_t )( ( sleepTime >> 8 ) & 0xFF ); 338 | buf[5] = ( uint8_t )( sleepTime & 0xFF ); 339 | SX126xWriteCommand( RADIO_SET_RXDUTYCYCLE, buf, 6 ); 340 | SX126xSetOperatingMode( MODE_RX_DC ); 341 | } 342 | 343 | void SX126xSetCad( void ) 344 | { 345 | SX126xWriteCommand( RADIO_SET_CAD, 0, 0 ); 346 | SX126xSetOperatingMode( MODE_CAD ); 347 | } 348 | 349 | void SX126xSetTxContinuousWave( void ) 350 | { 351 | SX126xWriteCommand( RADIO_SET_TXCONTINUOUSWAVE, 0, 0 ); 352 | SX126xSetOperatingMode( MODE_TX ); 353 | } 354 | 355 | void SX126xSetTxInfinitePreamble( void ) 356 | { 357 | SX126xWriteCommand( RADIO_SET_TXCONTINUOUSPREAMBLE, 0, 0 ); 358 | SX126xSetOperatingMode( MODE_TX ); 359 | } 360 | 361 | void SX126xSetStopRxTimerOnPreambleDetect( bool enable ) 362 | { 363 | SX126xWriteCommand( RADIO_SET_STOPRXTIMERONPREAMBLE, ( uint8_t* )&enable, 1 ); 364 | } 365 | 366 | void SX126xSetLoRaSymbNumTimeout( uint8_t symbNum ) 367 | { 368 | uint8_t mant = ( ( ( symbNum > SX126X_MAX_LORA_SYMB_NUM_TIMEOUT ) ? 369 | SX126X_MAX_LORA_SYMB_NUM_TIMEOUT : 370 | symbNum ) + 1 ) >> 1; 371 | uint8_t exp = 0; 372 | uint8_t reg = 0; 373 | 374 | while( mant > 31 ) 375 | { 376 | mant = ( mant + 3 ) >> 2; 377 | exp++; 378 | } 379 | 380 | reg = mant << ( 2 * exp + 1 ); 381 | SX126xWriteCommand( RADIO_SET_LORASYMBTIMEOUT, ®, 1 ); 382 | 383 | if( symbNum != 0 ) 384 | { 385 | reg = exp + ( mant << 3 ); 386 | SX126xWriteRegister( REG_LR_SYNCH_TIMEOUT, reg ); 387 | } 388 | } 389 | 390 | void SX126xSetRegulatorMode( RadioRegulatorMode_t mode ) 391 | { 392 | SX126xWriteCommand( RADIO_SET_REGULATORMODE, ( uint8_t* )&mode, 1 ); 393 | } 394 | 395 | void SX126xCalibrate( CalibrationParams_t calibParam ) 396 | { 397 | uint8_t value = ( ( ( uint8_t )calibParam.Fields.ImgEnable << 6 ) | 398 | ( ( uint8_t )calibParam.Fields.ADCBulkPEnable << 5 ) | 399 | ( ( uint8_t )calibParam.Fields.ADCBulkNEnable << 4 ) | 400 | ( ( uint8_t )calibParam.Fields.ADCPulseEnable << 3 ) | 401 | ( ( uint8_t )calibParam.Fields.PLLEnable << 2 ) | 402 | ( ( uint8_t )calibParam.Fields.RC13MEnable << 1 ) | 403 | ( ( uint8_t )calibParam.Fields.RC64KEnable ) ); 404 | 405 | SX126xWriteCommand( RADIO_CALIBRATE, &value, 1 ); 406 | } 407 | 408 | void SX126xCalibrateImage( uint32_t freq ) 409 | { 410 | uint8_t calFreq[2]; 411 | 412 | if( freq > 900000000 ) 413 | { 414 | calFreq[0] = 0xE1; 415 | calFreq[1] = 0xE9; 416 | } 417 | else if( freq > 850000000 ) 418 | { 419 | calFreq[0] = 0xD7; 420 | calFreq[1] = 0xDB; 421 | } 422 | else if( freq > 770000000 ) 423 | { 424 | calFreq[0] = 0xC1; 425 | calFreq[1] = 0xC5; 426 | } 427 | else if( freq > 460000000 ) 428 | { 429 | calFreq[0] = 0x75; 430 | calFreq[1] = 0x81; 431 | } 432 | else if( freq > 425000000 ) 433 | { 434 | calFreq[0] = 0x6B; 435 | calFreq[1] = 0x6F; 436 | } 437 | SX126xWriteCommand( RADIO_CALIBRATEIMAGE, calFreq, 2 ); 438 | } 439 | 440 | void SX126xSetPaConfig( uint8_t paDutyCycle, uint8_t hpMax, uint8_t deviceSel, uint8_t paLut ) 441 | { 442 | printf("SX126xSetPaConfig: paDutyCycle=%d, hpMax=%d, deviceSel=%d, paLut=%d \r\n", (int) paDutyCycle, (int) hpMax, (int) deviceSel, (int) paLut); 443 | uint8_t buf[4]; 444 | 445 | buf[0] = paDutyCycle; 446 | buf[1] = hpMax; 447 | buf[2] = deviceSel; 448 | buf[3] = paLut; 449 | SX126xWriteCommand( RADIO_SET_PACONFIG, buf, 4 ); 450 | } 451 | 452 | void SX126xSetRxTxFallbackMode( uint8_t fallbackMode ) 453 | { 454 | SX126xWriteCommand( RADIO_SET_TXFALLBACKMODE, &fallbackMode, 1 ); 455 | } 456 | 457 | void SX126xSetDioIrqParams( uint16_t irqMask, uint16_t dio1Mask, uint16_t dio2Mask, uint16_t dio3Mask ) 458 | { 459 | uint8_t buf[8]; 460 | 461 | buf[0] = ( uint8_t )( ( irqMask >> 8 ) & 0x00FF ); 462 | buf[1] = ( uint8_t )( irqMask & 0x00FF ); 463 | buf[2] = ( uint8_t )( ( dio1Mask >> 8 ) & 0x00FF ); 464 | buf[3] = ( uint8_t )( dio1Mask & 0x00FF ); 465 | buf[4] = ( uint8_t )( ( dio2Mask >> 8 ) & 0x00FF ); 466 | buf[5] = ( uint8_t )( dio2Mask & 0x00FF ); 467 | buf[6] = ( uint8_t )( ( dio3Mask >> 8 ) & 0x00FF ); 468 | buf[7] = ( uint8_t )( dio3Mask & 0x00FF ); 469 | SX126xWriteCommand( RADIO_CFG_DIOIRQ, buf, 8 ); 470 | } 471 | 472 | uint16_t SX126xGetIrqStatus( void ) 473 | { 474 | uint8_t irqStatus[2]; 475 | 476 | SX126xReadCommand( RADIO_GET_IRQSTATUS, irqStatus, 2 ); 477 | return ( irqStatus[0] << 8 ) | irqStatus[1]; 478 | } 479 | 480 | void SX126xSetDio2AsRfSwitchCtrl( uint8_t enable ) 481 | { 482 | SX126xWriteCommand( RADIO_SET_RFSWITCHMODE, &enable, 1 ); 483 | } 484 | 485 | void SX126xSetDio3AsTcxoCtrl( RadioTcxoCtrlVoltage_t tcxoVoltage, uint32_t timeout ) 486 | { 487 | uint8_t buf[4]; 488 | 489 | buf[0] = tcxoVoltage & 0x07; 490 | buf[1] = ( uint8_t )( ( timeout >> 16 ) & 0xFF ); 491 | buf[2] = ( uint8_t )( ( timeout >> 8 ) & 0xFF ); 492 | buf[3] = ( uint8_t )( timeout & 0xFF ); 493 | 494 | SX126xWriteCommand( RADIO_SET_TCXOMODE, buf, 4 ); 495 | } 496 | 497 | void SX126xSetRfFrequency( uint32_t frequency ) 498 | { 499 | uint8_t buf[4]; 500 | 501 | if( ImageCalibrated == false ) 502 | { 503 | SX126xCalibrateImage( frequency ); 504 | ImageCalibrated = true; 505 | } 506 | 507 | uint32_t freqInPllSteps = SX126xConvertFreqInHzToPllStep( frequency ); 508 | 509 | buf[0] = ( uint8_t )( ( freqInPllSteps >> 24 ) & 0xFF ); 510 | buf[1] = ( uint8_t )( ( freqInPllSteps >> 16 ) & 0xFF ); 511 | buf[2] = ( uint8_t )( ( freqInPllSteps >> 8 ) & 0xFF ); 512 | buf[3] = ( uint8_t )( freqInPllSteps & 0xFF ); 513 | SX126xWriteCommand( RADIO_SET_RFFREQUENCY, buf, 4 ); 514 | } 515 | 516 | void SX126xSetPacketType( RadioPacketTypes_t packetType ) 517 | { 518 | // Save packet type internally to avoid questioning the radio 519 | PacketType = packetType; 520 | SX126xWriteCommand( RADIO_SET_PACKETTYPE, ( uint8_t* )&packetType, 1 ); 521 | } 522 | 523 | RadioPacketTypes_t SX126xGetPacketType( void ) 524 | { 525 | return PacketType; 526 | } 527 | 528 | void SX126xSetTxParams( int8_t power, RadioRampTimes_t rampTime ) 529 | { 530 | printf("SX126xSetTxParams: power=%d, rampTime=%d\r\n", (int) power, (int) rampTime); 531 | uint8_t buf[2]; 532 | 533 | if( SX126xGetDeviceId( ) == SX1261 ) 534 | { 535 | if( power == 15 ) 536 | { 537 | SX126xSetPaConfig( 0x06, 0x00, 0x01, 0x01 ); 538 | } 539 | else 540 | { 541 | SX126xSetPaConfig( 0x04, 0x00, 0x01, 0x01 ); 542 | } 543 | if( power >= 14 ) 544 | { 545 | power = 14; 546 | } 547 | else if( power < -17 ) 548 | { 549 | power = -17; 550 | } 551 | //// TODO: Set the current max value in the over current protection. From SX126x-Arduino/src/radio/sx126x/sx126x.cpp 552 | SX126xWriteRegister(REG_OCP, 0x18); // current max is 80 mA for the whole device 553 | } 554 | else // sx1262 555 | { 556 | // WORKAROUND - Better Resistance of the SX1262 Tx to Antenna Mismatch, see DS_SX1261-2_V1.2 datasheet chapter 15.2 557 | SX126xWriteRegister( REG_TX_CLAMP_CFG, SX126xReadRegister( REG_TX_CLAMP_CFG ) | ( 0x0F << 1 ) ); 558 | // WORKAROUND END 559 | 560 | SX126xSetPaConfig( 0x04, 0x07, 0x00, 0x01 ); 561 | if( power > 22 ) 562 | { 563 | power = 22; 564 | } 565 | else if( power < -9 ) 566 | { 567 | power = -9; 568 | } 569 | 570 | //// TODO: Set the current max value in the over current protection. From SX126x-Arduino/src/radio/sx126x/sx126x.cpp 571 | SX126xWriteRegister(REG_OCP, 0x38); // current max 160mA for the whole device 572 | } 573 | buf[0] = power; 574 | buf[1] = ( uint8_t )rampTime; 575 | SX126xWriteCommand( RADIO_SET_TXPARAMS, buf, 2 ); 576 | } 577 | 578 | void SX126xSetModulationParams( ModulationParams_t *modulationParams ) 579 | { 580 | uint8_t n; 581 | uint32_t tempVal = 0; 582 | uint8_t buf[8] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 583 | 584 | // Check if required configuration corresponds to the stored packet type 585 | // If not, silently update radio packet type 586 | if( PacketType != modulationParams->PacketType ) 587 | { 588 | SX126xSetPacketType( modulationParams->PacketType ); 589 | } 590 | 591 | switch( modulationParams->PacketType ) 592 | { 593 | case PACKET_TYPE_GFSK: 594 | n = 8; 595 | tempVal = ( uint32_t )( 32 * SX126X_XTAL_FREQ / modulationParams->Params.Gfsk.BitRate ); 596 | buf[0] = ( tempVal >> 16 ) & 0xFF; 597 | buf[1] = ( tempVal >> 8 ) & 0xFF; 598 | buf[2] = tempVal & 0xFF; 599 | buf[3] = modulationParams->Params.Gfsk.ModulationShaping; 600 | buf[4] = modulationParams->Params.Gfsk.Bandwidth; 601 | tempVal = SX126xConvertFreqInHzToPllStep( modulationParams->Params.Gfsk.Fdev ); 602 | buf[5] = ( tempVal >> 16 ) & 0xFF; 603 | buf[6] = ( tempVal >> 8 ) & 0xFF; 604 | buf[7] = ( tempVal& 0xFF ); 605 | SX126xWriteCommand( RADIO_SET_MODULATIONPARAMS, buf, n ); 606 | break; 607 | case PACKET_TYPE_LORA: 608 | n = 4; 609 | buf[0] = modulationParams->Params.LoRa.SpreadingFactor; 610 | buf[1] = modulationParams->Params.LoRa.Bandwidth; 611 | buf[2] = modulationParams->Params.LoRa.CodingRate; 612 | buf[3] = modulationParams->Params.LoRa.LowDatarateOptimize; 613 | 614 | SX126xWriteCommand( RADIO_SET_MODULATIONPARAMS, buf, n ); 615 | 616 | break; 617 | default: 618 | case PACKET_TYPE_NONE: 619 | return; 620 | } 621 | } 622 | 623 | void SX126xSetPacketParams( PacketParams_t *packetParams ) 624 | { 625 | uint8_t n; 626 | uint8_t crcVal = 0; 627 | uint8_t buf[9] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 628 | 629 | // Check if required configuration corresponds to the stored packet type 630 | // If not, silently update radio packet type 631 | if( PacketType != packetParams->PacketType ) 632 | { 633 | SX126xSetPacketType( packetParams->PacketType ); 634 | } 635 | 636 | switch( packetParams->PacketType ) 637 | { 638 | case PACKET_TYPE_GFSK: 639 | if( packetParams->Params.Gfsk.CrcLength == RADIO_CRC_2_BYTES_IBM ) 640 | { 641 | SX126xSetCrcSeed( CRC_IBM_SEED ); 642 | SX126xSetCrcPolynomial( CRC_POLYNOMIAL_IBM ); 643 | crcVal = RADIO_CRC_2_BYTES; 644 | } 645 | else if( packetParams->Params.Gfsk.CrcLength == RADIO_CRC_2_BYTES_CCIT ) 646 | { 647 | SX126xSetCrcSeed( CRC_CCITT_SEED ); 648 | SX126xSetCrcPolynomial( CRC_POLYNOMIAL_CCITT ); 649 | crcVal = RADIO_CRC_2_BYTES_INV; 650 | } 651 | else 652 | { 653 | crcVal = packetParams->Params.Gfsk.CrcLength; 654 | } 655 | n = 9; 656 | buf[0] = ( packetParams->Params.Gfsk.PreambleLength >> 8 ) & 0xFF; 657 | buf[1] = packetParams->Params.Gfsk.PreambleLength; 658 | buf[2] = packetParams->Params.Gfsk.PreambleMinDetect; 659 | buf[3] = ( packetParams->Params.Gfsk.SyncWordLength /*<< 3*/ ); // convert from byte to bit 660 | buf[4] = packetParams->Params.Gfsk.AddrComp; 661 | buf[5] = packetParams->Params.Gfsk.HeaderType; 662 | buf[6] = packetParams->Params.Gfsk.PayloadLength; 663 | buf[7] = crcVal; 664 | buf[8] = packetParams->Params.Gfsk.DcFree; 665 | break; 666 | case PACKET_TYPE_LORA: 667 | n = 6; 668 | buf[0] = ( packetParams->Params.LoRa.PreambleLength >> 8 ) & 0xFF; 669 | buf[1] = packetParams->Params.LoRa.PreambleLength; 670 | buf[2] = LoRaHeaderType = packetParams->Params.LoRa.HeaderType; 671 | buf[3] = packetParams->Params.LoRa.PayloadLength; 672 | buf[4] = packetParams->Params.LoRa.CrcMode; 673 | buf[5] = packetParams->Params.LoRa.InvertIQ; 674 | break; 675 | default: 676 | case PACKET_TYPE_NONE: 677 | return; 678 | } 679 | SX126xWriteCommand( RADIO_SET_PACKETPARAMS, buf, n ); 680 | } 681 | 682 | void SX126xSetCadParams( RadioLoRaCadSymbols_t cadSymbolNum, uint8_t cadDetPeak, uint8_t cadDetMin, RadioCadExitModes_t cadExitMode, uint32_t cadTimeout ) 683 | { 684 | uint8_t buf[7]; 685 | 686 | buf[0] = ( uint8_t )cadSymbolNum; 687 | buf[1] = cadDetPeak; 688 | buf[2] = cadDetMin; 689 | buf[3] = ( uint8_t )cadExitMode; 690 | buf[4] = ( uint8_t )( ( cadTimeout >> 16 ) & 0xFF ); 691 | buf[5] = ( uint8_t )( ( cadTimeout >> 8 ) & 0xFF ); 692 | buf[6] = ( uint8_t )( cadTimeout & 0xFF ); 693 | SX126xWriteCommand( RADIO_SET_CADPARAMS, buf, 7 ); 694 | SX126xSetOperatingMode( MODE_CAD ); 695 | } 696 | 697 | void SX126xSetBufferBaseAddress( uint8_t txBaseAddress, uint8_t rxBaseAddress ) 698 | { 699 | uint8_t buf[2]; 700 | 701 | buf[0] = txBaseAddress; 702 | buf[1] = rxBaseAddress; 703 | SX126xWriteCommand( RADIO_SET_BUFFERBASEADDRESS, buf, 2 ); 704 | } 705 | 706 | RadioStatus_t SX126xGetStatus( void ) 707 | { 708 | uint8_t stat = 0; 709 | RadioStatus_t status = { .Value = 0 }; 710 | 711 | stat = SX126xReadCommand( RADIO_GET_STATUS, NULL, 0 ); 712 | status.Fields.CmdStatus = ( stat & ( 0x07 << 1 ) ) >> 1; 713 | status.Fields.ChipMode = ( stat & ( 0x07 << 4 ) ) >> 4; 714 | return status; 715 | } 716 | 717 | int8_t SX126xGetRssiInst( void ) 718 | { 719 | uint8_t buf[1]; 720 | int8_t rssi = 0; 721 | 722 | SX126xReadCommand( RADIO_GET_RSSIINST, buf, 1 ); 723 | rssi = -buf[0] >> 1; 724 | return rssi; 725 | } 726 | 727 | void SX126xGetRxBufferStatus( uint8_t *payloadLength, uint8_t *rxStartBufferPointer ) 728 | { 729 | uint8_t status[2]; 730 | 731 | SX126xReadCommand( RADIO_GET_RXBUFFERSTATUS, status, 2 ); 732 | 733 | // In case of LORA fixed header, the payloadLength is obtained by reading 734 | // the register REG_LR_PAYLOADLENGTH 735 | if( ( SX126xGetPacketType( ) == PACKET_TYPE_LORA ) && ( LoRaHeaderType == LORA_PACKET_FIXED_LENGTH ) ) 736 | { 737 | *payloadLength = SX126xReadRegister( REG_LR_PAYLOADLENGTH ); 738 | } 739 | else 740 | { 741 | *payloadLength = status[0]; 742 | } 743 | *rxStartBufferPointer = status[1]; 744 | } 745 | 746 | void SX126xGetPacketStatus( PacketStatus_t *pktStatus ) 747 | { 748 | uint8_t status[3]; 749 | 750 | SX126xReadCommand( RADIO_GET_PACKETSTATUS, status, 3 ); 751 | 752 | pktStatus->packetType = SX126xGetPacketType( ); 753 | switch( pktStatus->packetType ) 754 | { 755 | case PACKET_TYPE_GFSK: 756 | pktStatus->Params.Gfsk.RxStatus = status[0]; 757 | pktStatus->Params.Gfsk.RssiSync = -status[1] >> 1; 758 | pktStatus->Params.Gfsk.RssiAvg = -status[2] >> 1; 759 | pktStatus->Params.Gfsk.FreqError = 0; 760 | break; 761 | 762 | case PACKET_TYPE_LORA: 763 | pktStatus->Params.LoRa.RssiPkt = -status[0] >> 1; 764 | // Returns SNR value [dB] rounded to the nearest integer value 765 | pktStatus->Params.LoRa.SnrPkt = ( ( ( int8_t )status[1] ) + 2 ) >> 2; 766 | pktStatus->Params.LoRa.SignalRssiPkt = -status[2] >> 1; 767 | pktStatus->Params.LoRa.FreqError = FrequencyError; 768 | break; 769 | 770 | default: 771 | case PACKET_TYPE_NONE: 772 | // In that specific case, we set everything in the pktStatus to zeros 773 | // and reset the packet type accordingly 774 | memset( pktStatus, 0, sizeof( PacketStatus_t ) ); 775 | pktStatus->packetType = PACKET_TYPE_NONE; 776 | break; 777 | } 778 | } 779 | 780 | RadioError_t SX126xGetDeviceErrors( void ) 781 | { 782 | uint8_t err[] = { 0, 0 }; 783 | RadioError_t error = { .Value = 0 }; 784 | 785 | SX126xReadCommand( RADIO_GET_ERROR, ( uint8_t* )err, 2 ); 786 | error.Fields.PaRamp = ( err[0] & ( 1 << 0 ) ) >> 0; 787 | error.Fields.PllLock = ( err[1] & ( 1 << 6 ) ) >> 6; 788 | error.Fields.XoscStart = ( err[1] & ( 1 << 5 ) ) >> 5; 789 | error.Fields.ImgCalib = ( err[1] & ( 1 << 4 ) ) >> 4; 790 | error.Fields.AdcCalib = ( err[1] & ( 1 << 3 ) ) >> 3; 791 | error.Fields.PllCalib = ( err[1] & ( 1 << 2 ) ) >> 2; 792 | error.Fields.Rc13mCalib = ( err[1] & ( 1 << 1 ) ) >> 1; 793 | error.Fields.Rc64kCalib = ( err[1] & ( 1 << 0 ) ) >> 0; 794 | return error; 795 | } 796 | 797 | void SX126xClearDeviceErrors( void ) 798 | { 799 | uint8_t buf[2] = { 0x00, 0x00 }; 800 | SX126xWriteCommand( RADIO_CLR_ERROR, buf, 2 ); 801 | } 802 | 803 | void SX126xClearIrqStatus( uint16_t irq ) 804 | { 805 | uint8_t buf[2]; 806 | 807 | buf[0] = ( uint8_t )( ( ( uint16_t )irq >> 8 ) & 0x00FF ); 808 | buf[1] = ( uint8_t )( ( uint16_t )irq & 0x00FF ); 809 | SX126xWriteCommand( RADIO_CLR_IRQSTATUS, buf, 2 ); 810 | } 811 | 812 | static uint32_t SX126xConvertFreqInHzToPllStep( uint32_t freqInHz ) 813 | { 814 | uint32_t stepsInt; 815 | uint32_t stepsFrac; 816 | 817 | // pllSteps = freqInHz / (SX126X_XTAL_FREQ / 2^19 ) 818 | // Get integer and fractional parts of the frequency computed with a PLL step scaled value 819 | stepsInt = freqInHz / SX126X_PLL_STEP_SCALED; 820 | stepsFrac = freqInHz - ( stepsInt * SX126X_PLL_STEP_SCALED ); 821 | 822 | // Apply the scaling factor to retrieve a frequency in Hz (+ ceiling) 823 | return ( stepsInt << SX126X_PLL_STEP_SHIFT_AMOUNT ) + 824 | ( ( ( stepsFrac << SX126X_PLL_STEP_SHIFT_AMOUNT ) + ( SX126X_PLL_STEP_SCALED >> 1 ) ) / 825 | SX126X_PLL_STEP_SCALED ); 826 | } 827 | -------------------------------------------------------------------------------- /src/sx126x-board.c: -------------------------------------------------------------------------------- 1 | /*! 2 | * \file sx1262mbxcas-board.c 3 | * 4 | * \brief Target board SX1262MBXCAS shield driver implementation 5 | * 6 | * \copyright Revised BSD License, see section \ref LICENSE. 7 | * 8 | * \code 9 | * ______ _ 10 | * / _____) _ | | 11 | * ( (____ _____ ____ _| |_ _____ ____| |__ 12 | * \____ \| ___ | (_ _) ___ |/ ___) _ \ 13 | * _____) ) ____| | | || |_| ____( (___| | | | 14 | * (______/|_____)_|_|_| \__)_____)\____)_| |_| 15 | * (C)2013-2017 Semtech 16 | * 17 | * \endcode 18 | * 19 | * \author Miguel Luis ( Semtech ) 20 | * 21 | * \author Gregory Cristian ( Semtech ) 22 | */ 23 | #include 24 | #include 25 | #include 26 | #include // For spi_ioc_transfer_t 27 | #include // For hal_spi_transfer 28 | #include // For spi_init 29 | #include // For bl_gpio_output_set 30 | #include // For bl_irq_register_with_ctx 31 | #include // For GLB_GPIO_Func_Init 32 | #include "nimble_npl.h" // For NimBLE Porting Layer (multitasking functions) 33 | #include "radio.h" 34 | #include "sx126x.h" 35 | #include "sx126x-board.h" 36 | 37 | static int register_gpio_handler( 38 | uint8_t gpioPin, // GPIO Pin Number 39 | DioIrqHandler *handler, // GPIO Handler Function 40 | uint8_t intCtrlMod, // GPIO Interrupt Control Mode (see below) 41 | uint8_t intTrgMod, // GPIO Interrupt Trigger Mode (see below) 42 | uint8_t pullup, // 1 for pullup, 0 for no pullup 43 | uint8_t pulldown); // 1 for pulldown, 0 for no pulldown 44 | static void handle_gpio_interrupt(void *arg); 45 | 46 | #if defined( USE_RADIO_DEBUG ) 47 | /*! 48 | * \brief Writes new Tx debug pin state 49 | * 50 | * \param [IN] state Debug pin state 51 | */ 52 | static void SX126xDbgPinTxWrite( uint8_t state ); 53 | 54 | /*! 55 | * \brief Writes new Rx debug pin state 56 | * 57 | * \param [IN] state Debug pin state 58 | */ 59 | static void SX126xDbgPinRxWrite( uint8_t state ); 60 | #endif 61 | 62 | /*! 63 | * \brief Holds the internal operating mode of the radio 64 | */ 65 | static RadioOperatingModes_t OperatingMode; 66 | 67 | /*! 68 | * Antenna switch GPIO pins objects 69 | */ 70 | Gpio_t AntPow; 71 | Gpio_t DeviceSel; 72 | 73 | /*! 74 | * Debug GPIO pins objects 75 | */ 76 | #if defined( USE_RADIO_DEBUG ) 77 | Gpio_t DbgPinTx; 78 | Gpio_t DbgPinRx; 79 | #endif 80 | 81 | /////////////////////////////////////////////////////////////////////////////// 82 | // GPIO Functions 83 | 84 | static void GpioInitOutput(uint8_t pin, uint8_t value) { 85 | // Configure pin as a GPIO Pin 86 | GLB_GPIO_Type pins[1]; 87 | pins[0] = pin; 88 | BL_Err_Type rc2 = GLB_GPIO_Func_Init( 89 | GPIO_FUN_SWGPIO, // Configure as GPIO 90 | pins, // Pins to be configured 91 | sizeof(pins) / sizeof(pins[0]) // Number of pins (1) 92 | ); 93 | assert(rc2 == SUCCESS); 94 | 95 | // Configure pin as a GPIO Output Pin (instead of GPIO Input) 96 | int rc = bl_gpio_enable_output(pin, 0, 0); 97 | assert(rc == 0); 98 | 99 | // Set pin to Low or High 100 | rc = bl_gpio_output_set(pin, value); 101 | assert(rc == 0); 102 | } 103 | 104 | static void GpioInitInput(uint8_t pin, uint8_t pullup, uint8_t pulldown) { 105 | // Configure pin as a GPIO Pin 106 | GLB_GPIO_Type pins[1]; 107 | pins[0] = pin; 108 | BL_Err_Type rc2 = GLB_GPIO_Func_Init( 109 | GPIO_FUN_SWGPIO, // Configure as GPIO 110 | pins, // Pins to be configured 111 | sizeof(pins) / sizeof(pins[0]) // Number of pins (1) 112 | ); 113 | assert(rc2 == SUCCESS); 114 | 115 | // Configure Reset pin as a GPIO Input Pin 116 | int rc = bl_gpio_enable_input(pin, pullup, pulldown); 117 | assert(rc == 0); 118 | } 119 | 120 | /////////////////////////////////////////////////////////////////////////////// 121 | // SPI Functions 122 | 123 | /// SPI Device Instance 124 | spi_dev_t spi_device; 125 | 126 | /// SPI Transmit Buffer (1 byte) 127 | static uint8_t spi_tx_buf[1]; 128 | 129 | /// SPI Receive Buffer (1 byte) 130 | static uint8_t spi_rx_buf[1]; 131 | 132 | /// Blocking call to send a value on the SPI. Returns the value received from the SPI Peripheral. 133 | /// Assume that we are sending and receiving 8-bit values on SPI. 134 | /// Assume Chip Select Pin has already been set to Low by caller. 135 | /// TODO: We should combine multiple SPI DMA Requests, instead of handling one byte at a time 136 | uint16_t SpiInOut(int spi_num, uint16_t val) { 137 | // Populate the transmit buffer 138 | spi_tx_buf[0] = val; 139 | 140 | // Clear the receive buffer 141 | memset(&spi_rx_buf, 0, sizeof(spi_rx_buf)); 142 | 143 | // Prepare SPI Transfer 144 | static spi_ioc_transfer_t transfer; 145 | memset(&transfer, 0, sizeof(transfer)); 146 | transfer.tx_buf = (uint32_t) spi_tx_buf; // Transmit Buffer 147 | transfer.rx_buf = (uint32_t) spi_rx_buf; // Receive Buffer 148 | transfer.len = 1; // How many bytes 149 | 150 | // Assume Chip Select Pin has already been set to Low by caller 151 | 152 | // Execute the SPI Transfer with the DMA Controller 153 | int rc = hal_spi_transfer( 154 | &spi_device, // SPI Device 155 | &transfer, // SPI Transfers 156 | 1 // How many transfers (Number of requests, not bytes) 157 | ); 158 | assert(rc == 0); 159 | 160 | // Assume Chip Select Pin will be set to High by caller 161 | 162 | // Return the received byte 163 | return spi_rx_buf[0]; 164 | } 165 | 166 | /////////////////////////////////////////////////////////////////////////////// 167 | 168 | /// Initialise GPIO Pins and SPI Port. Called by SX126xIoIrqInit. 169 | /// Note: This is different from the Reference Implementation, 170 | /// which initialises the GPIO Pins and SPI Port at startup. 171 | void SX126xIoInit( void ) 172 | { 173 | printf("SX126xIoInit\r\n"); 174 | GpioInitOutput( SX126X_SPI_CS_PIN, 1 ); 175 | GpioInitInput( SX126X_BUSY_PIN, 0, 0 ); 176 | GpioInitInput( SX126X_DIO1, 0, 0 ); 177 | ////GpioInitInput( SX126X_DEVICE_SEL_PIN, 0, 0 ); 178 | if (SX126X_DEBUG_CS_PIN >= 0) { GpioInitOutput( SX126X_DEBUG_CS_PIN, 1 ); } 179 | 180 | // Note: We must swap MISO and MOSI to comply with the SPI Pin Definitions in BL602 / BL604 Reference Manual 181 | printf("Swap MISO and MOSI\r\n"); 182 | int rc = GLB_Swap_SPI_0_MOSI_With_MISO(ENABLE); assert(rc == 0); 183 | 184 | // Configure the SPI Port 185 | rc = spi_init( 186 | &spi_device, // SPI Device 187 | SX126X_SPI_IDX, // SPI Port 188 | 0, // SPI Mode: 0 for Controller 189 | // TODO: Due to a quirk in BL602 SPI, we must set 190 | // SPI Polarity-Phase to 1 (CPOL=0, CPHA=1). 191 | // But actually Polarity-Phase for SX126X should be 0 (CPOL=0, CPHA=0). 192 | 1, // SPI Polarity-Phase 193 | SX126X_SPI_BAUDRATE, // SPI Frequency 194 | 2, // Transmit DMA Channel 195 | 3, // Receive DMA Channel 196 | SX126X_SPI_CLK_PIN, // SPI Clock Pin 197 | SX126X_SPI_CS_OLD, // Unused SPI Chip Select Pin 198 | SX126X_SPI_SDO_PIN, // SPI Serial Data Out Pin (formerly MOSI) 199 | SX126X_SPI_SDI_PIN // SPI Serial Data In Pin (formerly MISO) 200 | ); 201 | assert(rc == 0); 202 | } 203 | 204 | /// Initialise GPIO Pins and SPI Port. Register GPIO Interrupt Handler for DIO1. 205 | /// Based on hal_button_register_handler_with_dts in https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_button.c 206 | /// Note: This is different from the Reference Implementation, 207 | /// which initialises the GPIO Pins and SPI Port at startup. 208 | void SX126xIoIrqInit( DioIrqHandler dioIrq ) 209 | { 210 | // Initialise GPIO Pins and SPI Port. 211 | // Note: This is different from the Reference Implementation, 212 | // which initialises the GPIO Pins and SPI Port at startup. 213 | SX126xIoInit(); 214 | 215 | printf("SX126X interrupt init\r\n"); 216 | assert(SX126X_DIO1 >= 0); 217 | assert(dioIrq != NULL); 218 | int rc = register_gpio_handler( // Register GPIO Handler... 219 | SX126X_DIO1, // GPIO Pin Number 220 | dioIrq, // GPIO Handler Function 221 | GLB_GPIO_INT_CONTROL_ASYNC, // Async Control Mode 222 | GLB_GPIO_INT_TRIG_POS_PULSE, // Trigger when GPIO level shifts from Low to High 223 | 0, // No pullup 224 | 0 // No pulldown 225 | ); 226 | assert(rc == 0); 227 | 228 | // Register Common Interrupt Handler for GPIO Interrupt 229 | bl_irq_register_with_ctx( 230 | GPIO_INT0_IRQn, // GPIO Interrupt 231 | handle_gpio_interrupt, // Interrupt Handler 232 | NULL // Argument for Interrupt Handler 233 | ); 234 | 235 | // Enable GPIO Interrupt 236 | bl_irq_enable(GPIO_INT0_IRQn); 237 | } 238 | 239 | void SX126xIoDeInit( void ) 240 | { 241 | printf("SX126xIoDeInit\r\n"); 242 | GpioInitOutput( SX126X_SPI_CS_PIN, 1 ); 243 | GpioInitInput( SX126X_BUSY_PIN, 0, 0 ); 244 | GpioInitInput( SX126X_DIO1, 0, 0 ); 245 | if (SX126X_DEBUG_CS_PIN >= 0) { GpioInitOutput( SX126X_DEBUG_CS_PIN, 1 ); } 246 | } 247 | 248 | void SX126xIoDbgInit( void ) 249 | { 250 | #if defined( USE_RADIO_DEBUG ) 251 | GpioInitOutput( SX126X_DBG_PIN_TX, 0 ); 252 | GpioInitOutput( SX126X_DBG_PIN_RX, 0 ); 253 | #endif 254 | } 255 | 256 | void SX126xIoTcxoInit( void ) 257 | { 258 | // No TCXO component available on this board design. 259 | } 260 | 261 | uint32_t SX126xGetBoardTcxoWakeupTime( void ) 262 | { 263 | return SX126X_TCXO_WAKEUP_TIME; 264 | } 265 | 266 | void SX126xIoRfSwitchInit( void ) 267 | { 268 | SX126xSetDio2AsRfSwitchCtrl( true ); 269 | } 270 | 271 | RadioOperatingModes_t SX126xGetOperatingMode( void ) 272 | { 273 | return OperatingMode; 274 | } 275 | 276 | void SX126xSetOperatingMode( RadioOperatingModes_t mode ) 277 | { 278 | OperatingMode = mode; 279 | #if defined( USE_RADIO_DEBUG ) 280 | switch( mode ) 281 | { 282 | case MODE_TX: 283 | SX126xDbgPinTxWrite( 1 ); 284 | SX126xDbgPinRxWrite( 0 ); 285 | break; 286 | case MODE_RX: 287 | case MODE_RX_DC: 288 | SX126xDbgPinTxWrite( 0 ); 289 | SX126xDbgPinRxWrite( 1 ); 290 | break; 291 | default: 292 | SX126xDbgPinTxWrite( 0 ); 293 | SX126xDbgPinRxWrite( 0 ); 294 | break; 295 | } 296 | #endif 297 | } 298 | 299 | #ifdef TODO 300 | void SX126xReset( void ) 301 | { 302 | DelayMs( 10 ); 303 | GpioInitOutput( SX126X_NRESET, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); 304 | DelayMs( 20 ); 305 | GpioInitAnalogic( SX126X_NRESET, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); // internal pull-up 306 | DelayMs( 10 ); 307 | } 308 | #endif // TODO 309 | 310 | void SX126xReset(void) 311 | { 312 | //// #warning Check SX126xReset 313 | 314 | printf("SX126xReset\r\n"); 315 | 316 | // Configure Reset pin as a GPIO Pin 317 | GLB_GPIO_Type pins[1]; 318 | pins[0] = SX126X_NRESET; 319 | BL_Err_Type rc2 = GLB_GPIO_Func_Init( 320 | GPIO_FUN_SWGPIO, // Configure as GPIO 321 | pins, // Pins to be configured 322 | sizeof(pins) / sizeof(pins[0]) // Number of pins (1) 323 | ); 324 | assert(rc2 == SUCCESS); 325 | 326 | // Configure Reset pin as a GPIO Output Pin (instead of GPIO Input) 327 | int rc = bl_gpio_enable_output(SX126X_NRESET, 0, 0); 328 | assert(rc == 0); 329 | 330 | // Set Reset pin to Low 331 | rc = bl_gpio_output_set(SX126X_NRESET, 1); 332 | assert(rc == 0); 333 | 334 | // Wait 1 ms 335 | DelayMs(1); 336 | 337 | // Configure Reset pin as a GPIO Input Pin, no pullup, no pulldown 338 | rc = bl_gpio_enable_input(SX126X_NRESET, 0, 0); 339 | assert(rc == 0); 340 | 341 | // Wait 6 ms 342 | DelayMs(6); 343 | } 344 | 345 | void SX126xWaitOnBusy( void ) 346 | { 347 | while( bl_gpio_input_get_value( SX126X_BUSY_PIN ) == 1 ); 348 | } 349 | 350 | void SX126xWakeup( void ) 351 | { 352 | printf("SX126xWakeup\r\n"); 353 | CRITICAL_SECTION_BEGIN( ); 354 | 355 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 0 ); 356 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 0 ); } 357 | 358 | SpiInOut( SX126X_SPI_IDX, RADIO_GET_STATUS ); 359 | SpiInOut( SX126X_SPI_IDX, 0x00 ); 360 | 361 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 1 ); 362 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 1 ); } 363 | 364 | // Wait for chip to be ready. 365 | SX126xWaitOnBusy( ); 366 | 367 | // Update operating mode context variable 368 | SX126xSetOperatingMode( MODE_STDBY_RC ); 369 | 370 | CRITICAL_SECTION_END( ); 371 | } 372 | 373 | void SX126xWriteCommand( RadioCommands_t command, uint8_t *buffer, uint16_t size ) 374 | { 375 | SX126xCheckDeviceReady( ); 376 | 377 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 0 ); 378 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 0 ); } 379 | 380 | SpiInOut( SX126X_SPI_IDX, ( uint8_t )command ); 381 | 382 | for( uint16_t i = 0; i < size; i++ ) 383 | { 384 | SpiInOut( SX126X_SPI_IDX, buffer[i] ); 385 | } 386 | 387 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 1 ); 388 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 1 ); } 389 | 390 | if( command != RADIO_SET_SLEEP ) 391 | { 392 | SX126xWaitOnBusy( ); 393 | } 394 | } 395 | 396 | uint8_t SX126xReadCommand( RadioCommands_t command, uint8_t *buffer, uint16_t size ) 397 | { 398 | printf("SX126xReadCommand\r\n"); 399 | uint8_t status = 0; 400 | 401 | SX126xCheckDeviceReady( ); 402 | 403 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 0 ); 404 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 0 ); } 405 | 406 | SpiInOut( SX126X_SPI_IDX, ( uint8_t )command ); 407 | status = SpiInOut( SX126X_SPI_IDX, 0x00 ); 408 | for( uint16_t i = 0; i < size; i++ ) 409 | { 410 | buffer[i] = SpiInOut( SX126X_SPI_IDX, 0 ); 411 | } 412 | 413 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 1 ); 414 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 1 ); } 415 | 416 | SX126xWaitOnBusy( ); 417 | 418 | return status; 419 | } 420 | 421 | void SX126xWriteRegisters( uint16_t address, uint8_t *buffer, uint16_t size ) 422 | { 423 | SX126xCheckDeviceReady( ); 424 | 425 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 0 ); 426 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 0 ); } 427 | 428 | SpiInOut( SX126X_SPI_IDX, RADIO_WRITE_REGISTER ); 429 | SpiInOut( SX126X_SPI_IDX, ( address & 0xFF00 ) >> 8 ); 430 | SpiInOut( SX126X_SPI_IDX, address & 0x00FF ); 431 | 432 | for( uint16_t i = 0; i < size; i++ ) 433 | { 434 | SpiInOut( SX126X_SPI_IDX, buffer[i] ); 435 | } 436 | 437 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 1 ); 438 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 1 ); } 439 | 440 | SX126xWaitOnBusy( ); 441 | } 442 | 443 | void SX126xWriteRegister( uint16_t address, uint8_t value ) 444 | { 445 | SX126xWriteRegisters( address, &value, 1 ); 446 | } 447 | 448 | void SX126xReadRegisters( uint16_t address, uint8_t *buffer, uint16_t size ) 449 | { 450 | SX126xCheckDeviceReady( ); 451 | 452 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 0 ); 453 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 0 ); } 454 | 455 | SpiInOut( SX126X_SPI_IDX, RADIO_READ_REGISTER ); 456 | SpiInOut( SX126X_SPI_IDX, ( address & 0xFF00 ) >> 8 ); 457 | SpiInOut( SX126X_SPI_IDX, address & 0x00FF ); 458 | SpiInOut( SX126X_SPI_IDX, 0 ); 459 | for( uint16_t i = 0; i < size; i++ ) 460 | { 461 | buffer[i] = SpiInOut( SX126X_SPI_IDX, 0 ); 462 | } 463 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 1 ); 464 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 1 ); } 465 | 466 | SX126xWaitOnBusy( ); 467 | } 468 | 469 | uint8_t SX126xReadRegister( uint16_t address ) 470 | { 471 | uint8_t data; 472 | SX126xReadRegisters( address, &data, 1 ); 473 | return data; 474 | } 475 | 476 | void SX126xWriteBuffer( uint8_t offset, uint8_t *buffer, uint8_t size ) 477 | { 478 | SX126xCheckDeviceReady( ); 479 | 480 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 0 ); 481 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 0 ); } 482 | 483 | SpiInOut( SX126X_SPI_IDX, RADIO_WRITE_BUFFER ); 484 | SpiInOut( SX126X_SPI_IDX, offset ); 485 | for( uint16_t i = 0; i < size; i++ ) 486 | { 487 | SpiInOut( SX126X_SPI_IDX, buffer[i] ); 488 | } 489 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 1 ); 490 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 1 ); } 491 | 492 | SX126xWaitOnBusy( ); 493 | } 494 | 495 | void SX126xReadBuffer( uint8_t offset, uint8_t *buffer, uint8_t size ) 496 | { 497 | SX126xCheckDeviceReady( ); 498 | 499 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 0 ); 500 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 0 ); } 501 | 502 | SpiInOut( SX126X_SPI_IDX, RADIO_READ_BUFFER ); 503 | SpiInOut( SX126X_SPI_IDX, offset ); 504 | SpiInOut( SX126X_SPI_IDX, 0 ); 505 | for( uint16_t i = 0; i < size; i++ ) 506 | { 507 | buffer[i] = SpiInOut( SX126X_SPI_IDX, 0 ); 508 | } 509 | bl_gpio_output_set( SX126X_SPI_CS_PIN, 1 ); 510 | if (SX126X_DEBUG_CS_PIN >= 0) { bl_gpio_output_set( SX126X_DEBUG_CS_PIN, 1 ); } 511 | 512 | SX126xWaitOnBusy( ); 513 | } 514 | 515 | void SX126xSetRfTxPower( int8_t power ) 516 | { 517 | printf("SX126xSetRfTxPower\r\n"); 518 | ////TODO: SX126xSetTxParams( power, RADIO_RAMP_40_US ); 519 | SX126xSetTxParams( power, RADIO_RAMP_3400_US );////TODO 520 | } 521 | 522 | uint8_t SX126xGetDeviceId( void ) 523 | { 524 | // For SX1262 525 | printf("SX126xGetDeviceId: SX1262\r\n"); 526 | return SX1262; 527 | 528 | // For SX1261 529 | // printf("SX126xGetDeviceId: SX1261\r\n"); 530 | // return SX1261; 531 | 532 | #ifdef NOTUSED 533 | if( bl_gpio_input_get_value( SX126X_DEVICE_SEL_PIN ) == 1 ) 534 | { 535 | printf("SX126xGetDeviceId: SX1261\r\n"); 536 | return SX1261; 537 | } 538 | else 539 | { 540 | printf("SX126xGetDeviceId: SX1262\r\n"); 541 | return SX1262; 542 | } 543 | #endif // NOTUSED 544 | } 545 | 546 | void SX126xAntSwOn( void ) 547 | { 548 | #if SX126X_HAS_ANT_SW 549 | GpioInit( &AntPow, RADIO_ANT_SWITCH_POWER, PIN_OUTPUT, PIN_PUSH_PULL, PIN_PULL_UP, 1 ); 550 | #endif // SX126X_HAS_ANT_SW 551 | } 552 | 553 | void SX126xAntSwOff( void ) 554 | { 555 | #if SX126X_HAS_ANT_SW 556 | GpioInit( &AntPow, RADIO_ANT_SWITCH_POWER, PIN_ANALOGIC, PIN_PUSH_PULL, PIN_NO_PULL, 0 ); 557 | #endif // SX126X_HAS_ANT_SW 558 | } 559 | 560 | bool SX126xCheckRfFrequency( uint32_t frequency ) 561 | { 562 | // Implement check. Currently all frequencies are supported 563 | return true; 564 | } 565 | 566 | uint32_t SX126xGetDio1PinState( void ) 567 | { 568 | return bl_gpio_input_get_value( SX126X_DIO1 ); 569 | } 570 | 571 | #if defined( USE_RADIO_DEBUG ) 572 | static void SX126xDbgPinTxWrite( uint8_t state ) 573 | { 574 | bl_gpio_output_set( SX126X_DBG_PIN_TX, state ); 575 | } 576 | 577 | static void SX126xDbgPinRxWrite( uint8_t state ) 578 | { 579 | bl_gpio_output_set( SX126X_DBG_PIN_RX, state ); 580 | } 581 | #endif 582 | 583 | /////////////////////////////////////////////////////////////////////////////// 584 | // GPIO Interrupt: Handle GPIO Interrupt triggered by received LoRa Packet and other conditions 585 | 586 | /// Maximum number of GPIO Pins that can be configured for interrupts 587 | #define MAX_GPIO_INTERRUPTS 6 // DIO0 to DIO5 588 | 589 | /// Array of Events for the GPIO Interrupts. 590 | /// The Events will be triggered to forward the GPIO Interrupt to the Application Task. 591 | /// gpio_events[i] corresponds to gpio_interrupts[i]. 592 | static struct ble_npl_event gpio_events[MAX_GPIO_INTERRUPTS]; 593 | 594 | /// Array of GPIO Pin Numbers that have been configured for interrupts. 595 | /// We shall lookup this array to find the GPIO Pin Number for each GPIO Interrupt Event. 596 | /// gpio_events[i] corresponds to gpio_interrupts[i]. 597 | static uint8_t gpio_interrupts[MAX_GPIO_INTERRUPTS]; 598 | 599 | static int init_interrupt_event(uint8_t gpioPin, DioIrqHandler *handler); 600 | static int enqueue_interrupt_event(uint8_t gpioPin, struct ble_npl_event *event); 601 | 602 | /// Register Handler Function for GPIO. Return 0 if successful. 603 | /// GPIO Handler Function will run in the context of the Application Task, not the Interrupt Handler. 604 | /// Based on bl_gpio_register in https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_gpio.c 605 | static int register_gpio_handler( 606 | uint8_t gpioPin, // GPIO Pin Number 607 | DioIrqHandler *handler, // GPIO Handler Function 608 | uint8_t intCtrlMod, // GPIO Interrupt Control Mode (see below) 609 | uint8_t intTrgMod, // GPIO Interrupt Trigger Mode (see below) 610 | uint8_t pullup, // 1 for pullup, 0 for no pullup 611 | uint8_t pulldown) // 1 for pulldown, 0 for no pulldown 612 | { 613 | printf("SX126X register handler: GPIO %d\r\n", (int) gpioPin); 614 | 615 | // Init the Event that will invoke the handler for the GPIO Interrupt 616 | int rc = init_interrupt_event( 617 | gpioPin, // GPIO Pin Number 618 | handler // GPIO Handler Function that will be triggered by the Event 619 | ); 620 | assert(rc == 0); 621 | 622 | // Configure pin as a GPIO Pin 623 | GLB_GPIO_Type pins[1]; 624 | pins[0] = gpioPin; 625 | BL_Err_Type rc2 = GLB_GPIO_Func_Init( 626 | GPIO_FUN_SWGPIO, // Configure as GPIO 627 | pins, // Pins to be configured 628 | sizeof(pins) / sizeof(pins[0]) // Number of pins (1) 629 | ); 630 | assert(rc2 == SUCCESS); 631 | 632 | // Configure pin as a GPIO Input Pin 633 | rc = bl_gpio_enable_input( 634 | gpioPin, // GPIO Pin Number 635 | pullup, // 1 for pullup, 0 for no pullup 636 | pulldown // 1 for pulldown, 0 for no pulldown 637 | ); 638 | assert(rc == 0); 639 | 640 | // Disable GPIO Interrupt for the pin 641 | bl_gpio_intmask(gpioPin, 1); 642 | 643 | // Configure GPIO Pin for GPIO Interrupt 644 | bl_set_gpio_intmod( 645 | gpioPin, // GPIO Pin Number 646 | intCtrlMod, // GPIO Interrupt Control Mode (see below) 647 | intTrgMod // GPIO Interrupt Trigger Mode (see below) 648 | ); 649 | 650 | // Enable GPIO Interrupt for the pin 651 | bl_gpio_intmask(gpioPin, 0); 652 | return 0; 653 | } 654 | 655 | // GPIO Interrupt Control Modes: 656 | // GLB_GPIO_INT_CONTROL_SYNC: GPIO interrupt sync mode 657 | // GLB_GPIO_INT_CONTROL_ASYNC: GPIO interrupt async mode 658 | // See hal_button_register_handler_with_dts in https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_button.c 659 | 660 | // GPIO Interrupt Trigger Modes: 661 | // GLB_GPIO_INT_TRIG_NEG_PULSE: GPIO negative edge pulse trigger 662 | // GLB_GPIO_INT_TRIG_POS_PULSE: GPIO positive edge pulse trigger 663 | // GLB_GPIO_INT_TRIG_NEG_LEVEL: GPIO negative edge level trigger (32k 3T) 664 | // GLB_GPIO_INT_TRIG_POS_LEVEL: GPIO positive edge level trigger (32k 3T) 665 | // See hal_button_register_handler_with_dts in https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/hal_button.c 666 | 667 | /// Interrupt Handler for GPIO Pin DIO1. Triggered by SX126X when LoRa Packet is received 668 | /// and for other conditions. Based on gpio_interrupt_entry in 669 | /// https://github.com/lupyuen/bl_iot_sdk/blob/master/components/hal_drv/bl602_hal/bl_gpio.c#L151-L164 670 | static void handle_gpio_interrupt(void *arg) 671 | { 672 | // Check all GPIO Interrupt Events 673 | for (int i = 0; i < MAX_GPIO_INTERRUPTS; i++) { 674 | // Get the GPIO Interrupt Event 675 | struct ble_npl_event *ev = &gpio_events[i]; 676 | 677 | // If the Event is unused, skip it 678 | if (ev->fn == NULL) { continue; } 679 | 680 | // Get the GPIO Pin Number for the Event 681 | GLB_GPIO_Type gpioPin = gpio_interrupts[i]; 682 | 683 | // Get the Interrupt Status of the GPIO Pin 684 | BL_Sts_Type status = GLB_Get_GPIO_IntStatus(gpioPin); 685 | 686 | // If the GPIO Pin has triggered an interrupt... 687 | if (status == SET) { 688 | // Forward the GPIO Interrupt to the Application Task to process 689 | enqueue_interrupt_event( 690 | gpioPin, // GPIO Pin Number 691 | ev // Event that will be enqueued for the Application Task 692 | ); 693 | } 694 | } 695 | } 696 | 697 | /// Interrupt Counters 698 | int g_dio0_counter, g_dio1_counter, g_dio2_counter, g_dio3_counter, g_dio4_counter, g_dio5_counter, g_nodio_counter; 699 | 700 | /// Enqueue the GPIO Interrupt to an Event Queue for the Application Task to process 701 | static int enqueue_interrupt_event( 702 | uint8_t gpioPin, // GPIO Pin Number 703 | struct ble_npl_event *event) // Event that will be enqueued for the Application Task 704 | { 705 | // Disable GPIO Interrupt for the pin 706 | bl_gpio_intmask(gpioPin, 1); 707 | 708 | // Note: DO NOT Clear the GPIO Interrupt Status for the pin! 709 | // This will suppress subsequent GPIO Interrupts! 710 | // bl_gpio_int_clear(gpioPin, SET); 711 | 712 | // Increment the Interrupt Counters 713 | if (SX126X_DIO1 >= 0 && gpioPin == (uint8_t) SX126X_DIO1) { g_dio1_counter++; } 714 | else { g_nodio_counter++; } 715 | 716 | // Use Event Queue to invoke Event Handler in the Application Task, 717 | // not in the Interrupt Context 718 | if (event != NULL && event->fn != NULL) { 719 | extern struct ble_npl_eventq event_queue; // TODO: Move Event Queue to header file 720 | ble_npl_eventq_put(&event_queue, event); 721 | } 722 | 723 | // Enable GPIO Interrupt for the pin 724 | bl_gpio_intmask(gpioPin, 0); 725 | return 0; 726 | } 727 | 728 | // Init the Event that will the Interrupt Handler will invoke to process the GPIO Interrupt 729 | static int init_interrupt_event( 730 | uint8_t gpioPin, // GPIO Pin Number 731 | DioIrqHandler *handler) // GPIO Handler Function 732 | { 733 | // Find an unused Event with null handler and set it 734 | for (int i = 0; i < MAX_GPIO_INTERRUPTS; i++) { 735 | struct ble_npl_event *ev = &gpio_events[i]; 736 | 737 | // If the Event is used, skip it 738 | if (ev->fn != NULL) { continue; } 739 | 740 | // Set the Event handler 741 | ble_npl_event_init( // Init the Event for... 742 | ev, // Event 743 | handler, // Event Handler Function 744 | NULL // Argument to be passed to Event Handler 745 | ); 746 | 747 | // Set the GPIO Pin Number for the Event 748 | gpio_interrupts[i] = gpioPin; 749 | return 0; 750 | } 751 | 752 | // No unused Events found, should increase MAX_GPIO_INTERRUPTS 753 | assert(false); 754 | return -1; 755 | } 756 | 757 | /////////////////////////////////////////////////////////////////////////////// 758 | // Timer Functions 759 | 760 | /// Initialise a timer 761 | void TimerInit( 762 | struct ble_npl_callout *timer, // The timer to initialize. Cannot be NULL. 763 | ble_npl_event_fn *f) // The timer callback function. Cannot be NULL. 764 | { 765 | // Implement with Callout Functions from NimBLE Porting Layer 766 | assert(timer != NULL); 767 | assert(f != NULL); 768 | 769 | // Event Queue containing Events to be processed, defined in demo.c. TODO: Move to header file. 770 | extern struct ble_npl_eventq event_queue; 771 | 772 | // Init the Callout Timer with the Callback Function 773 | ble_npl_callout_init( 774 | timer, // Callout Timer 775 | &event_queue, // Event Queue that will handle the Callout upon timeout 776 | f, // Callback Function 777 | NULL // Argument to be passed to Callback Function 778 | ); 779 | } 780 | 781 | /// Stops a timer from running. Can be called even if timer is not running. 782 | void TimerStop( 783 | struct ble_npl_callout *timer) // Pointer to timer to stop. Cannot be NULL. 784 | { 785 | // Implement with Callout Functions from NimBLE Porting Layer 786 | assert(timer != NULL); 787 | 788 | // If Callout Timer is still running... 789 | if (ble_npl_callout_is_active(timer)) { 790 | // Stop the Callout Timer 791 | ble_npl_callout_stop(timer); 792 | } 793 | } 794 | 795 | /// Sets a timer that will expire ‘millisecs’ milliseconds from the current time. 796 | void TimerStart( 797 | struct ble_npl_callout *timer, // Pointer to timer. Cannot be NULL. 798 | uint32_t millisecs) // The number of milliseconds from now at which the timer will expire. 799 | { 800 | // Implement with Callout Functions from NimBLE Porting Layer. 801 | assert(timer != NULL); 802 | 803 | // Stop the timer if running 804 | TimerStop(timer); 805 | 806 | // Convert milliseconds to ticks 807 | ble_npl_time_t ticks = ble_npl_time_ms_to_ticks32( 808 | millisecs // Duration in milliseconds 809 | ); 810 | 811 | // Wait at least 1 tick 812 | if (ticks == 0) { ticks = 1; } 813 | 814 | // Trigger the Callout Timer after the elapsed ticks 815 | ble_npl_error_t rc = ble_npl_callout_reset( 816 | timer, // Callout Timer 817 | ticks // Number of ticks 818 | ); 819 | assert(rc == 0); 820 | } 821 | 822 | /// Wait until ‘millisecs’ milliseconds has elapsed. This is a blocking delay. 823 | void DelayMs(uint32_t millisecs) // The number of milliseconds to wait. 824 | { 825 | // Implement with Timer Functions from NimBLE Porting Layer. 826 | // Convert milliseconds to ticks. 827 | ble_npl_time_t ticks = ble_npl_time_ms_to_ticks32( 828 | millisecs // Duration in milliseconds 829 | ); 830 | 831 | // Wait at least 1 tick 832 | if (ticks == 0) { ticks = 1; } 833 | 834 | // Wait for the ticks 835 | ble_npl_time_delay(ticks); 836 | } 837 | 838 | /// Return current time in microseconds 839 | uint32_t TimerGetCurrentTime(void) 840 | { 841 | // Convert ticks to milliseconds then microseconds 842 | return xTaskGetTickCount() * portTICK_PERIOD_MS * 1000; 843 | } 844 | 845 | /// Return elased time in microseconds 846 | uint32_t TimerGetElapsedTime(uint32_t saved_time) 847 | { 848 | return TimerGetCurrentTime() - saved_time; 849 | } 850 | --------------------------------------------------------------------------------