以前常听说码农需要有严密的逻辑思维,以前不明白。没有思维框架,真的很难写代码,不能瞎蒙,等我的只是效率低下
思路
首先我们要模拟串口通信,就要了解通信的必须条件,包括数据位及其他标志位,以及他的时序,有点像模拟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);//开启定时器API
io_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 1
main()
{
#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);//发送数据API
hr_delay(time_ - 5);
data >>= 1;
}
stop_function();
}
hr_delay(2000000);
}
}
这里要说明的是,我将开始位和一定的延时组合到一起,停止位亦如此,数据位后加延时,共同组成整个时序。写好之后数据一般都不对,我们可以用串口软件接收,看收到的数据,因为程序运行需要时间,所以一般都不是104us,我调出来是100us,还得根据自身的情况更改,代码框架是ok的。
串口接收(依旧9600)
思路:开启定时中断,进入中断后关闭中断,在中断函数内进行高低电平判断,将这些bit存储最后转换成需要的数据,也就是说,一个中断函数,完成一个字节的接收!
定时器初始化及中断
static hi_void hrtimer_callback(hi_u32 data)
{
block_or_go_on = 0;
}
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中断初始化及中断函数:看注释
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;//中断标志位置1
hi_gpio_init();//开启中断
}
}
}
这里我只是给出一个代码框架,细节问题还需要自己去调整,不能做到都copy就ok,为什么这次不在中断里做处理,因为我用的这个IC的GPIO和定时器中断的优先级是相同的,且无法修改。
以上的正确接收的time时间我是试的,就只会在104的周围,还看你的代码的简化程度!