任务与模块概述
选择的任务是:是嵌入式微控制器MCU(1) - 用于简易示波器/频谱仪/信号发生器的核心板。
根据任务要求,选择microchip的PIC16F18146,引脚数量为20Pin的,芯片引脚排序如下:
对外引出了所有的引脚和功能,核心板的框图如下:
设计思路和选型详解
通过仔细分析任务所需要的硬件资源,需要保证核心板上的资源以及管脚能够支持以下所有外设功能:
- 2个按键 + 1个光电旋转编码器用于控制输入
- 1个SPI接口的OLED显示屏(128*128分辨率)
- 1路音频放大电路用于产生ADC的测试信号,并可作为测试电路使用
- 一个蜂鸣器用于音效输出
- 1路基于PWM的DDS信号输出,用于产生测试信号(任意波形)
- 2路增益可调的模拟信号输入,通过12bits ADC采集2mVpp - 30Vpp,带宽为100KHz的模拟信号
刚体验过Funpack4-2的microchip的芯片,觉得PIC芯片功能还挺强大的。所以选择了PIC16F18146,其可以作为17通道的12位ADC、2个8bit的ADC、四个16位PWM接口和17个GPIO,完全可以满足上述的硬件资源要求,并且引脚基本上刚好使用完。
选择个芯片还有一个重要的原因,下面这些常用的功能如SPI、IIC、UART、PWM、ADC等等,几乎所有的管教都可以复用!!!这在使用的时候就会非常的方便,在硬件考虑功能的时候也能更好的自定义功能引脚,可以让布线更加美观方便
相较于同样20脚的STM32,在价格方面上便宜了4.13RMB。
原理图和PCB
这是引脚为20的一个芯片将所有IO全部引进,形成一个最小系统板。其本身并无具体的功能,但是可以方便的用于连接其余的外围设备(扩展板)就可以快速的组件成想要实现的任何功能。
下面是所需要的BOM清单,只需10个元器件即可制作出这个功能强大的核心板!
位号 | 型号 | 封装 |
---|---|---|
C2,C3 | 100nF | C0805 |
R1,R2 | 10K | R0805 |
U3 | PIC16F18146 | SSOP-20_L7.2-W5.3-P0.65-LS7.8-BL |
H1 | HDR-TH_5P-P2.54-V-M | HDR-TH_5P-P2.54-V-M |
H4,H5 | HDR-TH_10P-P2.54-V-F | HDR-TH_10P-P2.54-V-F |
LED1 | RLED0805R | LED_0805 |
USB1 | USB-SMD_KH-TYPE-C-2P | USB-SMD_KH-TYPE-C-2P |
调试软件和代码
查找12指神探的传感器扩展板上的IIC设备
uint8_t I2C_ProbeAll(const i2c_host_interface_t* i2cHost, uint8_t *deviceAddresses)
{
uint8_t txData = 0;
uint16_t txLength = 0;
uint8_t devicesFound = 0;
for (uint8_t probeAddress = 0U; probeAddress < 0x80U; probeAddress++)
{
i2cHost->Write((uint16_t)probeAddress, &txData, txLength);
// Wait for I2C module to be ready
while (i2cHost->IsBusy())
{
}
i2c_host_error_t errorState = i2cHost->ErrorGet(); // Update errorState with the result of ErrorGet()
if(errorState == I2C_ERROR_NONE)
{
deviceAddresses[devicesFound] = probeAddress;
devicesFound++;
(int) printf("Address: 0x%02X I2C_ERROR_NONE \r\n", probeAddress);
}
else
{
// Handle the error appropriately
}
}
return devicesFound;
}
void I2C_FindAndReportDevices(void)
{
uint8_t devicesFound = 0;
uint8_t deviceAddresses[64]; // Note: Adjust the size of this array if memory constraints arise.
// This array only needs to store the addresses of successfully detected devices.
devicesFound = I2C_ProbeAll(&I2C_Host, deviceAddresses);
(int) printf("\r\nDevices found = %u \r\n", devicesFound);
for (uint8_t i = 0; i < devicesFound; i++)
{
(int) printf("0x%02X ", deviceAddresses[i]);
}
(int) printf("\r\n\r\n");
}
读取传感器NSHT30的温湿度
double ret_temperature;
double ret_humidity;
void Read_Nsht30(void)
{
uint8_t txBuffer[5] = {0x2C,0x06};
uint8_t rxBuffer[5] = {0};
I2C_Host.Write((uint16_t)0x44, txBuffer, 2);
while (I2C_Host.IsBusy())
{
}
__delay_ms(1);
I2C_Host.Read((uint16_t)(0x44), rxBuffer, 6);
while (I2C_Host.IsBusy())
{
}
// for(uint8_t i = 0; i < 6; i++){
// printf("%d,",rxBuffer[i]);
// }
// printf("\r\n");
uint16_t temperature = ((uint16_t)rxBuffer[0]<<8) | rxBuffer[1];
uint16_t humidity = ((uint16_t)rxBuffer[3]<<8) | rxBuffer[4];
// printf("T = %u, H = %u\r\n", temperature, humidity);
ret_temperature = -45.0 + 175.0 * (double)temperature / 65535.0;
ret_humidity = 100.0 * (double)humidity / 65535.0;
// printf("T = %lf, H = %lf\r\n\r\n", ret_temperature, ret_humidity);
}
启停蜂鸣器和加热电阻
while(1)
{
if(IO_RA2_GetValue() == 0){
__delay_ms(50);
if(IO_RA2_GetValue() == 0){
if(Beep_state == 0){
Beep_state = 1;
TMR2_Start();
IO_RC0_SetHigh();
}
else if(Beep_state == 1){
Beep_state = 0;
TMR2_Stop();
IO_RC0_SetLow();
}
__delay_ms(50);
}
}
}
模块功能展示
除了核心板还有其他测试的硬件:
- 搭配带屏12指神探的传感器扩展板
- USB转串口模块
- GPIO按键模块
下图为硬件连接的示意图:
设计心得
这次是第一次参加硬禾的限时挑战赛比较新奇,并且尝试了eZ-PLM工程管理的一个工具,一开始以为时间就只有1天,所以在构思后在绘制原理图和PCB采用比较简单的方式,体验了一下想要尝试的新芯片,将IO全部预留出来了,作为一个主控核心为后续的验证其他模块做准备。因此在写软件的时候发现了一些问题,如果作为学习使用的板子,在使用的时候就比较麻烦了,需要在连接一个串口和下载器了,但是作为项目使用的核心板的话那基本没啥问题。