? ? 最近一直在搞3568的东西,涉及到底层的设备树修改,驱动编写等等,忙的焦头烂额的,也没时间往下面写东西了。今天差不多底层的东西快弄完了,把最近的感悟给大家分享下,并且加入点设备树的基础知识。给刚刚涉足开发板学习的小伙伴一点帮助。当然对于大神们来说,写的肯定是皮毛。一起共勉!
? ? 涉及到的知识,我会用类比的方法和STM32的硬件做对比,其实弄完之后发现两个差不多,没有什么本质的区别。所以小伙伴看之前的基础是必须编写过单片机的程序,对单片机的架构有一定的了解,要不看完会更迷茫。
? ? 学习这个真是很痛苦,并且不知道自己做的对不对,需要一遍一遍的测试。并且问了其他小伙伴,有一个共同的感觉:随着年龄的增加,学习能力大大下降。并且已经抽不出那么多精力去学习能力了。
? ? 每天刷抖音,看到什么35岁失业什么的,其实吧,感觉有三个原因吧,仅仅是自己猜测:
(1)技术没有做扎实就转到管理岗了。公司发展太快,所以刚进去的技术还没有很好的基础,就转到管理,使得自己的技术不扎实,再从管理岗转回来,已经很难了。
(2)技术也不错,但是忘记了学习,一直做自己习惯的东西,然后到了管理。但是到了管理之后,技术更新了,以前的技术已经适应不了现在的东西了。这个应该是大部分人的问题吧。就算在一个技术上做的时间长了,也会不学习了,感觉现在的东西够了。当然学习新的东西会很难、很闹心。其实我也想一直做单片机,也就控制几个灯,控制几个外设。然后搞搞逻辑啥的。一直在自己的舒适区。
(3)技术也够,学习也一直在学,但是换个工作工资低了,不愿将就。但愿每个人都是因为这个原因。
? ? 这次介绍4个内容,都是最简单的外设:(1)GPIO,(2)串口,(3)I2C,(4)CAN。这4个应该能包含大部分硬件应用了,当然还有麻烦的,后面再慢慢介绍,一点一点的来。先介绍最基本的,大概有个印象。
GPIO_InitTypeDef GPIO_Initure;
__HAL_RCC_GPIOE_CLK_ENABLE();
GPIO_Initure.Pin = GPIO_PIN_15;
GPIO_Initure.Mode = GPIO_MODE_INPUT;
GPIO_Initure.Pull = GPIO_PULLUP;
GPIO_Initure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOE, &GPIO_Initure);
? ? 这个是单片机的GPIO初始化。定义GPIO结构体,开时钟,管脚号,管脚输入,管脚模式,管脚速度,参数传入结构体。
HAL_GPIO_WritePin(GPIOE, GPIO_PIN_9, GPIO_PIN_SET);
? ? 这个是控制程序。直接根据HALk库给的函数就行。要是不用HAL库,自己控制,也都是别人给好的API函数。
对比下来,在Linux的3568设备树里:
? ? ? ? gpioled1 {
? ? ? ? ? ? ? ? compatible = "MY,led1";
? ? ? ? ? ? ? ? led1-gpio = <&gpio0 RK_PD5 GPIO_ACTIVE_HIGH>;
? ? ? ? ? ? ? ? status = "okay";
? ? ? ? };
????先说第二句:gpio的管脚定义,第三句:状态定义。
????第一句是匹配定义,这个后面再说。后面牵涉到驱动部分,这个后面会详细介绍,所以现在先由个概念,知道是这么回事。
????至此到这里,GPIO的设备树部分介绍完毕。下面说下两者的区别,仅仅说对于GPIO设定部分的区别哈,其他的就不说了,没啥用。
????相同点:定义了管脚,并且定义了初始电平;
????不同点:单片机定义了管脚的输入和输出,3568定义了匹配字。至于语法的区别,就不做过多的介绍了。
????所以,3568里面还需要对其他的进行定义,第一个:输入输出定义,第二个:控制程序。这个后面再说。先说下,输入输出定义在驱动里面定义,控制程序也是线程的API,只要拿过来使用就行了。
? ? GPIO_InitStruct.Pin = GPIO_PIN_2|GPIO_PIN_3;
? ? GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
? ? GPIO_InitStruct.Pull = GPIO_NOPULL;
? ? GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
? ? GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
? ? HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
????先说第一部分,管脚定义。这部分应该很熟悉,定义管脚,定义管脚复用,然后定义复用到什么上面,然后参数传入结构体。这部分是管脚的定义。
? huart2.Instance = USART2;
? huart2.Init.BaudRate = 1000000;
? huart2.Init.WordLength = UART_WORDLENGTH_8B;
? huart2.Init.StopBits = UART_STOPBITS_1;
? huart2.Init.Parity = UART_PARITY_NONE;
? huart2.Init.Mode = UART_MODE_TX_RX;
? HAL_UART_Init(&huart2);
????然后说第二部分,串口定义。前面定义了管脚,相当于管脚已经复用到了串口上,然后定义串口的信息。对应串口2,波特率,起始位,停止位等。
? ? HAL_NVIC_SetPriority(USART2_IRQn, 0, 0);
? ? HAL_NVIC_EnableIRQ(USART2_IRQn);
????然后就是中断函数,接收和发送中断。对应的就是接收和发送函数中断。这个API在it.c里面应该可以找到。要是找不到就直接去库函数里面找。
????对比下来,在3568的设备树里:?? ?
uart1 {
?? ??? ?/omit-if-no-ref/
?? ??? ?uart1m0_xfer: uart1m0-xfer {
?? ??? ??? ?rockchip,pins =
?? ??? ??? ??? ?/* uart1_rxm0 */
?? ??? ??? ??? ?<2 RK_PB3 2 &pcfg_pull_up>,
?? ??? ??? ??? ?/* uart1_txm0 */
?? ??? ??? ??? ?<2 RK_PB4 2 &pcfg_pull_up>;
?? ??? ?};
?? ??? ?/omit-if-no-ref/
?? ??? ?uart1m0_ctsn: uart1m0-ctsn {
?? ??? ??? ?rockchip,pins =
?? ??? ??? ??? ?/* uart1m0_ctsn */
?? ??? ??? ??? ?<2 RK_PB6 2 &pcfg_pull_none>;
?? ??? ?};
?? ??? ?/omit-if-no-ref/
?? ??? ?uart1m0_rtsn: uart1m0-rtsn {
?? ??? ??? ?rockchip,pins =
?? ??? ??? ??? ?/* uart1m0_rtsn */
?? ??? ??? ??? ?<2 RK_PB5 2 &pcfg_pull_none>;
?? ??? ?};
?? ??? ?/omit-if-no-ref/
?? ??? ?uart1m1_xfer: uart1m1-xfer {
?? ??? ??? ?rockchip,pins =
?? ??? ??? ??? ?/* uart1_rxm1 */
?? ??? ??? ??? ?<3 RK_PD7 4 &pcfg_pull_up>,
?? ??? ??? ??? ?/* uart1_txm1 */
?? ??? ??? ??? ?<3 RK_PD6 4 &pcfg_pull_up>;
?? ??? ?};
?? ??? ?/omit-if-no-ref/
?? ??? ?uart1m1_ctsn: uart1m1-ctsn {
?? ??? ??? ?rockchip,pins =
?? ??? ??? ??? ?/* uart1m1_ctsn */
?? ??? ??? ??? ?<4 RK_PC1 4 &pcfg_pull_none>;
?? ??? ?};
?? ??? ?/omit-if-no-ref/
?? ??? ?uart1m1_rtsn: uart1m1-rtsn {
?? ??? ??? ?rockchip,pins =
?? ??? ??? ??? ?/* uart1m1_rtsn */
?? ??? ??? ??? ?<4 RK_PB6 4 &pcfg_pull_none>;
?? ??? ?};
?? ?};
????这个是pinctrl里面的设备树结构。里面包含串口的各种复用管脚定义。里面m0和m1可以自己选择,当然选择方法在后面。
????对比不同点:单片机里面直接写出来用哪个管脚,用哪个需要自己去写。而3568里面全部给你写出来,然后你自己再配置。
?? ?uart1: serial@fe650000 {
?? ??? ?compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart";
?? ??? ?reg = <0x0 0xfe650000 0x0 0x100>;
?? ??? ?interrupts = <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>;
?? ??? ?clocks = <&cru SCLK_UART1>, <&cru PCLK_UART1>;
?? ??? ?clock-names = "baudclk", "apb_pclk";
?? ??? ?reg-shift = <2>;
?? ??? ?reg-io-width = <4>;
?? ??? ?dmas = <&dmac0 2>, <&dmac0 3>;
?? ??? ?pinctrl-names = "default";
?? ??? ?pinctrl-0 = <&uart1m0_xfer>;
?? ??? ?status = "disabled";
?? ?};
????然后底层的设备树,这部分不用修改,别人给写好的。至于怎么写,不用管,只知道在这里就行。
????解释下:匹配字,状态,寄存器地址。中断地址,时钟,时钟名称,寄存器偏移,寄存器宽度,DMA,管脚名称,管脚初始定义。
????其实在单片机里,中断函数已经给你配好了,而设备树里直接给你写出来了。怎么写的 ,千万别去研究,别人做好的,直接用就行,或者直接知道是啥东西就行。然后管脚选用的是m0。这个是别人的初始定义,后面可以再修改。所以这个地方就看看,知道是啥就行,至于reg啥的,千万别管。
&uart1 {
?? ?status = "okay";
?? ?pinctrl-0 = <&uart1m1_xfer>;
};
????这是后面的自己定义。前面别人给个初始的,后面用不用看自己。用的话就来个okay,不用就不用管。要是用的话,管脚不一样咋弄,那就给改改。
????然后,然后就没了。就那么多东西。
????对于GPIO中的驱动,GPIO的驱动简单,可以自己写一个。但是串口的比较麻烦,别人都写好了,不用管。配好设备树之后,linux会自己加载驱动函数。就像单片机里面的收发中断,然后放到buffer里面。其实你也不知道咋驱动的,就是可以收到数,然后解析就行。
????切记:千万别深究,千万别深究。
? ? 设备树放的比较乱,如果别人开发的不好,放的会更乱,所以选开发板的时候选大厂的。放的位置好,你就好看点,学习起来也快。至于程序放的位置,放哪都行,随你意,但是最好按照别人写好的去放,看起来方便。
????单片机里面的东西,都是一块一块的,相对来说,别人都给你封好了,3568或者其他开发板里面的程序都是按照功能一大块放一起,看起来比较麻烦。
? ? 设备树就是将单片机的不同的功能,大家放一起,然后自己挑出来。并且用很晦涩的表达方式表达出来,不用看懂具体是啥,也不用纠结怎么去编写,会改管脚就行。给的pdf里面肯定会有说明怎么弄,要是没有,引用别人的话:赶紧跑路,还搞啥,自己肯定编不了。
? ? 接触的时候脑袋疼,这是啥,这又是啥。当真的去一个一个的琢磨之后,才发现,哦,和单片机差不多。学习顺序:克服心理障碍,静下心,一句一句的看,不会的查,改,改,改,不用担心改错,反正也改不坏。还有一定要做好备份,在修改任何东西前,一定先保存一份,万一改错了还能复原,再从开始改。
????后面介绍I2C和CAN的设备树,然后再介绍GPIO的驱动函数,然后就再ETH,然后再其他的。慢慢来。喜欢的小伙伴点个关注,后续文章继续发送。