我将为您重新表述这段内容,以符合您的要求,同时保持原有的风格和语气。
好的,让我们来重新阐述一下这个示例,以帮助您更好地理解接口、io.Writer接口以及其他相关概念。
在本文的第一部分结束时,我们提到了接口的内容。为了更好地理解,我们将通过一个简单的实例来解释如何定义和使用接口,以及如何利用无处不在的io.Writer接口。除此之外,我们还会探讨一些关于反射和半主机的内容。
当我们阅读前面的代码示例时,可能会注意到一种打开或关闭LED的反直觉方式。Set方法用于关闭LED,而Clear方法用于打开LED。这种情况是由于在漏极开路配置下驱动了LED。为了使代码更加清晰,我们可以使用On和Off方法来定义LED类型。
现在,我们可以简单地调用led.On()和led.Off(),这样就不会再引起任何疑惑了。
在我们的示例中,我们尝试使用相同的漏极开路配置来避免代码复杂化。对于某些LED,例如第三个LED,我们可能会选择将其连接到GND和PA3引脚之间,并将PA3配置为推挽模式。为此,我们将定义另一个类型:PushPullLED。
重要的是要注意,这两种类型都具有相同的方法,它们的工作方式也相同。如果我们的代码可以在不关心当前使用的是哪种类型的情况下,在LED上运行并使用这两种类型,那就太好了。这就是接口的用处。
我们定义了LED接口,它有两个方法:On和Off。PushPullLED和OpenDrainLED类型代表了两种驱动LED的方式。我们还定义了两个用作构造函数的MakeLED函数。这两种类型都实现了LED接口,因此可以将这些类型的值赋给LED类型的变量。
在这种情况下,可赋值性在编译时进行检查。赋值后,led1变量包含一个OpenDrainLED对象,以及一个指向OpenDrainLED类型方法的指针。调用led1.On()方法大致对应于下面的C代码。
如你所见,从函数调用的开销角度来看,这是一个相当廉价的抽象。
对于任何包含在接的赋值,都会包含大量关于已赋值类型的信息。如果我们不使用反射,我们可以通过避免包含类型和结构字段的名称来节省一些字节。
生成的二进制文件仍然包含有关类型的必要信息和所有导出方法的完整信息。在运行时,主要是当你将存储在接口变量中的一个值赋值给任何其他变量时,需要这些信息来检查可赋值性。
让我们加载这个程序,看看它是否按预期工作。这一次我们将使用st-flash命令来加载程序。
请注意,st-flash与此板配合使用可能有点不可靠(通常需要重置ST-LINK加密狗)。当前版本不会通过SWD发出复位命令(仅使用NRST信号)。虽然软件复位是不可行的,但它通常是有效的。对于板级程序员来说,OpenOCD的表现可能会更好。
UART(通用异步收发传输器)仍然是当今微控制器最重要的外设之一。它的优点是以下属性的独特组合......(以下省略UART相关内容)
当我们想要从程序中打印文本消息时,我们可以使用usart.Driver这个高效的驱动程序,它使用了DMA和中断来减轻CPU的负担。要将usart.Driver配置为输出模式,我们只需要将其Tx信号连接到正确的GPIO引脚即可。
我们使用其WriteString方法来打印名言。让我们清理所有内容并编译该程序。为了查看输出内容,你需要在PC上使用UART外设。请确保不要使用RS232端口或U转RS232转换器,因为STM32系列使用3.3V逻辑,而RS232可能会产生-15V ~ +15V的电压,这可能会损坏你的MCU。你需要使用3.3V逻辑的U转UART转换器。
io.Writer接口可能是Go中第二种最常用的接口类型,仅次于error接口。其定义如下所示:usart.Driver实现了io.Writer接口......(以下省略相关内容)
通过上述方式,我们可以看到io.WriteString如何导致二进制文件的大小显著增加。通过分析数据,我们可以找到引起空间增大的具体原因和符号。即使我们不使用某些方法,它们也被编译进去了,这是因为接口需要完整的方法集。对于使用大多数方法的大型程序来说这并不是问题,但对于我们这种极简的情况而言这是一个巨大的负担。
我们已经接近MCU的极限,但让我们尝试打印一些数字(你需要在import部分中用strconv替换io包)。与使用io.WriteString函数的情况一样,strconv.WriteInt的第一个参数的类型为io.Writer。
为了进一步精简类型信息并减少空间占用,我们可以采取一些措施......(以下省略相关内容)
通过上述方式,我们可以将Emgo与Go进行类比,展示其如何像Unix流一样工作。在Unix中,我们可以轻松地