GPIO口模拟串口发送接收(基于H861)
以前常聽說碼農需要有嚴密的邏輯思維,以前不明白。沒有思維框架,真的很難寫代碼,不能瞎蒙,等我的只是效率低下
思路
首先我們要模擬串口通信,就要了解通信的必須條件,包括數據位及其他標志位,以及他的時序,有點像模擬IIC。
如圖是數據的格式,數據位為八位,不是七位,當然也是有數據位為七位的,在實際的傳輸過程中,我選擇起始位+數據為+停止位即可,奇偶校驗位和空閑位不要,光是知道要傳的數據,對方卻不知道什么時候來接收此數據,所以我們現在需要一個協議(uart串口協議)來將數據放到這個協議上,這樣就可以使得通信的上位機或者下位機能夠識別傳輸的數據了,那還需要什么呢??
我們都知道我們在使用串口軟件的時候都有設置,(只是舉個例子,串口軟件太多了)如圖:
波特率、數據位、校驗位、停止位都是可以更改的。
波特率概念:波特率表示每秒鐘傳送的碼元符號的個數,它是對符號傳輸速率的一種度量,它用單位時間內載波調制狀態改變的次數來表示,1波特即指每秒傳輸1個符號
一般的,我們采用模擬的形式來通信一般都在9600及以下,再高的話,精度無法保證。
在此,這里采用模擬波特率為9600的來做通信(正常來說波特率越低,難度越小)
9600的意思就是每一秒鐘發送9600個bit(其中包括非數據位),注意這里不是字節(就不能當作9600/8=1200byte),很多沒做過這個實驗的小伙伴是不清楚這個概念的。理想情況下,按照這個傳輸波特率,發送每一位的時間間隔為104us,
要進行通信,就有發送與接收,發送相對接收來說要簡單一點。
串口發送
發送中最重要的就是時間的控制,因為是微妙級的,需要很精確,我采用的是定時器計時。
定時器初始化:這里我們直接使用API即可(其中的hi_u32就是一個32位的數據定義),如果需要適配自己的IC,按照創建定時器創建即可
void io_hrtimer_init(hi_u32 *g_hrtimer) {hi_u32 ret;/* create timer handle */ret = hi_hrtimer_create(g_hrtimer);//創建定時器if (ret != HI_ERR_SUCCESS){printf("=====ERROR===== hrtimer handle create ret is: %d !!\r\n", ret);return;}printf("----- hrtimer handle create success -----\n"); }GPIO初始化:每一個API都有注釋說明
void io_uart_tx_init(void) {hi_u32 ret;ret = hi_gpio_init(); //GPIO初始化if (ret != HI_ERR_SUCCESS){printf("===== ERROR ===== gpio -> hi_gpio_init ret:%d\r\n", ret);return;}printf("----- gpio init success-----\r\n");ret = hi_io_set_func(HI_IO_NAME_GPIO_14, HI_IO_FUNC_GPIO_14_GPIO);//開啟GPIO復用if (ret != HI_ERR_SUCCESS){printf("===== ERROR ===== gpio -> hi_io_set_func ret:%d\r\n", ret);return;}printf("----- io set func success-----\r\n");ret = hi_gpio_set_dir(HI_GPIO_IDX_14, HI_GPIO_DIR_OUT);//設置管腳方向if (ret != HI_ERR_SUCCESS){printf("===== ERROR ===== gpio -> hi_gpio_set_dir1 ret:%d\r\n", ret);return;}printf("----- gpio set dir success! -----\r\n");hi_gpio_set_ouput_val(HI_GPIO_IDX_14, HI_GPIO_VALUE1); //初始化輸出高電平 }定時器延時函數及回調中斷:
static hi_void hrtimer_callback(hi_u32 data) {block_or_go_on = 0; } void hr_delay(hi_u32 time)int ret;ret = hi_hrtimer_start(rx_timer, time, hrtimer_callback, go_on);if (ret != 0){printf("-----hi timer faild-----\r\n");}printf("block_or_go_on:%d\r\n", block_or_go_on);while (block_or_go_on);block_or_go_on = 1; }停止函數
void stop_function(void) {hi_hrtimer_start(g_hrtim, (time_), hrtimer_callback, go_on);//開啟定時器APIio_uart_tx(1);while (!block_or_go_on);block_or_go_on = 1; }開始函數
void start_function(void) {hi_hrtimer_start(g_hrtim, (time_), hrtimer_callback, go_on);//開啟定時器API io_uart_tx(0);while (!block_or_go_on);block_or_go_on = 1; }主函數:
#define time_ (100) #define block 0 #define go_on 1main() { #define time_ (100) static volatile char block_or_go_on = 1; int ret = 0;int i = 0;char data;hi_u32 g_hrtim;char b[] = {0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55, 0x55};io_uart_tx_init();io_hrtimer_init(&g_hrtim);for (;;){ printf("---has enter TX----\r\n");for (ret = 0; ret < 100; ret++)//發送100個數據,查看是否有誤{data = b[ret];start_function();for (i = 0; i < 8; i++){hi_gpio_set_ouput_val(HI_GPIO_IDX_14, data & 0x01);//發送數據APIhr_delay(time_ - 5);data >>= 1;}stop_function();}hr_delay(2000000);}}這里要說明的是,我將開始位和一定的延時組合到一起,停止位亦如此,數據位后加延時,共同組成整個時序。寫好之后數據一般都不對,我們可以用串口軟件接收,看收到的數據,因為程序運行需要時間,所以一般都不是104us,我調出來是100us,還得根據自身的情況更改,代碼框架是ok的。
串口接收(依舊9600)
思路:開啟定時中斷,進入中斷后關閉中斷,在中斷函數內進行高低電平判斷,將這些bit存儲最后轉換成需要的數據,也就是說,一個中斷函數,完成一個字節的接收!
定時器初始化及中斷
GPIO中斷初始化及中斷函數:看注釋
static volatile hi_u32 irq_flag=0; static void irq_function(hi_void *arg) {hi_gpio_deinit();irq_flag = 1;printf("-----has enter irq_function-----\r\n"); } void gpio_rx_irq_init(void) {hi_u32 ret;ret = hi_gpio_init(); //注意,該接口不支持重復調用,只能在初始化階段調用一次//irq_flag = ret;if (ret != HI_ERR_SUCCESS){printf("===== ERROR ===== gpio -> hi_gpio_init ret:%d\r\n", ret);return;}printf("----- gpio init success-----\r\n");ret = hi_gpio_set_dir(HI_GPIO_IDX_5, HI_GPIO_DIR_IN);//方向if (ret != HI_ERR_SUCCESS){printf("===== ERROR ===== gpio -> hi_gpio_set_dir ret:%d\r\n", ret);return;}printf("----- dir init success-----\r\n");ret = hi_io_set_func(HI_GPIO_IDX_5, HI_IO_FUNC_GPIO_5_GPIO);//復用if (ret != HI_ERR_SUCCESS){printf("===== ERROR ===== gpio -> hi_io_set_func ret:%d\r\n", ret);return;}printf("----- io set func success-----\r\n");ret = hi_gpio_register_isr_function(HI_GPIO_IDX_5, HI_INT_TYPE_EDGE, HI_GPIO_EDGE_FALL_LEVEL_LOW, irq_function, 0); //開啟中斷功能if (ret != HI_ERR_SUCCESS){printf("===== ERROR ===== gpio -> hi_gpio_register_isr_function ret:%d\r\n", ret);return;}printf("----- func init success-----\r\n"); }主函數
#define _time_ (100) #define BIT(x) (0x01<<(x)) #define block 0 #define go_on 1 main() {hi_u32 rx_timer;io_hrtimer_init(&rx_timer); //定時器初始化gpio_rx_irq_init();for (;;){if (irq_flag == 1){int ret = 0;int electric_level[3] = {0};int bit_value;char data = 0;for (ret = 0; ret < 10; ret++)//數據位+停止位+開始位=10{hi_hrtimer_start(rx_timer, (_time_ - 8), hrtimer_callback, go_on);//進行了時間數據的微調while (block_or_go_on);block_or_go_on = 1;hi_gpio_get_input_val(HI_GPIO_IDX_5, &bit_value);if (ret == 8) //第九位停止位為1{printf("4\r\n");break;}if (bit_value == 1){data |= BIT(ret);printf("1\r\n");}else{printf("0\r\n");}block_or_go_on = 1;//置1}printf("%02X ", data);irq_flag = 0;//中斷標志位置1hi_gpio_init();//開啟中斷}} }這里我只是給出一個代碼框架,細節問題還需要自己去調整,不能做到都copy就ok,為什么這次不在中斷里做處理,因為我用的這個IC的GPIO和定時器中斷的優先級是相同的,且無法修改。
以上的正確接收的time時間我是試的,就只會在104的周圍,還看你的代碼的簡化程度!
總結
以上是生活随笔為你收集整理的GPIO口模拟串口发送接收(基于H861)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: VB6总结之添加附件(jgp等)
- 下一篇: Yhen手把手带你使用百度智能云②---