[linux] 通用的 uart & gpio & led & key 示例代碼

這是給 wiki.sipeed.com/m3axpi 準備的示例代碼。

gpio & pwm

  • linux_gpio.h

#ifndef __LINUX_GPIO_H
#define __LINUX_GPIO_H

#ifdef __cplusplus
extern "C"
{
#endif

#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

void _pwm_init(char *pin, uint64_t fre, float duty);

void _pwm_set_duty(char * pin, float duty);

void _pwm_deinit(char *pin);

/*
_pwm_init("PH8", 50000, 0.1);
sleep(1);
_pwm_set_duty("PH8", 0.6);
sleep(1);
_pwm_set_duty("PH8", 0.3);
sleep(1);
_pwm_deinit("PH8");
*/

void* _gpio_init(char* pin, int mode, int state);

void _gpio_deinit(char* pin);

void _gpio_read(char* pin, int* state);

/*
// _gpio_init("PH7", 0, 0);
// for (int i = 0; i < 30; i++)
// {
//     int val = 0;
//     _gpio_read("PH7", &val);
//     printf("%d\r\n", val);
//     sleep(1);
// }
// _gpio_deinit("PH7");
// return;
*/

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* __LINUX_GPIO_H */
  • linux_gpio.c

#include "linux_gpio.h"

int _write_file_only(char* path, char* buf, size_t count)
{
    int fd, res;

    fd = open(path, O_WRONLY | O_NONBLOCK);
    if (fd < 0)
        return -1;

    res = write(fd, buf, count);
    if (res < 0)
    {
        close(fd);
        return -2;
    }

    res = close(fd);
    if (res < 0)
        return -3;

    return res;
}

int _read_file_only(char* path, char* buf, size_t count)
{
    int fd, res;

    fd = open(path, O_RDONLY | O_NONBLOCK);
    if (fd < 0)
        return -1;

    res = read(fd, buf, count);
    if (res < 0)
    {
        close(fd);
        return -2;
    }

    res = close(fd);
    if (res < 0)
        return -3;

    return res;
}

int _get_pwm_num(char *pin)
{
    int pwm_num = 0;
    if (strlen(pin) < 3)    return -1;

    if (!strcmp(pin, "PD1") || !strcmp(pin, "PH0"))
    {
        pwm_num = 0;
    }
    else if (!strcmp(pin, "PD2") || !strcmp(pin, "PH1"))
    {
        pwm_num = 1;
    }
    else if (!strcmp(pin, "PD3") || !strcmp(pin, "PH2"))
    {
        pwm_num = 2;
    }
    else if (!strcmp(pin, "PD4") || !strcmp(pin, "PH3"))
    {
        pwm_num = 3;
    }
    else if (!strcmp(pin, "PD5") || !strcmp(pin, "PH4"))
    {
        pwm_num = 4;
    }
    else if (!strcmp(pin, "PD6") || !strcmp(pin, "PH5"))
    {
        pwm_num = 5;
    }
    else if (!strcmp(pin, "PD7") || !strcmp(pin, "PH6"))
    {
        pwm_num = 6;
    }
    else if (!strcmp(pin, "PD8") || !strcmp(pin, "PH7"))
    {
        pwm_num = 7;
    }
    else if (!strcmp(pin, "PD9") || !strcmp(pin, "PH8"))
    {
        pwm_num = 8;
    }
    else if (!strcmp(pin, "PD19") || !strcmp(pin, "PD22") || !strcmp(pin, "PH9"))
    {
        pwm_num = 9;
    }
    else
    {
        pwm_num = -1;
    }

    return pwm_num;
}

/**
 * @brief 初始化pwm
 * @details
 * @param [in] pwm_id   按鍵id(0 按鍵0,1 按鍵1)
 * @param [in] fre      頻率
 * @param [in] duty     佔空比
 * @retval
 */
void _pwm_init(char *pin, uint64_t fre, float duty)
{
    int res = 0;
    char path[100];
    char arg[20];
    int pwm_id;
    uint64_t period = 0, duty_cycle = 0;

    pwm_id = _get_pwm_num(pin);
    if (-1 == pwm_id)   return;

    snprintf(arg, sizeof(arg), "%d", pwm_id);
    res = _write_file_only("/sys/class/pwm/pwmchip0/export", arg, strlen(arg));
    if (res < 0)    {return;}

    period = 1000000000 / fre;
    snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip0/pwm%d/period", pwm_id);
    snprintf(arg, sizeof(arg), "%lld", period);
    res = _write_file_only(path, arg, strlen(arg));
    if (res < 0)    {return;}

    duty = duty > 1.0 ? 1.0 : duty;
    duty = 1.0 - duty;
    duty_cycle = period * duty;
    snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip0/pwm%d/duty_cycle", pwm_id);
    snprintf(arg, sizeof(arg), "%lld", duty_cycle);
    res = _write_file_only(path, arg, strlen(arg));
    if (res < 0)    {return;}

    snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip0/pwm%d/enable", pwm_id);
    res = _write_file_only(path, "1", 1);
    if (res < 0)    {return;}
}

void _pwm_deinit(char *pin)
{
    int res = 0;
    char path[100];
    char arg[20];
    int pwm_id;

    pwm_id = _get_pwm_num(pin);
    if (-1 == pwm_id)   return;

    snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip0/pwm%d/enable", pwm_id);
    res = _write_file_only(path, "0", 1);
    if (res < 0)    {return;}

    snprintf(arg, sizeof(arg), "%d", pwm_id);
    res = _write_file_only("/sys/class/pwm/pwmchip0/unexport", arg, strlen(arg));
    if (res < 0)    {return;}
}

void _pwm_set_duty(char * pin, float duty)
{
    int res = 0;
    char path[100];
    char arg[20];
    int pwm_id;
    uint64_t period = 0, duty_cycle = 0;

    pwm_id = _get_pwm_num(pin);
    if (-1 == pwm_id)   return;

    snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip0/pwm%d/period", pwm_id);
    res = _read_file_only(path, arg, sizeof(arg));
    if (res < 0)    {return;}
    period = atoi(arg);

    duty = 1.0 - duty;
    duty_cycle = period * duty;
    snprintf(arg, sizeof(arg), "%lld", duty_cycle);
    snprintf(path, sizeof(path), "/sys/class/pwm/pwmchip0/pwm%d/duty_cycle", pwm_id);
    res = _write_file_only(path, arg, strlen(arg));
    if (res < 0)    {return;}
}

/**
 * @brief 將字符引腳編號轉換爲數字引腳編號
 * @param [in] pin   字符引腳編號,格式必須爲PAxx~PHxx,且xx的範圍爲0~31
 * @retval 返回數字引腳編號,如果爲-1,則執行失敗
 */
int _get_gpio_num(char* pin)
{
	if (strlen(pin) < 3)
		return -1;
	char c;
	int group_id, group_offset, gpio_num;

	c = pin[0];
	if (c != 'p' && c != 'P')
	{
		return -1;
	}

	c = pin[1];
	if ('a' <= c && c <= 'z')			// 限制a~z
	{
		group_id = c - 'a';
	}
	else if ('A' <= c && c <= 'Z')	    // 限制A~Z
	{
		group_id = c - 'A';
	}
	else
	{
		return -1;
	}

	group_offset = atoi(pin + 2);
	if (group_offset > 31)
	{
		return -1;
	}

	gpio_num = (group_id << 5) + group_offset;
	return gpio_num;
}

/**
 * @brief 向export文件註冊gpio
 * @param [in] gpio   引腳編號
 * @retval 返回0,成功,小於0,失敗
 */
int gpio_export(uint32_t gpio)
{
    int len, res;
    char buf[10];

    len = snprintf(buf, sizeof(buf), "%d", gpio);
	if (len > sizeof(buf))
		return -1;

    res = _write_file_only("/sys/class/gpio" "/export", buf, len);
    if (res < 0)
	{
		perror("gpio_export");
	}

    return res;
}

/**
 * @brief 向unexport文件取消註冊gpio
 * @param [in] gpio   引腳編號
 * @retval 返回0,成功,小於0,失敗
 */
int gpio_unexport(uint32_t gpio)
{
    int len, res;
    char buf[10];

    len = snprintf(buf, sizeof(buf), "%d", gpio);
	if (len > sizeof(buf))
		return -1;

    res = _write_file_only("/sys/class/gpio" "/unexport", buf, len);
    if (res < 0)
	{
		perror("gpio unexport");
	}

    return res;
}

/**
 * @brief 設置gpio輸入/輸出模式
 * @param [in] gpio   引腳編號
 * @param [in] mode   引腳模式,取值如下:
 *  				0,輸入模式
 *  				1,輸出模式,默認低電平
 *  				2,輸出模式,默認低電平
 *  				3,輸出模式,默認高電平
 * @retval 返回0,成功,小於0,失敗
 */
int gpio_set_dir(uint32_t gpio, uint32_t mode)
{
    int len, res;
    char path[40];

    len = snprintf(path, sizeof(path), "/sys/class/gpio"  "/gpio%d/direction", gpio);
	if (len > sizeof(path))
		return -1;

    mode = mode > 3 ? 3 : mode;
    switch(mode)
    {
        case 0:res = _write_file_only(path, "in", sizeof("in"));break;
        case 1:res = _write_file_only(path, "out", sizeof("out"));break;
        case 2:res = _write_file_only(path, "low", sizeof("low"));break;
        case 3:res = _write_file_only(path, "high", sizeof("high"));break;
        default:break;
    }

    return res;
}

/**
 * @brief 設置gpio電平
 * @param [in] gpio   引腳編號
 * @param [in] value  引腳電平(1,高電平;0,低電平)
 * @retval 返回0,成功,小於0,失敗
 */
int gpio_set_value(uint32_t gpio, uint32_t value)
{
    int len, res;
    char path[40];

    len = snprintf(path, sizeof(path), "/sys/class/gpio"  "/gpio%d/value", gpio);
	if (len > sizeof(path))
		return -1;

	if (value)
		res = _write_file_only(path, "1", 2);
	else
		res = _write_file_only(path, "0", 2);

    return res;
}

/**
 * @brief 讀取gpio電平
 * @param [in] 	gpio   引腳編號
 * @param [out] value  引腳電平(1,高電平;0,低電平)
 * @retval 返回0,成功,小於0,失敗
 */
int gpio_get_value(uint32_t gpio, uint32_t *value)
{
    int len, res;
    char path[40], state;

    len = snprintf(path, sizeof(path), "/sys/class/gpio"  "/gpio%d/value", gpio);
	if (len > sizeof(path))
		return -1;

	res = _read_file_only(path, &state, 1);
	if (res < 0)
		return res;

	*value = state == '0' ? 0 : 1;

    return res;
}

/**
 * @brief 初始化gpio
 * @note 還沒有限制io_num的範圍,需要注意
 * @param [in] dev      設備名
 * @param [in] mode     模式
 * @param [in] state    初始狀態值
 * @retval 0 成功 <0 失敗
 */
void* _gpio_init(char* pin, int mode, int state)
{
    int res = -1;

	int io_num = _get_gpio_num(pin);
	if (io_num < 0)
	{
		return (void *)res;
	}

    /* 向export文件註冊一個gpio */
    res = gpio_export(io_num);
	if (res < 0)    return (void *)res;

	/* 設置gpio方向 */
	res = gpio_set_dir(io_num, mode);
	if (res < 0)    return (void *)res;

	if (mode > 0)
	{
		/* 設置gpio電平 */
		res = gpio_set_value(io_num, state);
		if (res < 0)    return (void *)res;

	}


    return (void*)res;
}

/**
 * @brief 初始化gpio
 * @note 還沒有限制io_num的範圍,需要注意
 * @param [in] handle      設備名
 * @retval
 */
void _gpio_deinit(char* pin)
{
    int res;

	int io_num = _get_gpio_num(pin);
	if (io_num < 0) return;
	res = gpio_unexport(io_num);
	if (res < 0) return;
}

/**
 * @brief 讀gpio電平,只能在輸入模式下調用
 * @note
 * @param [in]  handle  句柄,用來傳入文件描述符
 * @param [out] state 狀態值
 * @retval
 */
void _gpio_read(char* pin, int* state)
{
	int io_num = _get_gpio_num(pin);
	if (io_num < 0) return;

	gpio_get_value(io_num, (uint32_t *)state);

}

/**
 * @brief 寫gpio電平,只能在輸出模式下調用
 * @note
 * @param [in] handle  句柄,用來傳入文件描述符
 * @param [in] state   狀態值
 * @retval
 */
void _gpio_write(char* pin, int state)
{
	int io_num = _get_gpio_num(pin);
	if (io_num < 0) return;

	gpio_set_value(io_num, state);
}

linux uart ttySX

  • linux_uart.h
#ifndef __LINUX_UART_H
#define __LINUX_UART_H

#ifdef __cplusplus
extern "C"
{
#endif

#include "stdint.h"

#define PRINF_HEX_ARR(str,buf,len)\
do{\
    char *buff = (char *)buf;\
    printf("\e[32m[%s](%d):\e[0m", str, len);\
    for (int i = 0;i < len; ++i)\
    {\
        printf("0x%.2X ", buff[i] & 0xff);\
    }\
    printf("\r\n");\
} while (0);

typedef struct{
	int baud;
	int data_bits;
	int stop_bits;
	char parity;
}uart_t;

int linux_uart_init(char* dev, void* param);
void linux_uart_deinit(int fd);
int linux_uart_read(int fd, int cnt, uint8_t* buf);
int linux_uart_write(int fd, int cnt, uint8_t* buf);

#ifdef __cplusplus
} /* extern "C" */
#endif

#endif /* __LINUX_UART_H */
  • linux_uart.c
#include "linux_uart.h"

#include <stdio.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <linux/serial.h>


static int _get_baud(int baud)
{
    switch (baud)
    {
    case 9600:return B9600;
    case 19200:return B19200;
    case 38400:return B38400;
    case 57600:return B57600;
    case 115200:return B115200;
    case 230400:return B230400;
    case 460800:return B460800;
    case 500000:return B500000;
    case 576000:return B576000;
    case 921600:return B921600;
#ifdef B1000000
    case 1000000:return B1000000;
#endif
#ifdef B1152000
    case 1152000:return B1152000;
#endif
#ifdef B1500000
    case 1500000:return B1500000;
#endif
#ifdef B2000000
    case 2000000:return B2000000;
#endif
#ifdef B2500000
    case 2500000:return B2500000;
#endif
#ifdef B3000000
    case 3000000:return B3000000;
#endif
#ifdef B3500000
    case 3500000:return B3500000;
#endif
#ifdef B4000000
    case 4000000:return B4000000;
#endif
    default:return -1;
    }
}


static void clear_custom_speed_flag(int _fd)
{
    struct serial_struct ss;
    if (ioctl(_fd, TIOCGSERIAL, &ss) < 0) {
        // return silently as some devices do not support TIOCGSERIAL
        return;
    }

    if ((ss.flags & ASYNC_SPD_MASK) != ASYNC_SPD_CUST)
        return;

    ss.flags &= ~ASYNC_SPD_MASK;

    if (ioctl(_fd, TIOCSSERIAL, &ss) < 0) {
        perror("TIOCSSERIAL failed");
        exit(1);
    }
}

/**
 * @brief 初始化uart
 * @note
 * @param [in] dev    設備名
 * @param [in] param  參數
 * @retval
 */
int linux_uart_init(char* dev, void* param)
{
    int fd;

    uart_t* cfg = (uart_t *)param;

    int baud = _get_baud(cfg->baud);
    int data_bits = cfg->data_bits, stop_bits = cfg->stop_bits;
    char parity = cfg->parity;

    fd = open(dev, O_RDWR | O_NONBLOCK | O_NOCTTY);
    // fd = open(dev, O_RDWR | O_NOCTTY);
    if (fd < 0)
    {
        return fd;
    }

    struct termios opt;
    memset(&opt, 0, sizeof(opt));

    /* 忽略modem,使能讀模式 */
    opt.c_cflag |= CLOCAL | CREAD;

    /* 設置波特率 */
    opt.c_cflag |= baud;

    /* 設置數據位 */
    switch (data_bits)
    {
    case 7:
        opt.c_cflag |= CS7;
        break;
    case 8:
        opt.c_cflag |= CS8;
        break;
    default:break;
    }

    /* 設置奇偶校驗位 */
    switch (parity)
    {
    case 'N':
    case 'n':
        opt.c_iflag &= ~INPCK;
        opt.c_cflag &= ~PARENB;
        break;
    case 'O':
    case 'o':
        opt.c_iflag |= (INPCK | ISTRIP);
        opt.c_cflag |= (PARODD | PARENB);
        break;
    case 'E':
    case 'e':
        opt.c_iflag |= (INPCK | ISTRIP);
        opt.c_cflag |= PARENB;
        opt.c_cflag &= ~PARODD;
        break;
    default:break;
    }

    /* 設置停止位 */
    switch (stop_bits)
    {
    case 1:
        opt.c_cflag &= ~CSTOPB;
        break;
    case 2:
        opt.c_cflag |= CSTOPB;
        break;
    default:break;
    }

    /* 設置流控制 */
    opt.c_cflag &= ~CRTSCTS;

    /* 最小字節數與等待時間 */
    opt.c_cc[VMIN] = 1;
    opt.c_cc[VTIME] = 0;

    /* 刷新串口,更新配置 */
    tcflush(fd, TCIOFLUSH);
    tcsetattr(fd, TCSANOW, &opt);

    clear_custom_speed_flag(fd);

    return fd;
}

void linux_uart_deinit(int fd)
{
    int res;

    res = close(fd);
    if (res < 0)
        fprintf(stderr, "uart close fd(%d) err:%s\n", fd, strerror(errno));
    else
        fd = -1;
}

linux key & led

  • key_led.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <signal.h>
#include <errno.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/prctl.h>

#include <fcntl.h>
#include <sys/poll.h>

#include <time.h>
char set_key_led_irq_in(unsigned int gpio_num, unsigned int led_num) // user key num 85 led0 68 led1 69
{
    FILE *fp;
    char file_name[50];
    unsigned int led = led_num + 68;
//key
    sprintf(file_name, "/sys/class/gpio/export");
    fp = fopen(file_name, "w");
    if (fp == NULL)
    {
        printf("Cannot open %s.\n", file_name);
        return -1;
    }
    fprintf(fp, "%d", gpio_num);
    fclose(fp);
//led
    sprintf(file_name, "/sys/class/gpio/export");
    fp = fopen(file_name, "w");
    if (fp == NULL)
    {
        printf("Cannot open %s.\n", file_name);
        return -1;
    }
    fprintf(fp, "%d", led);
    fclose(fp);
//set key in
    sprintf(file_name, "/sys/class/gpio/gpio%d/direction", gpio_num);
    fp = fopen(file_name, "rb+");
    if (fp == NULL)
    {
        printf("Cannot open %s.\n", file_name);
        return -1;
    }
    fprintf(fp, "in");
    fclose(fp);

//set key edge
    sprintf(file_name, "/sys/class/gpio/gpio%d/edge", gpio_num);
    fp = fopen(file_name, "rb+");
    if (fp == NULL)
    {
        printf("Cannot open %s.\n", file_name);
        return -1;
    }
    fprintf(fp, "falling");
    fclose(fp);

//set led out
    sprintf(file_name, "/sys/class/gpio/gpio%d/direction", led);
    fp = fopen(file_name, "rb+");
    if (fp == NULL)
    {
        printf("Cannot open %s.\n", file_name);
        return -1;
    }
    fprintf(fp, "out");
    fclose(fp);

//set led 0
    sprintf(file_name, "/sys/class/gpio/gpio%d/value", led);
    fp = fopen(file_name, "rb+");
    if (fp == NULL)
    {
        printf("Cannot open %s.\n", file_name);
        return -1;
    }
    fprintf(fp, "0");
    fclose(fp);
}

char set_led_status(unsigned int led_num, bool led_status) // user key num 85 led0 68 led1 69
{
    FILE *fp;
    char file_name[50];
    unsigned int led = led_num + 68;

    sprintf(file_name, "/sys/class/gpio/gpio%d/value", led);
    fp = fopen(file_name, "rb+");
    if (fp == NULL)
    {
        printf("Cannot open %s.\n", file_name);
        return -1;
    }
    if (led_status)
        fprintf(fp, "1");
    else
        fprintf(fp, "0");
    fclose(fp);
}

char unset_key_led_irq(unsigned int gpio_num, unsigned int led_num)
{
    FILE *fp;
    char file_name[50];
    unsigned char buf[10];
    unsigned int led = led_num + 68;

    sprintf(file_name, "/sys/class/gpio/unexport");
    fp = fopen(file_name, "w");
    if (fp == NULL)
    {
        printf("Cannot open %s.\n", file_name);
        return -1;
    }
    fprintf(fp, "%d", gpio_num);
    fclose(fp);

    sprintf(file_name, "/sys/class/gpio/unexport");
    fp = fopen(file_name, "w");
    if (fp == NULL)
    {
        printf("Cannot open %s.\n", file_name);
        return -1;
    }
    fprintf(fp, "%d", led);
    fclose(fp);
}

int main(int argc, char *argv[])
{
    AX_S32 s32Ret = -1;
    int val;
    char buff[10];
    int gpio_fd = -1;
    bool rectrl = false;
    bool blink = false;
    struct pollfd fds;

    set_key_led_irq_in(85, 0);

    gpio_fd = open("/sys/class/gpio/gpio85/value", O_RDONLY);
    if (gpio_fd == -1)
        printf("gpio open err! \n");

    fds.fd = gpio_fd;
    fds.events = POLLPRI;

    s32Ret = read(gpio_fd, buff, 10);
    if (s32Ret == -1)
        printf("read gpio err! \n");

    printf("hello start!\r\n");
    while (1)
    {
        //讀取按鍵中斷
        s32Ret = poll(&fds, 1, 0);
        if (fds.revents & POLLPRI)
        {
            s32Ret = lseek(gpio_fd, 0, SEEK_SET);
            if (s32Ret == -1)
                printf("lseek err! \n");
            s32Ret = read(gpio_fd, buff, 10);
            if (s32Ret == -1)
                printf("read gpio err! \n");
            if (!rectrl)
            {
                rectrl = true;
            }
            else
            {
                rectrl = false;
                set_led_status(0, 0);
            }
            printf("get interrupt\n");
        }

        if(rectrl)
        {
            if (!blink)
            {
                blink = true;
                set_led_status(0, blink);
            }
            else
            {
                blink = false;
                set_led_status(0, blink);
            }
        }
        usleep(500000);
    }
    unset_key_led_irq(85, 0);
    printf("end!\r\n");
    exit(0);
}

linux i2c

https://wiki.sipeed.com/soft/maixpy3/zh/usage/hardware/I2C.html

https://zhuanlan.zhihu.com/p/575318033

linux spi

https://wiki.sipeed.com/soft/maixpy3/zh/usage/hardware/SPI.html

https://www.cnblogs.com/juwan/p/14341406.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章